What causes the glitch in getBoundingClientRect()? - javascript

I am struggling with a glitch caused by getBoundingClientRect() method. (see fiddle below). The goal is to make a bar following a cursor inside a container. The aforementioned method does not return valid results. I am a beginner in terms of JS - there is most probably an obvious reason behind this. I just can't find the anwswer.
https://jsfiddle.net/aveoL210/3/
var div_moving = document.getElementById('div_moving');
var parent_div = 'parent_div';
var movingDiv = {
getCoords: function(e) {
var rect = e.target.getBoundingClientRect();
var y_pos = rect.top;
console.log(y_pos);
var y = e.pageY;
y = y - y_pos;
return (y);
}
};
document.getElementById(parent_div).addEventListener('mousemove', function(e){
result = movingDiv.getCoords(e);
div_moving.style.top = result +'px';
div_moving.style.display = "initial";
});
document.getElementById(parent_div).addEventListener('mouseleave', function(){
div_moving.style.display = "none";
});

The target when moving the mouse down will alternate between the moving and parent div
while adding
if(e.target.id !== 'parent_div') return;
guard to the getCoords function fixes it a little, it's still glitchy
The better solution is to get the rect of the parent_div, regardless of the e.target
So .. try this
var div_moving = document.getElementById('div_moving');
var parent_div = document.getElementById('parent_div')
var movingDiv = {
getCoords: function(e) {
var rect = parent_div.getBoundingClientRect();
var y_pos = rect.top;
var y = e.pageY;
y = y - y_pos;
return (y);
}
};
parent_div.addEventListener('mousemove', function(e) {
result = movingDiv.getCoords(e);
div_moving.style.top = result + 'px';
div_moving.style.display = "initial";
});
parent_div.addEventListener('mouseleave', function() {
div_moving.style.display = "none";
});
#parent_div {
position: relative;
width: 50%;
height: 300px;
margin: 1em auto;
border: 1px solid #333;
background: #f0f0f0;
}
#div_moving {
display: none;
position: absolute;
width: 100%;
height: 5px;
margin: 0;
background: blue;
}
<div id="parent_div">
<div id="div_moving"></div>
</div>

In getCoords you want to get parent_div all the time, so you have to use e.currentTarget instead of e.target otherwise, when div_moving is under the cursor e.target return div_moving and all calculation goes wrong, it will move to top and when you move your mouse again all wrong things repeat.
var div_moving = document.getElementById('div_moving');
var parent_div = 'parent_div';
var movingDiv = {
getCoords: function(e) {
console.log(e.target.id)
var rect = e.currentTarget.getBoundingClientRect();
var y_pos = rect.top;
console.log(y_pos);
var y = e.pageY;
y = y - y_pos;
return (y);
}
};
document.getElementById(parent_div).addEventListener('mousemove', function(e){
result = movingDiv.getCoords(e);
div_moving.style.top = result +'px';
div_moving.style.display = "initial";
});
document.getElementById(parent_div).addEventListener('mouseleave', function(){
div_moving.style.display = "none";
});

The, what you called "glitch", is actually what is expected to happen. Let me tell you why:
The line e.target.getBoundingClientRect() is the source of the issue, precisely e.target.
The, so called glitch, happens when the mouse is on top of the blue line element and at this point e.target no longer points to your #parent_div and instead e.target is #div_moving.
A quick fix would be by caching #parent_div in a variable so can use it later on, and on the getCoords() method we simply replace e.target by that new variable.
Here's a live demo:
var parent_div = document.getElementById('parent_div');
var div_moving = document.getElementById('div_moving');
var movingDiv = {
getCoords: function(e) {
var rect = parent_div.getBoundingClientRect();
var y_pos = rect.top;
//console.log(y_pos);
var y = e.pageY;
y = y - y_pos;
return (y);
}
};
parent_div.addEventListener('mousemove', function(e) {
result = movingDiv.getCoords(e);
div_moving.style.top = result + 'px';
div_moving.style.display = "initial";
});
parent_div.addEventListener('mouseleave', function() {
div_moving.style.display = "none";
});
#parent_div {
position: relative;
width: 50%;
height: 300px;
margin: 1em auto;
border: 1px solid #333;
background: #f0f0f0;
}
#div_moving {
display: none;
position: absolute;
width: 100%;
height: 5px;
margin: 0;
background: blue;
}
<div id="parent_div">
<div id="div_moving"></div>
</div>
DISCLAIMAIR: The above demo did not change a lot from the OP's original code which, by the way, can be hugely improved which is out of the scope of my answer.

