I have 2 canvas elements on top of each other and i want to move the canvas element on top on mouse drag but it produces weird results.
This is my code for the events (the variable cvs is the canvas element which is on top of other canvas element)
var drag = false;
cvs.addEventListener('mousedown', function(event) {
drag = true;
});
cvs.addEventListener('mouseup', function(event) {
drag = false;
});
cvs.addEventListener('mousemove', function(event) {
if (drag) {
const rect = cvs.getBoundingClientRect()
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
cvs.style.left = x + "px";
cvs.style.top = y + "px";
console.log(x, y);
}
});
When I drag the top canvas it starts to flicker back-and-forth between 2 positions
At a glance, it looks like you are using a relative value to set an absolute position.
So, first iteration, the left position updates to x, then the next iteration you subtract the last value of x from the mouse position. I think this is going to move it on and off screen.
say, clientX is at 100, and left is at 10.
T1 -> x = 100 - 10 = 90,
T2 -> x = 100 - 90 = 10.
Hence the "flickering"
What you want to do, is take the relative movement value of the mouse and move the element by the same amount.
So on mouse down, record the mouse initial position and element initial position.
Subtract the initial mouse position from the mouse position on each mouse move iteration, and assign the initial element position plus the relative change to the element.
var initialPosition = null
var initialMouseCoords = null
cvs.addEventListener('mousedown', function(event) {
initialPosition = cvs.getBoundingClientRect()
initialMouseCoords = {clientX: event.clientX, clientY: event.clientY}
});
cvs.addEventListener('mouseup', function(event) {
initialPosition = null
initialMouseCoords = null
});
cvs.addEventListener('mousemove', function(event) {
if (initialMouseCoords) {
const dx = event.clientX - initialMouseCoords.clientX;
const dy = event.clientY - initialMouseCoords.clientY;
cvs.style.left = initialPosition.left + dx;
cvs.style.top = initialPosition.top + dy;
console.log(dx, dy);
}
});
Bare in mind there are drag events depending on your use case, you might want to explore that as an alternative.
I have a <canvas> element that spans the width and height of my webpage, like a background. It has interactive elements that rely on mouse coordinates. When the canvas was in it's own block (i.e. nothing over it) the interactive elements worked fine. But now that it's got divs over it it's not picking up any of the mouse interactions.
Below is my javascript code for the mousemove stuff. Why would items on top affect it picking up mouse xy coordinates, and how do I fix it?
var mouse = {x:-100,y:-100};
var mouseOnScreen = false;
canvas.addEventListener('mousemove', MouseMove, false);
canvas.addEventListener('mouseout', MouseOut, false);
var MouseMove = function(e) {
if (e.layerX || e.layerX == 0) {
//Reset particle positions
mouseOnScreen = true;
mouse.x = e.layerX - canvas.offsetLeft;
mouse.y = e.layerY - canvas.offsetTop;
}
}
var MouseOut = function(e) {
mouseOnScreen = false;
mouse.x = -100;
mouse.y = -100;
}
var update = function(){
var i, dx, dy, sqrDist, scale;
//...... this chunk is the only part of the function that references the mouse
dx = parts[i].x - mouse.x;
dy = parts[i].y - mouse.y;
sqrDist = Math.sqrt(dx*dx + dy*dy);
if (sqrDist < 20){
parts[i].r = true;
}
.....
}
If you don't need the top divs to be mouse-aware then set their CSS pointer-events:none; and the mouse events will filter down to your canvas underneath. Questioner needs responsive buttons placed over the canvas.
If the top divs do need to respond to mouse events, you might have to listen for mouse events on the window and convert those to canvas coordinates that your app can respond to.
You can listen for mousemove events on the window and get all moves -- even when over button elements. Also listen for mouseout events on the window. Since the canvas spans the window, you know mouseout happens when mouseevent.clientX & mouseevent.clientY report the coordinates are outside the window
Solved the issue with this code I found from another SO answer that I modified a bit.
function simulate(e) {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("mousemove", true, true, window,
0, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);
canvas.dispatchEvent(evt);
console.log("Emulated.");
}
$("body > section, body > header").each(function(){
this.addEventListener("mousemove", simulate);
});
Also, as a side note. Changed original layerX and layerY in mouse code to offsetX and offsetY to work in firefox (in addition to all other browsers)
I have an array of canvas objects that draw correctly. i have three problems:
Offset. I have tested the code below in JS fiddle and it works, but when i export it my web page, the variables get skewed. The detection happens, but not in the right place. the page width is set in CSS, and the actual canvas area is centered using a margin:0 auto call, however it is smaller than the page width.
<canvas id="canvas" width="780" height="690" style="position:absolute;"></canvas>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var $results = $("#results");
// define the polygon items
var polyArray = new Array (6);
polyArray [0] =[{x:50,y:236}, {x:200,y:115}, {x:350,y:50}, {x:350,y:300}, {x:232,y:325}, {x:75,y:300}];
polyArray [1] =[{x:350,y:55}, {x:350,y:300}, {x:510,y:300}, {x:510,y:205}, {x:578,y:172}, {x:690,y:96}, {x:650,y:17}];
polyArray [2] =[{x:510,y:300}, {x:510,y:200}, {x:715,y:113}, {x:780,y:200}, {x:780,y:485}, {x:625,y:468}, {x:605,y:456}, {x:605,y:428}];
polyArray [3] =[{x:0,y:446}, {x:284,y:320}, {x:255,y:540}, {x:240,y:566}, {x:73,y:600}, {x:0,y:565}];
polyArray [4] =[{x:355,y:305}, {x:510,y:305}, {x:604,y:423}, {x:604,y:460}, {x:628,y:484}, {x:610,y:513}, {x:587,y:468}, {x:537,y:426}, {x:500,y:400}, {x:447,y:424}, {x:312,y:365}, {x:307,y:314 }];
polyArray [5] =[{x:350,y:425}, {x:415,y:421}, {x:455,y:434}, {x:495,y:411}, {x:550,y:444}, {x:618,y:590}, {x:570,y:616}, {x:359,y:597}, {x:333,y:522}];
// call the function to draw all the objects in the array
define(polyArray);
// call through the array to draw the objects
function define(polygon) {
ctx.beginPath();
for (var i = 0; i < polygon.length; i++) {
ctx.moveTo(polygon[i][0].x, polygon[i][0].y);
for (var j = 1; j < polygon[i].length; j++) {
ctx.lineTo(polygon[i][j].x, polygon[i][j].y);
}
ctx.fill();
}
ctx.closePath();
}
function hitTest(polygon) {
// redefine the polygon
define(polygon);
// ask isPointInPath to hit test the mouse position
// against the current path
return (ctx.isPointInPath(mouseX, mouseY));
}
function handleMouseMove(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// check if the mouse is inside the polygon
var isInside = hitTest(polyArray);
if (isInside) {
canvas.style.cursor = 'pointer';
$results.text("Mouse is inside the area);
} else {
canvas.style.cursor = 'default';
$results.text("Outside");
}
}
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
Detecting which object has been hovered over. What needs to happen is on hover of one the array shapes should effect some CSS/JS. How can i assign an ID variable and detect it?
when i bring responsive design into the equation i'm a bit stuck for how to incorporate this offset and the poly co-ords to scale appropriately.
Any point in the right direction would be greatly appreciated.
Question#1: Getting accurate mouse position after the canvas has moved
Whenever you move your canvas (fex: margin: 0 auto), you must recalculate your offsetX and offsetY values:
If you manually change the canvas element's CSS (fex: canvas.style.margin='50px' inside javascript), then you must also manually call reOffset().
// cache the canvas's offset positions since the
// offset positions are used often
var offsetX,offsetY;
// call this once at the beginning of your app
// and whenever you change the canvas's position on the page
// (eg call when you change margins, scroll, etc)
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
// have the browser auto-reset offsetX & offsetX when
// the viewport scrolls or resizes
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
Question#2 Detecting hovers & blurs over your polygons
Your hitTest function will test if the mouse is currently inside a specified polygon. So inside handleMousemove you could call hitText for each of the polygons inside your polyArray.
Keep a flag variable indicating the index# of the last polygon the mouse was inside (or -1 to indicate the mouse was outside all polygons. When your flag variable value changes, you know there has been either a hover-event or a blur-event. Compare the last and current flag variables to determine which polygon is now hovered or blurred.
Question#3 Incorporating a responsive design
Mouse coordinates reported by the browser into e.clientX and e.clientY are always in unscaled values relative to the browser viewport.
So if you:
Click the mouse and use e.clientX/e.clientY to determine the mouse is at [100,100],
Scale your canvas: context.scale(2,2),
And reclick without moving the mouse from its original [100,100] position,
Then:
Using e.clientX/e.clientY to detect the mouse coordinates will still report the position as [100,100] even if the canvas has been scaled and the mouse is at [200,200] relative to the scaled canvas.
The fix:
You must scale the browser's reported mouse position to match the scaling factor of the canvas:
// Determine how much you want to scale the canvas
var scaleFactor=2.00;
// scale the canvas
context.scale(scaleFactor,scaleFactor);
// also scale the mouse position reported by the browser
mouseX=parseInt(e.clientX-offsetX)*scaleFactor;
mouseY=parseInt(e.clientY-offsetY)*scaleFactor;
I want to create an element and then have that element be immediately bound to the cursor. I have tools to move the element, but I don't know how to bind them to the cursor without having to click the element. I thought about simulating the mousedown() event, but I don't know how to do it.
For context, my ultimate goal is to create a line with user defined endpoint. The user clicks a point and 2 small black circles are created. One as a reference point the the first click and the other to be attached to the cursor with a path connect the 2 points. Once the user clicks another point, both small black circles with disappear and only the line will remain.
Any ideas?
Thanks to #Joan Charmant for pointing me in the right direction. Here's my solution thus far. $('#paper') is my canvas and tempPoint is the circle I created to bind to cursor movement.
$("#paper").mousemove(function (event)
{
if(firstLinePointSelected && tempPoint!=null)
{
if (!event) var event = window.event;
var x=0, y=0;
if (event.pageX || event.pageY)
{
x = event.pageX;
y = event.pageY;
}
else if (event.clientX || event.clientY)
{
x = event.clientX + document.body.scrollLeft
+ document.documentElement.scrollLeft;
y = event.clientY + document.body.scrollTop
+ document.documentElement.scrollTop;
}
// subtract paper coords on page
tempPoint.attr("cx", x - $('#paper').offset().left);
tempPoint.attr("cy", y - $('#paper').offset().top);
}
});
I am trying to learn how to make a div in an HTML page draggable by pure JavaScript not by using external library so I tried some of mine techniques but I failed to make it a proper draggable object. I am sure I'm missing something important in my code so I want to know what is the basic idea behind draggable object. I was trying to achieve it by setting some startX and startY position and making the Div position absolute and setting the left and top of div by css as
p.style.left = (e.clientX-startX) + 'px';
p.style.top = (e.clientY-startY) + 'px';
// where p is the element i am trying to make draggable
You should not forget to save p's initial position and add it each time to make sure you're doing relative calculations. Currently, you assume p is always at position (0, 0) when starting dragging.
Secondly, cancelling the selectstart event makes for no ugly selection being created when dragging.
I updated your code a bit to this effect: http://jsfiddle.net/rLegF/1/.
var p = document.getElementById("p"),
startX, startY,
origX, origY,
down = false;
document.documentElement.onselectstart = function() {
return false; // prevent selections
};
p.onmousedown = function(e) {
startX = e.clientX;
startY = e.clientY;
origX = p.offsetLeft;
origY = p.offsetTop;
down = true;
};
document.documentElement.onmouseup = function() {
// releasing the mouse anywhere to stop dragging
down = false;
};
document.documentElement.onmousemove = function(e) {
// don't do anything if not dragging
if(!down) return;
p.style.left = (e.clientX - startX) + origX + 'px';
p.style.top = (e.clientY - startY) + origY + 'px';
};
Edit: You could also combine startX and origX since you're basically always doing - startX + origX: http://jsfiddle.net/rLegF/2/.
What you're then doing is calculating the mouse position with respect to the top left-hand corner of the element, and then set the position to the new mouse position minus that old mouse position. Perhaps it's a little more intuitive that way.
I cleaned up some more as well.