How to move the canvas like drag and drop? - javascript

I need to move the whole canvas by touch like the movement of drag and drop
the position of the canvas is supposed to be from
flattenCanvas.height = canvas.height = SCREEN_HEIGHT - menu.container.offsetHeight;
canvas.style.position = 'absolute';
canvas.style.top = menu.container.offsetHeight+'px';
canvas.style.left = -(menu.container.offsetHeight)/2+'px';
context = canvas.getContext("2d");
and the touches for drawing or scrolling is from this
else if(event.touches.length == 1) ////one finger scrolling
{
canvas.style.top = (((event.touches[0].pageY)))+'px';
canvas.style.left = (((event.touches[0].pageX)))+'px';
}
from the last code i can move the canvas by one touch but it is not really dragging.
how to fix the canvas screen movement?
this is a demo of the webapp demo

Based on these docs, the event you want to handle is touchmove.
The code in your question appears to live inside a touchstart handler; just execute the same code in a touchmove handler to continuously update during the 'drag'.
Update
What you are trying to do is offset a target element (in this case, a canvas element) by the difference between a touch event's "current" (touchmove) and "start" (touchstart) positions. To do so, you should capture the positions of both target and touch in a touchstart handler. Then, in your touchmove handler, take the difference in touch positions, and add them to the target's start position. This might look like:
// Get your draggable element from the DOM
var draggable = document.getElementById('draggable');
// Set initial position.
draggable.style.position = 'relative'; // 'absolute' also works.
draggable.style.top = '0';
draggable.style.left = '0';
var targetStartX, targetStartY, touchStartX, touchStartY;
// Capture original coordinates of target and touch
function dragStart(e) {
targetStartX = parseInt(e.target.style.left);
targetStartY = parseInt(e.target.style.top);
touchStartX = e.touches[0].pageX;
touchStartY = e.touches[0].pageY;
}
function dragMove(e) {
// Calculate touch offsets
var touchOffsetX = e.touches[0].pageX - touchStartX,
touchOffsetY = e.touches[0].pageY - touchStartY;
// Add touch offsets to original target coordinates,
// then assign them to target element's styles.
e.target.style.left = targetStartX + touchOffsetX + 'px';
e.target.style.top = targetStartY + touchOffsetY + 'px';
}
draggable.addEventListener('touchstart', dragStart);
draggable.addEventListener('touchmove', dragMove);
Here's a live demo.

Related

How to move a canvas element on top of another canvas element

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.

How to draw on a HTML5 canvas with a stylus

I used onmousedown, onmousemove and onmouseup events to draw with JavaScript on a HTML5 canvas object. Everything is working.
Now I want to replace the mouse with a sylus (Wacom Intous Pro)
Therefore I replaced the mouse Events with onpointerdown, onpointerup and onpointermove.
But now, if I touch and move the pen, I do not get any onpointermove Events, instead the whole page is draged. By adding html, body {overflow: hidden} to the HTML construct I could Prevent this behaviour, but still I do not get any onpointermove Events. These I only get when the pen is above the tablet.
Has somebody an idea how to solve it?
Corrently this is the concept I use (but not working):
$(function() {
var el=document.getElementById("myDraw");
el.onpointerdown = down_handler;
el.onpointerup = up_handler;
el.onpointermove = move_handler;
ctx = el.getContext("2d");
ctx.canvas.width = window.innerWidth*0.75;
ctx.canvas.height = window.innerHeight*0.75;
});
function move_handler(ev)
{
if (onTrack>0)
{
var xPosition = ev.clientX-getPosition(document.getElementById("myDraw")).x;
var yPosition = ev.clientY-getPosition(document.getElementById("myDraw")).y;
ctx.beginPath();
ctx.lineWidth=10*ev.pressure;
ctx.moveTo(lastX,lastY);
ctx.lineTo(xPosition,yPosition);
ctx.stroke();
lastX = xPosition;
lastY = yPosition;
}
}
function down_handler(ev)
{
var xPosition = ev.clientX-getPosition(document.getElementById("myDraw")).x;
var yPosition = ev.clientY-getPosition(document.getElementById("myDraw")).y;
ctx.beginPath();
ctx.arc(xPosition, yPosition, 5, 0, 2 * Math.PI);
ctx.stroke();
startX = xPosition;
startY = yPosition;
lastX = xPosition;
lastY = yPosition;
onTrack=1;
var el=document.getElementById("myRemoteDraw");
el.setPointerCapture(ev.pointerId);
console.log('pointer down '+ev.pointerId);
}
function up_handler(ev)
{
var xPosition = ev.clientX-getPosition(document.getElementById("myDraw")).x;
var yPosition = ev.clientY-getPosition(document.getElementById("myDraw")).y;
ctx.beginPath();
ctx.rect(xPosition-5, yPosition-5, 10, 10);
ctx.stroke();
onTrack = 0;
var el=document.getElementById("myRemoteDraw");
el.releasePointerCapture(ev.pointerId);
console.log('pointer up '+ev.pointerId);
}
This CSS should help you:
<style>
/* Disable intrinsic user agent touch behaviors (such as panning or zooming) */
canvas {
touch-action: none;
}
</style>
or in JavaScript:
ctx.canvas.style.touchAction = "none";
More details from this link about "touch-action" and some general info in this link about inputs:
The touch-action CSS property is used to specify whether or not the browser should apply its default (native) touch behavior (such as zooming or panning) to a region. This property may be applied to all elements except: non-replaced inline elements, table rows, row groups, table columns, and column groups.
A value of auto means the browser is free to apply its default touch behavior (to the specified region) and the value of none disables the browser's default touch behavior for the region. The values pan-x and pan-y, mean that touches that begin on the specified region are only for horizontal and vertical scrolling, respectively. The value manipulation means the browser may consider touches that begin on the element are only for scrolling and zooming.

