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.
Related
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);
},
}
I'm wondering how to do an effect like this:
http://www.gazprom.com/
(please note, the moving blue background, in top blue menu), and its relativity to cursor position)
Is there any script that follows cursor and changes background position style?
This effect can be achieved using small piece of JavaScript:
JavaScript
document.addEventListener('mousemove', function (event) {
if (window.event) { // IE fix
event = window.event;
}
// Grab the mouse's X-position.
var mousex = event.clientX;
var header = document.getElementById('header');
header.style.backgroundPosition = mousex/3 + 'px 0';
}, false);
Working Demo
How it works :
It binds a function on the mousemove event on document.
It grabs the current mouse position using event.clientX.
It changes the background-position of element #header with 1/3rd of the speed (mousex/3). Reference
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.
on the http://www.associationtsunami.org/ site if i make a mousedown on the document the cube rotates depending on the direction the user moves the mouse.
the code is:
key code ...
).bind('mousedown touchstart', function (evt) {
delete mouse.last;
if ($(evt.target).is('a, iframe')) {
return true;
}
evt.originalEvent.touches ? evt = evt.originalEvent.touches[0] : null;
mouse.start.x = evt.pageX;
mouse.start.y = evt.pageY;
$(document).bind('mousemove touchmove', function (event) {
dragging = 1;
// Only perform rotation if one touch or mouse (e.g. still scale with pinch and zoom)
if (!touch || !(event.originalEvent && event.originalEvent.touches.length > 1)) {
event.preventDefault();
// Get touch co-ords
event.originalEvent.touches ? event = event.originalEvent.touches[0] : null;
$('.viewport').trigger('move-viewport', { x: event.pageX, y: event.pageY });
}
});
$(document).bind('mouseup touchend', function () {
dragging = 0;
$(document).unbind('mousemove touchmove');
});
});
full code https://github.com/AssociationTsunami/tsunami/blob/gh-pages/js/cube.js#L72
i would like to disable this event if a user makes the mousedown on a scrollbar - for example on the 'ONSONPARLA' page there is a TAB with ACCORDIONS, if you open any of the accordion content you get a scrollbar on the edge or within the accordion and if you try to move the scrollbar, this also moves the cube.
what is the correct way to overwrite this in the cube.js so that the cube does not turn if the event is on a scrollbar element?
It can't be done in such manner.
But there is an alternative solution. Use some custom scroll bar plugin to replace classic scroll bar. You will be able to prevent events on him. I understand this is not an excellent solution, but according to you web page you like to take a chance. :)
Here you can find few good plugins.
Good plugin example is here.
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.