I would like to pan a canvas's viewport on a touch device, I wrote this code:
canvas.on('touch:drag', function (opt) {
var e = opt.e;
if (this.isDragging) {
if (e.clientX === undefined ) {
this.isDragging = false;
} else {
this.viewportTransform[4] += e.clientX - this.lastPosX;
this.viewportTransform[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
} else {
this.isDragging = true;
this.selection = false;
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
});
This works well on my desktop PC but not on my smartphone (The viewport is not moving): Do you have an idea why?
In touch devices, normally you have e.touches[0].clientX instead of e.clientX
Related
I want to achieve 2 things on my page:
drag HTML elements on the screen;
zoom the page using the "wheel" event.
I can accomplish both of these as follows:
Element Drag:
function make_element_draggable(id) {
const elem = document.getElementById(id);
elem.style.position = "absolute";
let initX, initY, firstX, firstY, whichDown;
window.addEventListener("mouseup", function() {
if(whichDown) {
whichDown.style.zIndex = 0;
}
whichDown = null;
}, false);
window.addEventListener("mousemove", draggable, false);
elem.addEventListener("mousedown", function(e) {
e.preventDefault();
whichDown = this;
initX = this.offsetLeft;
initY = this.offsetTop;
firstX = e.pageX;
firstY = e.pageY;
});
function draggable(e) {
e.preventDefault();
if(!whichDown) return;
whichDown.style.zIndex = 9;
whichDown.style.left = initX + e.pageX - firstX + "px";
whichDown.style.top = initY + e.pageY - firstY + "px";
}
}
Page Zoom:
function page_zoom(container_id) {
zoom = 1;
zoom_speed = 0.1;
const container = document.getElementById(container_id);
document.addEventListener("wheel", function(e) {
if(e.deltaY > 0) {
container.style.transform = `scale(${zoom += zoom_speed})`;
} else {
container.style.transform = `scale(${zoom -= zoom_speed})`;
}
});
}
HTML:
<body id="body">
<div id="container">
<a id="text_1">TEXT 1</a>
</div>
</body>
Usage:
make_element_draggable("text_1")
page_zoom("body")
Here is the result.
Notice how the drag works perfectly when no zoom in enabled (text remains centered in the circle), but once zoom in enabled the text no longer maintains its position relative to the cursor.
Is there a way to compensate for zoom amount, perhaps using it as a factor to adjust the top and left settings of the drag function?
Here is a CODEPEN showing all the code. Drag the text element, then zoom using your mouse wheel (trackpad on Mac), and notice the incorrect positioning of the text element relative to the cursor.
You forgot to take in account zoom when dragging:
function make_element_draggable(id) {
const elem = document.getElementById(id);
elem.style.position = "absolute";
let initX, initY, firstX, firstY, whichDown;
window.addEventListener("mouseup", function() {
if(whichDown) {
whichDown.style.zIndex = 0;
}
whichDown = null;
}, false);
window.addEventListener("mousemove", draggable, false);
elem.addEventListener("mousedown", function(e) {
e.preventDefault();
whichDown = this;
initX = this.offsetLeft;
initY = this.offsetTop;
firstX = e.pageX;
firstY = e.pageY;
});
function draggable(e) {
e.preventDefault();
if(!whichDown) return;
whichDown.style.zIndex = 9;
whichDown.style.left = initX + (e.pageX - firstX)/zoom + "px";
whichDown.style.top = initY + (e.pageY - firstY)/zoom + "px";
}
}
function page_zoom(container_id) {
zoom = 1;
zoom_speed = 0.1;
const container = document.getElementById(container_id);
document.addEventListener("wheel", function(e) {
if(e.deltaY > 0) {
container.style.transform = `scale(${zoom += zoom_speed})`;
} else {
container.style.transform = `scale(${zoom -= zoom_speed})`;
}
});
}
page_zoom("body")
make_element_draggable("text_1")
<body id="body">
<div id="container">
<a id="text_1">TEXT 1</a>
</div>
</body>
I'm trying to snap an element below another element and make it stay put. The function is ok, but it runs every time I move it 10px. So it moves 10px even further each time..
function move_box() {
if(el.offsetLeft = 10){
console.log('moved the element 10px to the left!');
el.setAttribute('class', 'snap_in'); // class with css (transform: translateY(10px)
let not_called = false;
console.log(not_called);
} else if (not_called = false){
console.log(not_called);
el.setAttribute('class', 'dont_move'); //class with css transform:translateY(0px);
}
}
}
use '==' instead of =
you are assigning not comparing
const el = document.querySelector('.item');
let not_called;
el.addEventListener('mousedown', mousedown);
function mousedown(e){
window.addEventListener('mousemove', mousemove);
window.addEventListener('mouseup', mouseup);
let prevX = e.clientX;
let prevY = e.clientY;
function mousemove(e) {
let newX = prevX - e.clientX;
let newY = prevY - e.clientY;
const rect = el.getBoundingClientRect();
el.style.left = rect.left - newX + "px";
el.style.top = rect.top - newY + "px";
prevX = e.clientX;
prevY = e.clientY;
}
function mouseup() {
window.removeEventListener('mousemove', mousemove);
window.removeEventListener('mouseup', mouseup);
console.log(el.offsetLeft);
console.log(el.offsetTop);
//let leftoffset = el.offsetLeft;
if (not_called == false){
console.log(not_called);
el.setAttribute('class', 'item');
}
else if(el.offsetLeft >= 180 && el.offsetLeft <= 220){
console.log('yes!');
el.setAttribute('class', 'snap_in');
not_called = false;
console.log(not_called);
}
}
}
I'm building a Floor Map Plan for an Expo using <canvas>.
With some research and trial-and-error I managed to make my <canvas> scroll horizontally. As shown here Floor Plan Map - Horizontal Only
But I want my <canvas> to scroll/move anywhere the user wants. Example: scroll vertically and horizontally at same time.
I tried adding a Y axis but it just scrolls diagonally with some weird mouse interaction. Here's the extract of the lines that manage the scrolling:
function drawSettings(){
var dragging = false;
var lastX;
var lastY;
var marginLeft = 0;
var marginTop = 0;
//Scroll settings start
canvas.addEventListener('mousedown', function(e) {
var evt = e || event;
dragging = true;
lastX = evt.clientX;
lastY = evt.clientY;
e.preventDefault();
}, false);
window.addEventListener('mousemove', function(e) {
var evt = e || event;
if (dragging) {
var delta = evt.clientX - lastX;
lastX = evt.clientX;
lastY = evt.clientY;
marginLeft += delta;
marginTop += delta;
canvas.style.marginLeft = marginLeft + "px";
canvas.style.marginTop = marginTop + "px";
}
e.preventDefault();
}, false);
window.addEventListener('mouseup', function() {
dragging = false;
}, false);
//Scroll settings end
}
Here's the result of the example above: Floor Map Plan - Diagonal Error
How can I solve this?
EDIT FOR FUTURE REFERENCE: Floor Plan Map - FULLY WORKING
You can just do what you did for horizontal dragging, for vertical dragging too, like this:
window.addEventListener('mousemove', function(e) {
var evt = e || event;
if (dragging) {
var deltaX = evt.clientX - lastX;
var deltaY = evt.clientY - lastY;
lastX = evt.clientX;
lastY = evt.clientY;
marginLeft += deltaX;
marginTop += deltaY;
canvas.style.marginLeft = marginLeft + "px";
canvas.style.marginTop = marginTop + "px";
}
e.preventDefault();
}, false);
The position of mouse clicks on canvas is correct when I do not have a navigation bar. However, it shifted when I added a navigation bar at the top of the page.
Here is my code:
<div class="container-fluid">
<div class="row" style="display:none">
<div class="col-md-12">
<nav class="navbar navbar-default" role="navigation" style="margin-bottom:0px; margin-top:5px;">...</nav>
</div></div></div>
<div class="row">
<div class="col-md-6" style="background-color: #d6d9dc; height:700px;">
<div id="myimg" name="myimg"></div>
<div id="canvasContainer" style="height:600px;">
<canvas id="canvas" style="position: absolute; background-color:#ecf0f1"></canvas>
</div>
</div>
</div>
</div>
Javascript for cursor position:
var canvas = document.getElementById('canvas');
var canvasContainer = document.getElementById("canvasContainer");
var context = canvas.getContext('2d');
var flag = false,
prevX = 0,
currX = 0,
prevY = 0,
currY = 0,
dot_flag = false;
var x = "red",
pixelSize = 2;
function make_base(image_url){
image = new Image();
image.src = image_url;
canvas.width = 1000;
canvas.height = 600;
canvas.style.background = 'url('+image.src+')';
image.onload = function(){
console.log("onload "+image.width)
canvas.width = image.width;
canvas.height = image.height;
canvas.addEventListener("mousemove", function (e) {
findxy('move', e)
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e)
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e)
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e)
}, false);
}
}
function color(obj) {
console.log(obj);
if (obj === "white"){
pixelSize = 16;
x = "white";
}else{
x = obj.value;
pixelSize = 2;
}
}
function draw() {
context.beginPath();
context.moveTo(prevX, prevY);
context.lineTo(currX, currY);
context.strokeStyle = x;
context.lineWidth = pixelSize;
if(pixelSize == 16){
context.clearRect(prevX, prevY, 14, 14);
}else{
context.stroke();
}
context.closePath();
}
function findxy(res, e) {
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
flag = true;
dot_flag = true;
if (dot_flag) {
context.beginPath();
context.fillStyle = x;
context.fillRect(currX, currY, 2, 2);
context.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
draw();
}
}
}
Any suggestion is greatly appreciated. Thank you.
Use the mouse event properties pageX, pageY they give you the position of the mouse relative to the top left of the page. You then get the position of the element you have the mouse events on with event.target.getBoundingClientRect. Then you need to remove the scroll offset the page may be at.
At this stage you have the mouse position to the top left of the element. You have one issue left and that is the element's border width. If you have the border width in pixels then subtract that as well from the mouse.x, mouse.y pos, if the border is using a different metric and or the top and left borders are different sizes you will have to make the appropriate adjustment.
var mouse = {x : 0, y : 0, events : "mousemove,mousedown,mouseup"};
function mouseEvent(e) {
var bounds = e.target.getBoundingClientRect();
mouse.x = e.pageX - bounds.left - scrollX; // is window.scrollX same for Y
mouse.y = e.pageY - bounds.top - scrollY; //
}
mouse.events // chaining functions
.split(",")
.forEach((eName) => {
canvas.addEventListener(eName,mouseEvent)
});
For older browsers you will need to get the scroll position via.
// add this to the mouse event listeners.
var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
var currentScrollX = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft;
var currentScrollY = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
From MDN web API scrollY
The topic may be insufficient to adress my question sorry for that because I am not so competent with the terms. The thing is I have a canvas and can draw things on that while I click mouse.. BUT when I try to do it with touching (i.e iphone and other touch operated devices) it did not detect the movements so no thing can be drawn on canvas..
What do I need to detect touch actions ? any ideas would be great..
here is my js fiddle link http://jsfiddle.net/regeme/xNSVm/
also here is the script for drawing
painting = false;
var WIDTH = 300;
var HEIGHT =300;
function clear() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = "white";
}
function generate(){
var newCanvas = document.createElement('canvas');
newCanvas.width = WIDTH;
newCanvas.height = HEIGHT;
document.getElementById('container').appendChild(newCanvas);
ctx = newCanvas.getContext('2d');
ctx.fillStyle = "black";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = "white";
newCanvas.onmousedown = function(e) {
painting = true;
ctx = newCanvas.getContext('2d');
lastX = e.pageX - this.offsetLeft;
lastY = e.pageY - this.offsetTop;
};
newCanvas.onmouseup = function(e) {
painting = false;
};
newCanvas.onmousemove = function(e) {
if (painting) {
mouseX = e.pageX - this.offsetLeft;
mouseY = e.pageY - this.offsetTop;
// find all points between
var x1 = mouseX,
x2 = lastX,
y1 = mouseY,
y2 = lastY;
var steep = (Math.abs(y2 - y1) > Math.abs(x2 - x1));
if (steep){
var x = x1;
x1 = y1;
y1 = x;
var y = y2;
y2 = x2;
x2 = y;
}
if (x1 > x2) {
var x = x1;
x1 = x2;
x2 = x;
var y = y1;
y1 = y2;
y2 = y;
}
var dx = x2 - x1,
dy = Math.abs(y2 - y1),
error = 0,
de = dy / dx,
yStep = -1,
y = y1;
if (y1 < y2) {
yStep = 1;
}
for (var x = x1; x < x2; x++) {
if (steep) {
ctx.fillRect(y, x, 1, 1);
} else {
ctx.fillRect(x, y, 1, 1);
}
error += de;
if (error >= 0.5) {
y += yStep;
error -= 1.0;
}
}
lastX = mouseX;
lastY = mouseY;
}
};
}
document.getElementById('button').onclick = clear;
document.getElementById('generate').onclick = generate;
document.onmouseup = function(e) {
painting = false;
};
generate();
I can't comment or I would just leave this link in a comment. http://jsfiddle.net/gfcarv/66nVn/
However, since I am here I can kind of (emphasis on kind of) what is going on in the code.
This is the part where the touch events or the mouse events are attached:
// detect touch capabilities
var touchAvailable = ('createTouch' in document) || ('ontouchstart' in window);
// attach the touchstart, touchmove, touchend event listeners.
if(touchAvailable){
canvas.addEventListener('touchstart', draw, false);
canvas.addEventListener('touchmove', draw, false);
canvas.addEventListener('touchend', draw, false);
}
// attach the mousedown, mousemove, mouseup event listeners.
else {
canvas.addEventListener('mousedown', draw, false);
canvas.addEventListener('mousemove', draw, false);
canvas.addEventListener('mouseup', draw, false);
}
and this is the drawer function, it is basically using a switch statement to perform the correct calculations on the correct event that is happening
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
var type = null;
// map mouse events to touch events
switch(event.type){
case "mousedown":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchstart";
break;
case "mousemove":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchmove";
break;
case "mouseup":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchend";
break;
}
// touchend clear the touches[0], so we need to use changedTouches[0]
var coors;
if(event.type === "touchend") {
coors = {
x: event.changedTouches[0].pageX,
y: event.changedTouches[0].pageY
};
}
else {
// get the touch coordinates
coors = {
x: event.touches[0].pageX,
y: event.touches[0].pageY
};
}
type = type || event.type
// pass the coordinates to the appropriate handler
drawer[type](coors);
}
I would break those down and study that fiddle, then you should be able to either rework your code or just add-in the correct events. I would rework if possible just b/c the functions seem a little cleaner in this context.
If I have time tomorrow I will edit with some more info if needed just figured this would help you get started. Thanks!
you should use "touch" events , such as ouchstart,touchend,touchmove, here is a paper about touch events , hopes it will help you
http://www.html5rocks.com/en/mobile/touch/