Raphael JS - Start/Continue animating during mouseover. Pause animation on mouseout - javascript

Using Raphael JS, is there a way to make a circle move to the right (or any direction) during mouseover, and then pause/stop the movement when the cursor is no longer on the circle.
I've tried few different methods, but they have bugs. One of the main issues is: if the mouse cursor doesn't move after entering the circle, "mouseout" will not be triggered once the circle moves to a location where the mouse cursor is no longer over top of the circle.
You'll see what I mean in these different attempts:
1) Animate with pause / resume:
jsfiddle.net/fKKNt/
But the animation is jerky and unreliable. If you hover over the object, as the object moves outside of where the mouse cursor is, it doesn't trigger the "mouseout" listener.
2) Repositioning with mouseover & .attr("cx"):
jsfiddle.net/c4BFt/
But we want the animation to continue while the cursor is in the circle too.
3) Using setInterval (as suggested in:
An "if mouseover" or a "do while mouseover" in JavaScript/jQuery):
jsfiddle.net/9bBcm/
But "mouseout" is not called as the circle moves outside of where the cursor lies. I.e. the circle move to a location where "mouseout" should be called, but it is not called.
The same thing happens with "hover":
jsfiddle.net/STruB/

I'm sure there's a much more elegant way to do this, but off the top of my head, you could try something like this: http://jsfiddle.net/D6Ps4/2/
In case that disappears for some reason, I've included the code below. The solution simply initiates the animation, then checks to see if the mouse cursor (note the e.offsetX/e.offsetY) is within the bounding box of your Raphael Object (Element.getBBox()) at some set interval. If it is, do nothing and use setTimeout to check again in some time, if it's not, pause the animation.
var paper = Raphael("holder");
var animObject = Raphael.animation({cx: 400}, 5000);
circle = paper.circle(90, 90, 45).attr({fill: "#0E4"});
var timer;
circle.mouseover(function(e) {
var anim = function(shouldAnim) {
if (shouldAnim) {
circle.animate(animObject);
}
if (!mouseInsideCircle(e, circle)) {
circle.pause();
return;
} else {
timer = setTimeout(function() {anim(false)}, 20);
}
}
anim(true);
});
circle.mouseout(function() {
this.pause();
clearTimeout(timer);
});
var mouseInsideCircle = function(e, c) {
var bb = c.getBBox();
if (e.offsetX > bb.x && e.offsetY > bb.y) {
return true;
}
return false;
}
I'm sure the solution is flawed (it's checking the boundBox, not the circle itself; it also assumes the circle is moving right) and perhaps not ideal, but it seems to work reasonably smoothly and hopefully gets you on the right path.

Related

Dragging element horizontally by 100px increments