Moving dynamically created grouped object within contained area? (html5canvas - fabric.js)

What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.
The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;
There is no possible way for the dynamically created shape to be
dragged anywhere outside the image area. (completed - ish, relies on 2nd step)
You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)
Here are two examples that I've drawn up, neither of which work correctly;
First example - getBoundingRect does not update and is bound to the image
Second example - getBoundingRect does update and is bound to the grouped object
From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.
The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.
I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?
Code snippet below for first plunk;
// creates group
var objs = canvas.getObjects();
var group = new fabric.Group(objs, {
status: 'moving'
});
// sets grouped object position
var originalX = active.left,
originalY = active.top,
mouseX = evt.e.pageX,
mouseY = evt.e.pageY;
active.on('moving', function(evt) {
group.left += evt.e.pageX - mouseX;
group.top += evt.e.pageY - mouseY;
active.left = originalX;
active.top = originalY;
originalX = active.left;
originalY = active.top;
mouseX = evt.e.pageX;
mouseY = evt.e.pageY;
// sets boundary area for image when zoomed
// THIS IS THE PART THAT DOESN'T WORK
active.setCoords();
// SET BOUNDING RECT TO 'active'
var boundingRect = active.getBoundingRect();
var zoom = canvas.getZoom();
var viewportMatrix = canvas.viewportTransform;
// scales bounding rect when zoomed
boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
boundingRect.width /= zoom;
boundingRect.height /= zoom;
var canvasHeight = canvas.height / zoom,
canvasWidth = canvas.width / zoom,
rTop = boundingRect.top + boundingRect.height,
rLeft = boundingRect.left + boundingRect.width;
// checks top left
if (rTop < canvasHeight || rLeft < canvasWidth) {
group.top = Math.max(group.top, canvasHeight - boundingRect.height);
group.left = Math.max(group.left, canvasWidth - boundingRect.width);
}
// checks bottom right
if (rTop > 0 || rLeft > 0) {
group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
}
});
// deactivates all objects on mouseup
active.on('mouseup', function() {
active.off('moving');
canvas.deactivateAll().renderAll();
})
// sets group
canvas.setActiveGroup(group.setCoords()).renderAll();
}
EDIT:
I've added comments and tried to simplify the code in the plunks.
The relevant code starts within the if (active.id == "img") { code block.
I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )
I've removed one of the plunks to avoid confusion.
Let me know if it helps, or If I should try to simplify further.
Thanks!
I think that the grouping was messing with the position of the background image. So, I tried removing the group when the image is moving and manually updating the position of the rect instead.
It sets the last position of the image before moving
var lastLeft = active.left,
lastTop = active.top;
And then it updates those and the position of the rect every time the image moves
rect.left += active.left - lastLeft;
rect.top += active.top - lastTop;
// I think this is needed so the rectangle can be re-selected
rect.setCoords();
lastLeft = active.left;
lastTop = active.top;
Since the image has to stay within the canvas, the rect stays inside the canvas, too, whenever the image moves. The rest of the code you wrote seemed to work fine.
http://plnkr.co/edit/6GGcUxGC7CjcyQzExMoK?p=preview

making canvas register mouse move with div over it

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)

What is technique behind draggable objects?

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.

Categories