Related

Mouse dragging an element

I've been waiting to ask this question for a long time, but couldn't, because I knew I would get bad reputation. This post was very hard to post, but I REALLY need this code...
Let's say there was a draggable element with the ID of "dragme"... You have to drag and drop the element to a specific spot. I was wondering if there is a code that does this task automatically for me when I execute a function. Lets name that function "dropElement". I am trying to drag "dragme" to my mouse position with a "dragElement" function with jquery or js.
This is what I tried:
(function() {
'use strict';
var mouseX = 0;
var mouseY = 0;
var timer = 0;
//tracks mouse position
document.body.addEventListener("mousemove", function(e) {mouseX = e.clientX; mouseY = e.clientY;});
function dropElement() {
$("#dragme").trigger($.Event("mousedown", {button: 0}));
$("body").trigger($.Event("mouseup", {button: 0, clientX: mouseX, clientY: mouseY}));
timer = setTimeout(drop, 100);
}
dropElement() //executes function and drops "dragme" to mouse position
I found the code in the question a bit complex to follow, especially with a timing function.
Instead I've gone back to basics (and vanilla JS) to think about the sequence of events. The user moves the mouse, we aren't interested unless they have put the mousedown within the element we want to drag. So this snippet sets a variable isDown which is set to true when the user puts the mouse down on the element.
Then it looks for a mousemove event on the whole window and if isDown is set it moves the element.
We also look for the mouseup event on the window and unset isDown.
The reason for looking for some events on the actual element and some on the window is because things are moving - the mouse may get out of the window before it is released for example.
let isDown = false;
const dragMe = document.querySelector('.dragme');
dragMe.addEventListener('mousedown', function() {
isDown = true;
});
window.addEventListener('mouseup', function() {
isDown = false;
});
window.addEventListener('mousemove', function() {
if (isDown) {
dragMe.style.top = event.clientY + 'px';
dragMe.style.left = event.clientX + 'px';
}
});
.dragme {
width: 5em;
height: 5em;
background-color: cyan;
position: relative;
top: 0;
left: 0;
}
<div class="dragme">Drag me</div>
I hope this sample helps you
var drag = {
elem: null,
x: 0,
y: 0,
state: false
};
var delta = {
x: 0,
y: 0
};
function dropElement(e){
var cur_offset = $("#autoDrag").offset();
$("#autoDrag").animate({
left: (e.pageX),
top: (e.pageY )
});
}
$(document).mousedown(function(e) {
dropElement(e);
})
$("#dragMe").mousedown(function(e) {
drag.elem = dragMe;
drag.x = e.pageX;
drag.y = e.pageY;
drag.state = true;
})
$(document).mousemove(function(e) {
if ( drag.state) {
delta.x = e.pageX - drag.x;
delta.y = e.pageY - drag.y;
var cur_offset = $(drag.elem).offset();
$(drag.elem).offset({
left: (cur_offset.left + delta.x),
top: (cur_offset.top + delta.y)
});
drag.x = e.pageX;
drag.y = e.pageY;
}
})
$("#dragMe").mouseup(function() {
drag.state = false;
})
#dragMe {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
width: 80px;
height: 80px;
padding:10px;
background-color: #00a1ff;
color: white;
border-radius: 50px;
}
#autoDrag {
position: absolute;
right:0;
display: flex;
justify-content: center;
align-items: center;
text-align:center;
width: 80px;
height: 80px;
padding:10px;
background-color: #ff00ff;
color: white;
border-radius: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span id="dragMe">DragMe!</span>
<span id="autoDrag">Click somewhere I will be there!</span>

Style drag ghost element

I am facing an issue, where I am dragging a div.
While the ghost element looks good on MacOs(yes both on Chrome and FireFox),it appears to be too transparent,in Windows (yes both on Chrome and FireFox.
I tried multiple approaches but nothing seems to work.
So is it possible to style the ghost element?
Also, I tried to make an image of that element on the go, and use it as ghost dragging image, but the transparency issue still remains.
You can do this by implementing dragging of the element yourself in JavaScript. That way, you can apply a CSS class to the element while it is being dragged, which styles it in any way you wish.
const d = 'dragging';
const container = document.getElementById('container');
const el = document.getElementById('drag');
var cOffX = 0;
var cOffY = 0;
el.addEventListener('mousedown', dragStart);
function dragStart(e) {
e = e || window.event;
e.preventDefault();
cOffX = e.clientX - el.offsetLeft;
cOffY = e.clientY - el.offsetTop;
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
el.classList.add(d);
container.style.cursor = 'move';
};
function dragMove(e) {
e = e || window.event;
e.preventDefault();
el.style.top = (e.clientY - cOffY).toString() + 'px';
el.style.left = (e.clientX - cOffX).toString() + 'px';
};
function dragEnd(e) {
e = e || window.event;
e.preventDefault();
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
el.classList.remove(d);
container.style.cursor = null;
};
#container {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
#drag {
position: absolute;
height: 100px;
width: 100px;
background-color: lime;
border-radius: 0;
transition: background-color 0.25s, border-radius 0.25s;
cursor: move;
}
#drag.dragging {
background-color: navy;
border-radius: 50%;
}
<div id="container">
<div id="drag"></div>
</div>
As others have said, the ghosting is browser-based and can't be changed, so it seems you need your own solution if you want to get around that.