I'm having a hard time figuring this out.
I have a bunch of divs (with some content inside them), and I want to be able to drag and drop them, horizontally. However, I want to move them by 100px increments (the left position needs to be 0, 100, 200 etc). Imagine having a table in the background with 100px wide cells and you can only move the element to another cell. Except there's no table.
jQuery is out of the question I think (I'm using Vue).
I won't write your code for you, but I'll help you figure it out by telling you where to start.
First, listen to the mousedown and mouseup events on the element:
<div v-on="{ mousedown, mouseup }">Some content</div>
Next register a mousemove listener on mousedown, and deregister it on mouseup:
methods: {
mousemove(e) {
const moved = e.offsetX - this.startX;
// The mouse has moved "moved" pixels.
// Now calculate whatever you want
},
mousedown(e) {
this.startX = e.offsetX;
e.currentTarget.addEventListener(this.mousemove);
},
mouseup(e) {
e.currentTarget.removeEventListener(this.mousemove);
},
}

Mouse:over event fires twice in Fabric.js

I'm trying to write a simple card game on canvas + Fabricjs. Need to make bigger player's card when mouse is on it and return to normal size if mouse is out.
Here is my js code:
canvas.on('mouse:over', function(e) {
if(e.target.mytype && e.target.mytype=='card'){
e.target.animate({
top:352,
left: e.target.left - (max_width - min_width)/2,
scaleX:0.4,
scaleY:0.4
} , {
duration: 500,
onChange: function(value) {
canvas.renderAll.bind(canvas);
},
easing: fabric.util.ease.easeInOut
});
canvas.renderAll();
}
});
canvas.on('mouse:out', function(e) {
if(e.target.mytype && e.target.mytype=='card'){
e.target.animate({
top:385,
left: e.target.left + (max_width - min_width)/2,
scaleX:0.3,
scaleY:0.3
} , {
duration: 500,
onChange: function(value) {
canvas.renderAll.bind(canvas);
},
easing: fabric.util.ease.easeInOut
});
canvas.renderAll();
}
});
if player hover the card for 200ms (animation duration is 500ms) and move out, the animation freeze and the card remains at a new position. Hovering again wil start animation from this new position.
Here is a fiddle
Just try to move mouse in/out on the object and you'll see the bug. Please, help me to fix this.
The problem is the way that your code has relative positioning changes within the animation action - in the jsFiddle, the "top" value, in your code above, in the "left" value.
What each of these do is move the element +X or -X from the position the event starts at. If the animation finishes before the mouseout event is fired, it's fine, because the amount it moves back is equal (but opposite) to the amount it moved in the mouseover event.
However, if the animation for mouseout starts Before the first is finished, it takes it's current position, not the position it will be at when it finishes the animation. This leads to a situation where the element drifts away from its original position. This is certainly what the issue is in the JSFiddle, I understand from your comments that this is the same issue in your own code.
How to resolve it? As long as you are using a fixed relative position value in the mouseout event you probably can't. You could try logging the initial positioning (i.e., the value of "left/top" you started at) in the mouseover function and returning specifically to that point in the mouseout event.

Improving JavaScript Dragging Effect

I have managed to implement a simple JavaScript dragging effect to create "panel windows" inside a webpage, in which I can drag the object from any corner or position. I have only used the onmousedown (to indicate a possible dragging), onmousemove (to perform dragging) and onmouseup (to stop dragging) events.
It works well except when I drag the object too near an edge and move the mouse even at a normal speed. When I move the mouse towards an area outside the object, the dragging fails until I move again to the object's area, and I have to click the object to stop dragging it.
It's like the mouse is too fast, but obviously it doesn't happen in native application windows because they are better implemented.
What checks could I add to my current code so it doesn't fail as much? It currently seems to support a little bit of this problem by somehow handling the onmouseout event, but when it happens the window shakes to return the mouse pointer to the object area, and it doesn't look so good.
JavaScript Simple Dragging Effect
<!doctype html>
<html>
<head>
<title>JavaScript Simple Dragging Effect</title>
<style type="text/css">
.moveableRect {
position:absolute;
width:320px;
height:200px;
background-color:rgba(254, 220, 186, 0.8);
}
</style>
</head>
<body bgcolor="#abcdef">
<span id="span0000" class="moveableRect"><b>Drag Me<hr /></b></span>
<textarea id="txt" cols="40" rows="12"></textarea>
<script>
//Custom variable to indicate that we
//must drag the SPAN:
///
document.getElementById("span0000").myDragFlag = false;
//When we click and hold the mouse down,
//we must activate the dragging process:
///
document.getElementById("span0000").onmousedown = function(e)
{
if(e.button==0)
{
//This is the part of the trick that allows us
//to drag the object from any starting position
//that we click on it:
///
this.startX=(e.pageX-this.offsetLeft);
this.startY=(e.pageY-this.offsetTop);
//This flag indicates that we must drag
//when moving the mouse while the button is pressed:
///
this.myDragFlag=true;
}
};
//When we move the mouse, we must follow
//the mouse cursor around:
///
document.getElementById("span0000").onmousemove = function(e)
{
var bcr=this.getBoundingClientRect();
if(this.myDragFlag)
{
//When we start dragging (moving the mouse
//while the mouse button is pressed)
//we will perform the effect of dragging from any
//initial position in the rectangle:
///
this.style.left = (e.pageX-this.startX)+"px";
this.style.top = (e.pageY-this.startY)+"px";
document.getElementById("txt").value=
"getBoundingClientRect.left="+bcr.left+"\n"+
"getBoundingClientRect.top="+bcr.top+"\n"+
"getBoundingClientRect.width="+bcr.width+"\n"+
"getBoundingClientRect.height="+bcr.height+"\n"+
"getBoundingClientRect.bottom="+bcr.bottom+"\n"+
"getBoundingClientRect.right="+bcr.right+"\n"+
"e.pageX="+e.pageX+"\n"+
"e.pageY="+e.pageY+"\n"+
"this.offsetLeft="+this.offsetLeft+"\n"+
"this.offsetTop="+this.offsetTop+"\n"+
"relatX="+(e.pageX-this.offsetLeft)+"\n"+
"relatY="+(e.pageY-this.offsetTop);
}
};
//When we release the mouse button,
//we must finish the dragging process:
///
document.getElementById("span0000").onmouseup = function(e)
{
if(e.button==0)
this.myDragFlag=false;
};
document.getElementById("span0000").onmouseout = function(e)
{
if(this.myDragFlag==true)
{
//In this code, we basically check that
//when the mouse slips out from the object
//area while we are still dragging, we will
//force moving the object back under the mouse
//pointer. Here we will check from a logical
//edge of 48 pixels, and we will move the object
//back to 90% within those pixels, vertically and/or
//horizontally. It makes look the object shaky
//but at least it is minimally functional:
///
var minEdge=48;
var edgeCorrect=0.90;
var minEdgeCorrect=(minEdge*edgeCorrect)|0;
var bcr=this.getBoundingClientRect();
var bcrw=bcr.width;
var bcrh=bcr.height;
if(this.startX<minEdge)
{
this.style.left = (e.pageX-minEdgeCorrect)+"px";
}
else if(this.startX>bcrw-minEdge)
{
this.style.left = (e.pageX-this.startX+minEdgeCorrect)+"px";
}
if(this.startY<minEdge)
{
this.style.top = (e.pageY-minEdgeCorrect)+"px";
}
else if(this.startY>bcrh-minEdge)
{
this.style.top = (e.pageY-this.startY+minEdgeCorrect)+"px";
}
}
};
</script>
</body>
</html>
Your event listener for onmousemove is placed on the draggable div. So if the mouse goes off of it, the event stops firing. And it goes off while dragging because it fires mousemove events at intervals instead of every pixel.
To fix it, put the mousemove listener on the container.
document.body.onmousemove
And you will probably need to change how you get the coordinates.
There are other ways too I imagine. But that's the easiest.
Edit
Started to doubt myself when I tried to fiddle this but eventually got it working:
http://jsfiddle.net/tg33u8mv/3/
document.body.onmousemove = function (e) {
if (myDragFlag) {
var draggable = document.getElementById("span0000");
var bcr = draggable.getBoundingClientRect();
draggable.style.left = (e.pageX - draggable.startX) + "px";
draggable.style.top = (e.pageY - draggable.startY) + "px";
}
};
And I changed myDragFlag to a scoped variable instead of this.myDragFlag.
Bonus
In this version I add a class when it is dragging. http://jsfiddle.net/tg33u8mv/4/
The CSS for this class currently disables highlighting, significantly improving the look. You could also make it change color or add a shadow for a nice effect.

Error when mousemove arrow on image in javascript?

I am a demo here
function mouseTagObject() {
var x = document.getElementsByTagName("img")[0];
x.addEventListener('mouseover', function(){document.getElementById('arrow').style.display = 'none';}, false);
x.addEventListener('mouseout', function(){document.getElementById('arrow').style.display = 'block';}, false);
}
function mousemoveDiv(e) {
document.getElementById('arrow').style.top = e.pageY - 10 + "px";
document.getElementById('arrow').style.left = e.pageX - 15 + "px";
mouseTagObject();
}
document.addEventListener('mousemove', mousemoveDiv, false);
And html
When I mousemove on image tag (slowly), <div> tag not hide, how to fix it ?
I'll admit, it gave me the slip for a minute. But the slow vs. fast thing is what did it. Imagine, why would it not work when moving slowly? Well, look at your mouse. If you move very quickly, the button gets out from under the mouse. But if you move slowly, the button stays under the mouse the whole time, meaning that it never actually enters the image. Get rid of that button or put it next to the cursor, not under it.
Also, why are you adding the event listeners from within the mousemove handler? You're going to re-add those listeners every time the mouse moves.

Drag mouse events jittery - onmousemove

Working on a scrub bar for a chromeless player through youtube. I have the functionality working pretty much as Id like BUT when I click to drag the blue "seeker" button and drag it, it jumps back to its original position until I release the mouse click. Once I release, it starts the video at the appropriate position and draws the progress bar at the appropriate position too. code is here: http://jsfiddle.net/VysBU/1/
I also logged the position of the mouse and width of the progress bar (which is the part that jumps around) and the width values move consistently upward or downward on drag which doesn't make sense because visually, it jumps back and forth. Odd.
Any help is appreciated...if you need me to clarify something, let me know.
NOTE: just remembered...it tends to jump on vertical mouse movements only. ie, if i move the mouse horizontally without changing its vertical position at all, it 'animates' fine. if the vertical position does move, the 'animations' are erratic.
Check this out http://jsfiddle.net/sz4FF/
you need to stop the interval of
setInterval(animateProgress, 100);
when you begin seeking, and continue it when the seeking stops. The reason why it makes that jump is simply because animateProgress is called and sets the width of the playedBar and seeker.
I hastily added it to a global function (window.TEST_INTERVAL) just to check if it would work, and it does.
(how to initialize and clear the interval)
clearInterval(TEST_INTERVAL);
TEST_INTERVAL = setInterval(animateProgress, 100);
within seeking
function seeking(e){
clearInterval(TEST_INTERVAL);
within doneSeeking
function doneSeeking(e){
TEST_INTERVAL = setInterval(animateBuffer, 250);
UPDATE: IE8 and below problem
mousePos = e==undefined ? event.clientX : e.pageX;
//get the position of the mouse
//mousePos = e.pageX;
the event returned by onmousemove is "undefined" in ie7 and 8, that way we are checking for window.event.clientX, which shows the mouse position relative to the window. It seems to work fine but I believe that in a normal environment some minor tweaks might be needed

Categories