Detect direction of touchpad swipe properly - javascript

I was able to scale in scale out x axis and y axis , Its working very good with arrow keys , I want to do that with touchpad aswel.I tried this below code ,its working but its not smooth .Sometimes when i zoom in X , its even zooming in Y and vice versa.
window.addEventListener('mousewheel', function(e) {
window.addEventListener('mousewheel', function(e) {
e.preventDefault();
yDelta = e.deltaY;
xDelta = e.deltaX;
if (yDelta < -1 && Math.round(xDelta) < 1) {
zoomOutY();
} else if (yDelta > 1 && Math.round(xDelta) < 1) {
zoomInY();
} else if (xDelta < -1 && Math.round(yDelta) < 1) {
zoomOut();
} else if (xDelta > -1 && Math.round(yDelta) < 1) {
zoomIn();
}
}, {
passive: false
});
And Again Same issue with mousemove method , how to detect the 4 directions smoothly , below is my code.
document.addEventListener('mousemove', mouseMoveMethod);
document.addEventListener('mousedown', mouseDownMethod);
document.addEventListener('mouseup', mouseUpMethod);
// Prevent context menu popup, so we can move our mouse.
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
return false;
}, false);
mouseMoveMethod = function(e) {
if (e.ctrlKey || mouseIsHeld) {
let offsetX = 0
let offsetY = 0
if (e.pageX > oldx && e.pageY == oldy) {
direction = "East";
offsetX -= 1
} else if (e.pageX == oldx && e.pageY > oldy) {
direction = "South";
offsetY += 1
} else if (e.pageX == oldx && e.pageY < oldy) {
direction = "North";
offsetY -= 1
} else if (e.pageX < oldx && e.pageY == oldy) {
offsetX += 1
direction = "West";
}
updateKeyboardPan(offsetX, offsetY);
oldx = e.pageX;
oldy = e.pageY;
})
Again in the above code I am able to find the direction , but its lagging and hanging in middle.Is this the right approach ? or can I improve my code to improve my swipe/mousewheel direction , thank you.

I found a quite interesting example of multi-touch trackpad gestures in JavaScript.
The code snippet below utilizes this for overriding LCJS chart interactions for trackpad. To me it seems to perform in a surprisingly intuitive manner for zooming in/out on pinch interaction (2 fingers, move to opposite directions) and panning with dragging 2 fingers in same direction.
I did not find any way to differentiate pinch interaction along X and Y separately, it seems that the JS events just get a single value for both, which is assigned to deltaY.
const {
lightningChart
} = lcjs;
const {
createProgressiveTraceGenerator
} = xydata;
const chart = lightningChart().ChartXY();
const series = chart.addLineSeries()
createProgressiveTraceGenerator()
.setNumberOfPoints(1000)
.generate()
.toPromise()
.then(data => {
series.add(data)
})
chart.setMouseInteractionWheelZoom(false)
chart.onSeriesBackgroundMouseWheel((_, e) => {
const xInterval = chart.getDefaultAxisX().getInterval()
const yInterval = chart.getDefaultAxisY().getInterval()
if (e.ctrlKey) {
// Zoom in / out (no separation of X or Y amount!)
const zoomAmount = e.deltaY * 0.01
chart.getDefaultAxisX().setInterval(xInterval.start + zoomAmount * (xInterval.end - xInterval.start), xInterval.end - zoomAmount * (xInterval.end - xInterval.start), false, true)
chart.getDefaultAxisY().setInterval(yInterval.start + zoomAmount * (yInterval.end - yInterval.start), yInterval.end - zoomAmount * (yInterval.end - yInterval.start), false, true)
} else {
// Pan X and Y simultaneously.
const panX = e.deltaX * 0.001 * (xInterval.end - xInterval.start)
const panY = e.deltaY * -0.001 * (yInterval.end - yInterval.start)
chart.getDefaultAxisX().setInterval(xInterval.start + panX, xInterval.end + panX, false, true)
chart.getDefaultAxisY().setInterval(yInterval.start + panY, yInterval.end + panY, false, true)
}
e.preventDefault()
})
<script src="https://unpkg.com/#arction/xydata#1.4.0/dist/xydata.iife.js"></script>
<script src="https://unpkg.com/#arction/lcjs#3.0.0/dist/lcjs.iife.js"></script>

Related

allow native scroll in container during touchmove event like with drag and drop