How to make mouseup fire anyehere?

I have a drag and drop script that is relatively functional. However, I want to be able to trigger mouseup anywhere on the screen. Is there a way to trigger mouseup outside of the window, or outside of the current element? I know this is possible and I've seen other questions like this. I wanted to find a way in vanilla Javascript to detect mouseups like this.
document.onmousemove = mouseCoords;
var x = 0;
var y = 0;
var cl1= false;
var divid;
var offs1;
var offs2;
var topPos;
var leftPos;
function mouseCoords(e) {
x = e.x
y = e.y
if(cl1 === true){
document.getElementById(divid).style.top = topPos + (y-offs1) + 'px';
document.getElementById(divid).style.left = leftPos + (x-offs2) + 'px';
}
}
var drag = function(i, cas) {
divid= i
switch(cas){
case 1:
var rect = document.getElementById(divid).getBoundingClientRect();
leftPos = rect.left;
topPos = rect.top;
offs1 = y;
offs2 = x;
cl1= true;
break;
case 0:
offs1 = 0;
offs2 = 0;
cl1= false;
break;
}
}
#block{
width: 100px; z-index: 20; height: 50px; background-color: blue; position: fixed; user-select: none; -webkit-user-select: none;
}
.drag{
width: 200px; height: 100px; background-color: red; position: fixed;
}
<div id="block">mouseup doesn't trigger over me!</div>
<div id="1" class="drag" onmousedown="drag(1, 1)" onmouseup="drag(1, 0)"></div>
Use
document.addEventListener("mouseup", drag(null, 0));
for mouseup,
and this code for mousedown.
document.addEventListener("mousedown", drag(null, 1));
Basically, document.addEventListener works for the whole window. "mouseup" tells the script that the event is a mouseup, and the final bit is the function to be executed (drag(1, 0))

zooming a selected part of image with cursor event