Is there a way to allow native scrolling easily without heavy JS modifications when you reach the border of a div via custom drag and drop via touchmove listener?
When you drag the text in the div here you'll see the div inside is scrolling automatically
I provided an example with touchmove listeners but this one does not scroll, when you reach a border with your mouse
Is there an easy way to include a scrolling behavior to the 2nd example?
const element = document.body.querySelector('#draggable');
const isInContainer = (x,y) => {
const elements = document.elementsFromPoint(x, y)
return elements.find(el => el && el.classList && el.classList.contains('container')) || false;
}
const onMouseMove = (e) => {
if(isInContainer(e.pageX, e.pageY)){
element.style.top = e.pageY + 'px';
element.style.left = e.pageX + 'px';
}
}
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp)
}
element.addEventListener('mousedown', (e) => {
e.preventDefault();
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp)
});
In case someone has a native better solution I'm willing to accept that one... for the time being this would be my current way to solve the issue.
Note: I made a custom interval for scrolling and don't use the mousemove event so users don't have to move the mouse to trigger it. moving outside will start the interval moving inside will clear it.
// the container that should scroll
const scrollBody = document.getElementById('scrollContainer');
// parameter to check which directions should scroll
const scrollPositions = {
left: false,
right: false,
up: false,
down: false,
}
// how far should be scrolled
const nextScrollDistance = {
x: 0,
y: 0
}
// scroll interval
let scrollInterval= null;
const startScrolling = (scrollBody) => {
if (scrollInterval !== null) {
return true;
}
const intervalCallback = () => {
if (scrollInterval !== null && nextScrollDistance.x === 0 && nextScrollDistance.y === 0) {
window.clearInterval(scrollInterval);
scrollInterval = null;
} else {
scrollBody.scrollLeft += nextScrollDistance.x;
scrollBody.scrollTop += nextScrollDistance.y;
}
}
scrollInterval = window.setInterval(intervalCallback, 50);
}
const onMouseMove = (e) => {
if(isInContainer(e.pageX, e.pageY)){
element.style.top = e.pageY + 'px';
element.style.left = e.pageX + 'px';
}
const rects = scrollBody.getBoundingClientRect();
// check directions
// max x that can be scrolled
const maxX = scrollBody.scrollWidth - scrollBody.clientWidth;
// max y that can be scrolled
const maxY = scrollBody.scrollHeight - scrollBody.clientHeight;
// check all directions if it's even possible to scroll
const canScrollTop = Math.round(scrollBody.scrollTop) > 0;
const canScrollBottom = Math.round(scrollBody.scrollTop) < maxY;
const canScrollLeft = Math.round(scrollBody.scrollLeft) > 0;
const canScrollRight = Math.round(scrollBody.scrollLeft) < maxX;
// current x and y coordinates of the mouse
const x = e.pageX;
const y = e.pageY;
// dynamic value to decrease the speed.. otherwise it might scroll too fast
const minifier = 2;
// the modifiers for scrollTop and scrollLeft
nextScrollDistance.y = 0;
nextScrollDistance.x = 0;
if (canScrollBottom && y > rects.bottom) {
// distance between the right border and the mouse
const distance = Math.abs(y - rects.bottom);
// the next time it scrolls -> scroll distance / minifier
nextScrollDistance.y = Math.round(distance / minifier)
scrollPositions.down = true;
} else {
scrollPositions.down = false;
}
// all other directions...
if (canScrollTop && y < rects.top) {
const distance = Math.abs(y - rects.top);
nextScrollDistance.y = Math.round(distance / minifier) * -1;
scrollPositions.up = true;
} else {
scrollPositions.up = false;
}
if (canScrollRight && x > rects.right) {
const distance = Math.abs(x - rects.right);
nextScrollDistance.x = Math.round(distance / minifier)
scrollPositions.right = true;
} else {
scrollPositions.right = false;
}
if (canScrollLeft && x < rects.left) {
const distance = Math.abs(x - rects.left);
nextScrollDistance.x = Math.round(distance / minifier) * -1;
scrollPositions.left = true;
} else {
scrollPositions.left = false;
}
// in case one of those are set.. trigger scrolling
if (nextScrollDistance.x || nextScrollDistance.y) {
startScrolling();
}
}

event propagation in three.js

I have a scene with several objects. I use an orthographic camera controller. When I have a selected object, I want to rotate it with the mouse using shiftKey + but 1 (rotation around its own center) and translate it in the scene using shiftKey + but 3.
When setting up my mouse events handlers, I tried to stop the camera controls to fire, but it still does as soon as button is up.
Can someone show me where I'm wrong ?
$(canva).mousedown(function (e) {
if (selObject && e.shiftKey) {
e.preventDefault();
e.stopPropagation()
e.cancelBubble=true;
e.returnValue=false;
controls.enabled = false
mDown = e.which
clientX = e.clientX
clientY = e.clientY
return false
}
});
$(canva).mousemove(function (e) {
if (! selObject || ! mDown) {
return true
}
e.stopPropagation()
e.preventDefault();
e.cancelBubble=true;
e.returnValue=false;
dX = e.clientX - clientX
dY = e.clientY - clientY
if (mDown == 1) {
dX /= 100
dY /= 100
selObject.rotation.x += dX
selObject.rotation.y += dY
} else if (mDown == 3) {
dX /= 2
dY /= 2
var pos = mouse2world(e)
selObject.position = pos
}
clientX = e.clientX
clientY = e.clientY
return false
});
$(canva).mouseup(function (e) {
if (mDown) {
mDown = 0
e.preventDefault();
e.stopPropagation()
e.cancelBubble=true;
e.returnValue=false;
controls.enabled = true
return false
}
});
Try
event.stopImmediatePropagation();
instead of
event.stopPropagation();
Source: MDN https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation
Semicolon is missing on all e.stopPropagation() statements.