I have this jsfiddle : http://jsfiddle.net/seekpunk/JDU9f/1/
what I want is when the user do a selection the selected part is zoomed in .Is there anyway to achieve this ?
this is the code so far :
var canvas = document.getElementById("MyCanvas");
var ctx = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height,
x1,
y1,
isDown = false;
var img = new Image();
img.src = "http://www.stockfreeimages.com/static/homepage/female-chaffinch-free-stock-photo-106202.jpg";
canvas.onmousedown = function (e) {
var rect = canvas.getBoundingClientRect();
x1 = e.clientX - rect.left;
y1 = e.clientY - rect.top;
isDown = true;
}
canvas.onmouseup = function () {
isDown = false;
}
canvas.onmousemove = function (e) {
if (!isDown) return;
var rect = canvas.getBoundingClientRect(),
x2 = e.clientX - rect.left,
y2 = e.clientY - rect.top;
ctx.clearRect(0, 0, w, h);
ctx.drawImage(img, 0, 0, w, h);
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
}
img.onload = function () {
ctx.drawImage(img, 0, 0, w, h);
};
I can make the selection but how can i zoom the selected part only ? like this example : http://canvasjs.com/docs/charts/basics-of-creating-html5-chart/zooming-panning/
For those looking for zooming functionality without the use of a canvas, here's a pretty foolproof solution using a simple <img />:
$(window).on("load",function() {
//VARS===================================================
var zoom = {
zoomboxLeft:null, zoomboxTop:null, //zoombox
cursorStartX:null, cursorStartY:null, //cursor
imgStartLeft:null, imgStartTop:null, //image
minDragLeft:null,maxDragLeft:null, minDragTop:null,maxDragTop:null
};
//KEY-HANDLERS===========================================
$(document).keydown(function(e) {
if (e.which==32) {e.preventDefault(); if (!$(".zoombox img").hasClass("moving")) {$(".zoombox img").addClass("drag");}} //SPACE
});
$(document).keyup(function(e) {
if (e.which==32) {if (!$(".zoombox img").hasClass("moving")) {$(".zoombox img").removeClass("drag");}} //SPACE
});
//RESET IMAGE SIZE=======================================
$(".reset").on("click",function() {
var zoombox = "#"+$(this).parent().attr("id")+" .zoombox";
$(zoombox+" img").css({"left":0, "top":0, "width":$(zoombox).width(), "height":$(zoombox).height()});
}).click();
//ZOOM&DRAG-EVENTS=======================================
//MOUSEDOWN----------------------------------------------
$(".zoombox img").mousedown(function(e) {
e.preventDefault();
$(".zoombox img").addClass("moving");
var selector = $(this).next();
var zoombox = $(this).parent();
$(zoombox).addClass("active");
//store zoombox left&top
zoom.zoomboxLeft = $(zoombox).offset().left + parseInt($(zoombox).css("border-left-width").replace(/\D+/,""));
zoom.zoomboxTop = $(zoombox).offset().top + parseInt($(zoombox).css("border-top-width").replace(/\D+/,""));
//store starting positions of cursor (relative to zoombox)
zoom.cursorStartX = e.pageX - zoom.zoomboxLeft;
zoom.cursorStartY = e.pageY - zoom.zoomboxTop;
if ($(".zoombox img").hasClass("drag")) {
//store starting positions of image (relative to zoombox)
zoom.imgStartLeft = $(this).position().left;
zoom.imgStartTop = $(this).position().top;
//set drag boundaries (relative to zoombox)
zoom.minDragLeft = $(zoombox).width() - $(this).width();
zoom.maxDragLeft = 0;
zoom.minDragTop = $(zoombox).height() - $(this).height();
zoom.maxDragTop = 0;
} else {
//set drag boundaries (relative to zoombox)
zoom.minDragLeft = 0;
zoom.maxDragLeft = $(zoombox).width();
zoom.minDragTop = 0;
zoom.maxDragTop = $(zoombox).height();
//activate zoom-selector
$(selector).css({"display":"block", "width":0, "height":0, "left":zoom.cursorStartX, "top":zoom.cursorStartY});
}
});
//MOUSEMOVE----------------------------------------------
$(document).mousemove(function(e) {
if ($(".zoombox img").hasClass("moving")) {
if ($(".zoombox img").hasClass("drag")) {
var img = $(".zoombox.active img")[0];
//update image position (relative to zoombox)
$(img).css({
"left": zoom.imgStartLeft + (e.pageX-zoom.zoomboxLeft)-zoom.cursorStartX,
"top": zoom.imgStartTop + (e.pageY-zoom.zoomboxTop)-zoom.cursorStartY
});
//prevent dragging in prohibited areas (relative to zoombox)
if ($(img).position().left <= zoom.minDragLeft) {$(img).css("left",zoom.minDragLeft);} else
if ($(img).position().left >= zoom.maxDragLeft) {$(img).css("left",zoom.maxDragLeft);}
if ($(img).position().top <= zoom.minDragTop) {$(img).css("top",zoom.minDragTop);} else
if ($(img).position().top >= zoom.maxDragTop) {$(img).css("top",zoom.maxDragTop);}
} else {
//calculate selector width and height (relative to zoombox)
var width = (e.pageX-zoom.zoomboxLeft)-zoom.cursorStartX;
var height = (e.pageY-zoom.zoomboxTop)-zoom.cursorStartY;
//prevent dragging in prohibited areas (relative to zoombox)
if (e.pageX-zoom.zoomboxLeft <= zoom.minDragLeft) {width = zoom.minDragLeft - zoom.cursorStartX;} else
if (e.pageX-zoom.zoomboxLeft >= zoom.maxDragLeft) {width = zoom.maxDragLeft - zoom.cursorStartX;}
if (e.pageY-zoom.zoomboxTop <= zoom.minDragTop) {height = zoom.minDragTop - zoom.cursorStartY;} else
if (e.pageY-zoom.zoomboxTop >= zoom.maxDragTop) {height = zoom.maxDragTop - zoom.cursorStartY;}
//update zoom-selector
var selector = $(".zoombox.active .selector")[0];
$(selector).css({"width":Math.abs(width), "height":Math.abs(height)});
if (width<0) {$(selector).css("left",zoom.cursorStartX-Math.abs(width));}
if (height<0) {$(selector).css("top",zoom.cursorStartY-Math.abs(height));}
}
}
});
//MOUSEUP------------------------------------------------
$(document).mouseup(function() {
if ($(".zoombox img").hasClass("moving")) {
if (!$(".zoombox img").hasClass("drag")) {
var img = $(".zoombox.active img")[0];
var selector = $(".zoombox.active .selector")[0];
if ($(selector).width()>0 && $(selector).height()>0) {
//resize zoom-selector and image
var magnification = ($(selector).width()<$(selector).height() ? $(selector).parent().width()/$(selector).width() : $(selector).parent().height()/$(selector).height()); //go for the highest magnification
var hFactor = $(img).width() / ($(selector).position().left-$(img).position().left);
var vFactor = $(img).height() / ($(selector).position().top-$(img).position().top);
$(selector).css({"width":$(selector).width()*magnification, "height":$(selector).height()*magnification});
$(img).css({"width":$(img).width()*magnification, "height":$(img).height()*magnification});
//correct for misalignment during magnification, caused by size-factor
$(img).css({
"left": $(selector).position().left - ($(img).width()/hFactor),
"top": $(selector).position().top - ($(img).height()/vFactor)
});
//reposition zoom-selector and image (relative to zoombox)
var selectorLeft = ($(selector).parent().width()/2) - ($(selector).width()/2);
var selectorTop = ($(selector).parent().height()/2) - ($(selector).height()/2);
var selectorDeltaLeft = selectorLeft - $(selector).position().left;
var selectorDeltaTop = selectorTop - $(selector).position().top;
$(selector).css({"left":selectorLeft, "top":selectorTop});
$(img).css({"left":"+="+selectorDeltaLeft, "top":"+="+selectorDeltaTop});
}
//deactivate zoom-selector
$(selector).css({"display":"none", "width":0, "height":0, "left":0, "top":0});
} else {$(".zoombox img").removeClass("drag");}
$(".zoombox img").removeClass("moving");
$(".zoombox.active").removeClass("active");
}
});
});
/*CONTAINER-------------------*/
#container {
width: 234px;
height: 199px;
margin-left: auto;
margin-right: auto;
}
/*ZOOMBOX=====================*/
/*IMAGE-----------------------*/
.zoombox {
position:relative;
width: 100%;
height: 100%;
border: 2px solid #AAAAAA;
background-color: #666666;
overflow: hidden;
}
.zoombox img {position:relative;}
.zoombox img.drag {cursor:move;}
.zoombox .selector {
display: none;
position: absolute;
border: 1px solid #999999;
background-color: rgba(255,255,255, 0.3);
}
/*CONTROL---------------------*/
.reset {float:left;}
.info {float:right;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<!--ZOOMBOX-->
<div class="zoombox">
<img src="http://www.w3schools.com/colors/img_colormap.gif" />
<div class="selector"></div>
</div>
<input type="button" class="reset" value="Reset" /><span class="info">Press SPACE to drag</span>
</div>
jsfiddle: http://jsfiddle.net/2nt8k8e6/
The container element can be anything, just place the .zoombox on your webpage and it should work.
You should even be able to put multiple .zoomboxes next to each other in the same container-element. I haven't tested it though.
It's not too complicated, there's not a whole lot of comments, but the variable names are pretty self-explanatory and make the code easy enough to read, and all the jquery-functions can be looked up.
The code is essentially divided into three handlers: .mousedown(), .mousemove(), .mouseup().
- In mousedown some values are stored into variables (like the start-coordinates for the selector), which are used in mousemove and mouseup as a reference.
If you want to know exactly what's happening, just have a look at the code, as I said it's not too difficult.

Moving a image in jQuery (image crop)

My code is working fine, but Im getting a few problems. The first problem regards to the browsers ability to drag images around, when it happens a "stop signal" appears, and it breaks the code. Sometimes the signal appears (firefox), sometimes not. I dont know why. The second problem regards to the text outside the div. When the user drag the image, the text outside gets selected. Who can I solve it?
If you run the code, you need a 140px width image.
The code:
<div style="position: relative; height: 100px; width: 100px; overflow: hidden;" class="img-profile">
<div id="crop" style="position: absolute; width: 140px; left: -20px; height: 140px; top: 0px; background: url(/image/user/teste.jpg) no-repeat; cursor: move;"></div>
</div>
var x = 0;
var y = 0;
$(document).ready(function () {
$("#crop").mousedown(function () {
var crop = $(this);
$(document).mousemove(function (e) {
var x_movement = 0;
var y_movement = 0;
if (x == e.pageX || x == 0) {
x = e.pageX;
} else {
x_movement = e.pageX - x;
x = e.pageX;
}
if (y == e.pageY || y == 0) {
y = e.pageY;
} else {
y_movement = e.pageY - y;
y = e.pageY;
}
var left = parseFloat(crop.css("left")) + x_movement;
var min_left = 0;
var max_left = -40;
if (left >= min_left) left = min_left;
if (left <= max_left) left = max_left;
crop.css("left", left);
var top = parseFloat(crop.css("top")) + y_movement;
var min_top = 0;
var max_top = (parseFloat(crop.css("height")) - 100) * -1;
if (top >= min_top) top = min_top;
if (top <= max_top) top = max_top;
crop.css("top", top);
});
});
$(document).mouseup(function () {
x = 0;
y = 0;
$(document).unbind("mousemove");
});
});
Just noted that changing the crop div to a input solved both problems.
<input type="button" id="crop" style="position: absolute; width: 140px; left: -20px; height: 140px; top: 0px; background: url(/image/user/teste.jpg) no-repeat; cursor: move; border: 0px;" />

Categories

Resources