touchmove handler after touchstart handler wont work

I have a problem with an touchmove event. When the user touches the display (touchstart) the touchmove event handler and game() should be executed and if the user leaves the screen everything should be stopped. But then the if conditions for the collisiondetection interval won't work right because e.pageX and e.pageY always have the coordinates of the touchstart and won't update their values when the user moves their finger (touchmove) on the screen. How can I fix this? demo
$("body").on({
'touchstart mousedown': function (e) {
$(this).on('touchmove mousemove');
collisiondetection = setInterval(function() {
var xp1 = $("#n1").position();
if (e.pageY >= xp1.top && e.pageY <= xp1.top + cy * 10 && e.pageX >= xp1.left && e.pageX <= xp1.left + cx * 25) {
console.log("hit");
}
var xp2 = $("#n2").position();
if (e.pageY >= xp2.top && e.pageY <= xp2.top + cy * 10 && e.pageX >= xp2.left && e.pageX <= xp2.left + cx * 25) {
console.log("hit");
}
},10);
game();
},
'touchend mouseup': function (e) {
$(this).off('touchmove mousemove');
clearInterval(animaterects);
clearInterval(collisiondetection);
}
});
UPDATE: If I edit it to 'touchstart mousedown touchmove mousemove': function (e) { the collision detection and the updating coordinates works well but the animation dont.
Becasue your code doesn't update the coordinates when users move their fingers.
$("body").on({
'touchstart mousedown': function (e) {
var pageX = e.pageX
var pageY = e.pageY;
$(this).on('touchmove mousemove',function(e){
pageX = e.pageX;
pageY = e.pageY;
});
collisiondetection = setInterval(function() {
var xp1 = $("#n1").position();
if (pageY >= xp1.top && pageY <= xp1.top + cy * 10 && pageX >= xp1.left && pageX <= xp1.left + cx * 25) {
console.log("hit");
}
var xp2 = $("#n2").position();
if (pageY >= xp2.top && pageY <= xp2.top + cy * 10 && pageX >= xp2.left && pageX <= xp2.left + cx * 25) {
console.log("hit");
}
},10);
game();
},
'touchend mouseup': function (e) {
$(this).off('touchmove mousemove');
clearInterval(animaterects);
clearInterval(collisiondetection);
}
});

Pan Svg While dragging elements

I want to achieve 'Panning' in svg while 'dragging' an element in particular direction.
Let say i select an element and start 'dragging' it upward untill it reached top of screen, now my svg should pan upwards automatically, without causing any problem with dragging. how i can achieve this.?
i have made a small mockup of this, where user can select and drag elements. it also contain two button, which cause svg to pan upward and downward. I am achiveing 'Panning' by changing 'ViewBox' of svg. ( i have to use this logic, i cannot use any other solution);
here is the fiddle link : http://jsfiddle.net/9J25r/
Complete Code:-
addEventListener('mousedown', mousedown, false);
var mx, my;
var dx, dy;
var mainsvg = document.getElementById('svg');
var selectedElement;
var eleTx, eleTy;
function getSvgCordinates(event) {
var m = mainsvg.getScreenCTM();
var p = mainsvg.createSVGPoint();
var x, y;
x = event.pageX;
y = event.pageY;
p.x = x;
p.y = y;
p = p.matrixTransform(m.inverse());
x = p.x;
y = p.y;
x = parseFloat(x.toFixed(3));
y = parseFloat(y.toFixed(3));
return {x: x, y: y};
}
function mousedown(event) {
if (event.target.id === 'arrow_t') {
panning('up');
}
else if (event.target.id === 'arrow_b') {
panning('down');
}
else if (event.target.id.split('_')[0] === 'rect') {
selectedElement = event.target;
var translatexy = selectedElement.getAttribute('transform');
translatexy = translatexy.split('(');
translatexy = translatexy[1].split(',');
eleTx = translatexy[0];
translatexy = translatexy[1].split(')');
eleTy = translatexy[0];
eleTx = parseFloat(eleTx);
eleTy = parseFloat(eleTy);
var xy = getSvgCordinates(event);
mx = xy.x;
my = xy.y;
mx = parseFloat(mx);
my = parseFloat(my);
addEventListener('mousemove', drag, false);
addEventListener('mouseup', mouseup, false);
}
}
function drag(event) {
var xy = getSvgCordinates(event);
dx = xy.x - mx;
dy = xy.y - my;
selectedElement.setAttribute('transform', 'translate(' + (eleTx + dx) + ',' + (eleTy + dy) + ')');
}
function mouseup(event) {
removeEventListener('mousemove', drag, false);
removeEventListener('mouseup', mouseup, false);
}
function panning(direction) {
var viewBox = svg.getAttribute('viewBox');
viewBox = viewBox.split(' ');
var y = parseFloat(viewBox[1]);
if (direction === 'up')
{
y+=5;
}
else if (direction === 'down')
{
y-=5;
}
viewBox=viewBox[0]+' '+y+' '+viewBox[2]+' '+viewBox[3];
svg.setAttribute('viewBox',viewBox);
}
here is the fiddle link : http://jsfiddle.net/9J25r/
EDIT:- (UPDATE)
I use the solution of Ian , it works well on the sample, but when i applied it to my original application, it did not work. check the below gif. You can see the 'gap' between mouse pointer and element. how i can remove that? .
This is one way, I've just done it with the Y/vertical for the moment...
You may want to adjust it, so that if the cursor is off the screen it adjusts the viewBox automatically as well, depends how you want it to drag (otherwise you will need to keep wiggling it to kick the drag func in).
var viewBox = svg.getAttribute('viewBox');
viewBoxSplit = viewBox.split(' ');
if( ely < viewBoxSplit[1] ) {
panning('down');
} else if( ely + +event.target.getAttribute('height')> +viewBoxSplit[1] + 300 ) {
panning('up');
}
jsfiddle here

Moving a div when mouse near

I want that the div I created will move when the mouse is getting near to him.
Here is the fiddle link: http://jsfiddle.net/jLAq3/2/
Basic starting code (because I don't have a clue how to do it):
$('#leaf').();
Bind a function to the movement of the mouse. As the mouse moves:
Get the coordinates of the element.
Get the coordinates of the cursor.
Compare cursor coordinates with element coordinates.
If cursor is near element, move element - else do nothing.
Easy stuff.
This is a start. Every time the mouse is moving outside the leaf then a message appears.
$('body').mousemove(function(e){
var w = $('#leaf').outerWidth(),
h = $('#leaf').outerHeight(),
x = e.pageX,
y = e.pageY;
if(x > w && y > h)
{
console.log("The leaf is moving");
}
})
Furthermore you can apply some css with js to the leaf for movement etc. In a more complex example you have to spot more carefully the position and not simply rely on the width and the height of the image.
Here is a start.
http://jsfiddle.net/Lpg8x/80/
$( 'body' ).mousemove( function( event ) {
if( isNear( $( '#near' ), 20, event ) ) {
$( '#near' ).html( 'is near!' );
} else {
$( '#near' ).empty();
};
} );
function isNear( $element, distance, event ) {
var left = $element.offset().left - distance,
top = $element.offset().top - distance,
right = left + $element.width() + ( 2 * distance ),
bottom = top + $element.height() + ( 2 * distance ),
x = event.pageX,
y = event.pageY;
return ( x > left && x < right && y > top && y < bottom );
};
Have fun!
Here is a basic working example of how you actually would do all that
http://jsfiddle.net/jLAq3/10/
var leafX = 0, leafY = 0;
$('#leaf').css({position: 'relative'});
$(document).mousemove(function(e){
var offset = $('#leaf').offset()
,x1 = offset.left - 20
,x2 = offset.left + $('#leaf').width() + 20
,y1 = offset.top
,y2 = offset.top + $('#leaf').height() + 20
,center, mousePos
;
if(e.pageX > x1 && e.pageX < x2 && e.pageY > y1 && e.pageY < y2) {
center = (x2 - x1) / 2;
mousePos = e.pageX - x1;
if(mousePos < center) {
leafX += 20;
} else {
leafX -= 20;
}
center = (y2 - y1) / 2;
mousePos = e.pageY - y1;
if(mousePos < center) {
leafY += 20;
} else {
leafY -= 20;
}
}
$('#leaf').css({ top : leafY + 'px', left : leafX + 'px'});
});
But you should really learn the basics of DHTML before jumping into things, for example the difference between position absolute and relative, how to actually move HTML elements, layering, event binding etc.
Here are couple of good resources:
http://www.quirksmode.org/sitemap.html
http://www.w3schools.com/

Categories