I have the following code:
swipeDetect = function(el, callback){
var touchsurface = el,
swipedir,
startX,
startY,
distX,
distY,
threshold = 7, //required min distance traveled to be considered swipe
restraint = 100, // maximum distance allowed at the same time in perpendicular direction
allowedTime = 500, // maximum time allowed to travel that distance
maxangle=40,
elapsedTime,
startTime,
touchinprocess=false,
handleswipe = callback || function(swipedir){};
touchsurface.addEventListener('touchstart', function(e){
touchinprogress = true;
var touchobj = e.changedTouches[0];
swipedir = 'none';
dist = 0;
startX = touchobj.pageX;
startY = touchobj.pageY;
startTime = new Date().getTime();
}, false);
touchsurface.addEventListener('touchmove', function(e){
var touchobj = e.changedTouches[0];
distX = startX - touchobj.pageX;
distY = startY - touchobj.pageY;
rads = Math.atan(distY,distX);
var deg = rads * (180 / 3.14);
console.log(deg);
if (Math.abs(distY) > restraint && Math.abs(deg) > maxangle) e.preventDefault();
}, false);
touchsurface.addEventListener('touchend', function(e){
var touchobj = e.changedTouches[0];
distX = touchobj.pageX - startX;
distY = touchobj.pageY - startY;
elapsedTime = new Date().getTime() - startTime;
if (elapsedTime <= allowedTime){ // first condition for awipe met
if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met
swipedir = (distX < 0)? 'left' : 'right'; // if dist traveled is negative, it indicates left swipe
}
else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
swipedir = (distY < 0)? 'up' : 'down'; // if dist traveled is negative, it indicates up swipe
};
};
handleswipe(e,swipedir);
return true;
}, false);
},
I'm trying to get the angle in degrees of a touch swipe in the touch move function (and kill the default scroll if the swipe is above a certain number of degrees). But It keeps outputting numbers around 80. No matter how I swipe. What is wrong here?
I'm using this for image swiping and I want to let the user swipe and not have it scroll and swipe at the same time.
Maybe i'm going about this all wrong.
Thanks for your help.
The Math.atan(x)[1] function takes a single argument, which in your case should be the ratio of y over x:
rads = Math.atan(distY/distX);
It might be hard to see the difference, but in your initial code you have a , rather than a /. I ran your code with this change and it worked. Keep in mind that Math.atan2(y,x) does take two arguments.
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
I did some testing and I think the issue may be with your atan function. Give atan2 a try?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
It seems to essentially work for me.
Related
This is my first post so I'm trying to make my problem as clear as possible. I'm making a game and I want to improve my collision detection. This is because I want to check what side is being hit and stop the player from moving past it without using something general like if(collision(player, enemy)) player.x = enemy.x - player.w(width) because if the player were to collide with the top it wouldn't keep the player on top.
In the code it checks if any one of the statements is true and then returns it but it doesn't tell me which statement was the one that was equal to true so I can stop the player from moving accordingly, if that makes sense. If you have a more efficient collision detection for me to use it would be greatly appreciated.
I've already tried to make a position variable to be equal to whatever side gets collided into and then stop the player from moving past it but it only works for the left side and won't let my player jump over the enemy or block.
function collision(object1, object2) {
return !(
object1.x > object2.x + object2.w ||
object1.x + object1.w < object2.x ||
object1.y > object2.y + object2.h ||
object1.y + object1.h < object2.y
)
}
//Only works for the left side
if(collision(player, enemy)) player.x = enemy.x - player.w
I expect it to be able to tell me what side is being collided into and then either stop the player from moving past/into it and for the player to be able to be on top of the block/enemy without just being pushed to the left.
You'll want to calculate the distance between the x's and y's and also use the minimum distance that they could be colliding along each axis to find the depth along both axes. Then you can pick the smaller depth and move along that one. Here's an example:
if(collision(player, enemy)){
// Most of this stuff would probably be good to keep stored inside the player
// along side their x and y position. That way it doesn't have to be recalculated
// every collision check
var playerHalfW = player.w/2
var playerHalfH = player.h/2
var enemyHalfW = enemy.w/2
var enemyHalfH = enemy.h/2
var playerCenterX = player.x + player.w/2
var playerCenterY = player.y + player.h/2
var enemyCenterX = enemy.x + enemy.w/2
var enemyCenterY = enemy.y + enemy.h/2
// Calculate the distance between centers
var diffX = playerCenterX - enemyCenterX
var diffY = playerCenterY - enemyCenterY
// Calculate the minimum distance to separate along X and Y
var minXDist = playerHalfW + enemyHalfW
var minYDist = playerHalfH + enemyHalfH
// Calculate the depth of collision for both the X and Y axis
var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX
var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY
// Now that you have the depth, you can pick the smaller depth and move
// along that axis.
if(depthX != 0 && depthY != 0){
if(Math.abs(depthX) < Math.abs(depthY)){
// Collision along the X axis. React accordingly
if(depthX > 0){
// Left side collision
}
else{
// Right side collision
}
}
else{
// Collision along the Y axis.
if(depthY > 0){
// Top side collision
}
else{
// Bottom side collision
}
}
}
}
Working example
Here's a working example that you can play around with. Use the arrow keys to move the player around.
player = {
x: 9,
y: 50,
w: 100,
h: 100
}
enemy = {
x: 100,
y: 100,
w: 100,
h: 100
}
output = document.getElementById("collisionType");
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d")
function collision(object1, object2) {
return !(
object1.x > object2.x + object2.w ||
object1.x + object1.w < object2.x ||
object1.y > object2.y + object2.h ||
object1.y + object1.h < object2.y
)
}
function draw() {
ctx.clearRect(0, 0, 400, 400)
ctx.lineWidth = "5"
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.rect(player.x, player.y, player.w, player.h);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.rect(enemy.x, enemy.y, enemy.w, enemy.h);
ctx.stroke();
}
function handleCollision() {
if (collision(player, enemy)) {
var playerHalfW = player.w / 2
var playerHalfH = player.h / 2
var enemyHalfW = enemy.w / 2
var enemyHalfH = enemy.h / 2
var playerCenterX = player.x + player.w / 2
var playerCenterY = player.y + player.h / 2
var enemyCenterX = enemy.x + enemy.w / 2
var enemyCenterY = enemy.y + enemy.h / 2
// Calculate the distance between centers
var diffX = playerCenterX - enemyCenterX
var diffY = playerCenterY - enemyCenterY
// Calculate the minimum distance to separate along X and Y
var minXDist = playerHalfW + enemyHalfW
var minYDist = playerHalfH + enemyHalfH
// Calculate the depth of collision for both the X and Y axis
var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX
var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY
// Now that you have the depth, you can pick the smaller depth and move
// along that axis.
if (depthX != 0 && depthY != 0) {
if (Math.abs(depthX) < Math.abs(depthY)) {
// Collision along the X axis. React accordingly
if (depthX > 0) {
output.innerHTML = "left side collision"
} else {
output.innerHTML = "right side collision"
}
} else {
// Collision along the Y axis.
if (depthY > 0) {
output.innerHTML = "top side collision"
} else {
output.innerHTML = "bottom side collision"
}
}
}
} else {
output.innerHTML = "No collision"
}
}
keyStates = []
function handleKeys() {
if (keyStates[39]) {
player.x += 2 //Move right
} else if (keyStates[37]) {
player.x -= 2 //Move left
}
if (keyStates[38]) {
player.y -= 2 //Move up
}
if (keyStates[40]) {
player.y += 2 //Move down
}
}
function main() {
handleKeys();
draw();
handleCollision();
window.requestAnimationFrame(main);
}
window.onkeydown = function(e) {
keyStates[e.keyCode] = true
}
window.onkeyup = function(e) {
keyStates[e.keyCode] = false
}
main();
<h2 id="collisionType"></h2>
<canvas id="canvas" width='300' height='300'></canvas>
Reacting to the collision
Now that you know the side the collision happened on, it should be fairly trivial to decide how to react. It would be very similar to what you are currently doing for the left side just flip some signs around and change the axis.
Other Considerations
You may want to take into account your player's velocity (if it has one) otherwise the detection may fail.
If the player's velocity is too high, it might 'tunnel' through the enemy and no collision will be detected.
The player's movement can also look jittery if the velocity is not stopped upon collision
Can your objects rotate or have more than 4 sides? If so, you'll probably want to use another method as described below.
Here's a good answer to another post that talks in depth about collision engines
Other Methods
As for other collision detection methods, there's quite a few but one that comes to mind is Separating Axis Theorem which is a little more complex than what you have but will work with more complex convex shapes and rotation. It also tells you the direction and distance needed to move to resolve the collision. Here's a site that has interactive examples and goes in-depth on the subject. It doesn't appear to give a full implementation but those can be found other places.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am making a game and there is a wall that I don't want the player to pass. I am using html5 canvas and have a player object to hold the x and y values. The wall is at x: 650 and y: 0. Since the player is 20x20 pixels when its x coordinate is 630, it touches the wall.
if(player.x > 630 && player.y <= 500) {
player.x = 630;
}
What is wrong with this code? I appreciate any help!
Answer
The code you have give is OK, there is nothing wrong with it. So I suspect the problem is elsewhere in the code, most likely in the movement code. If you are moving the player after the wall test and then display it, the player may start to creep into the wall, but without the rest of the code it is hard to know what is wrong with your code.
I have included more details on the correct way to do collision tests as there are two answers showing only a partial solution. It is there as a general guide to collision testing and may not be directly applicable to the question.
Inter frame movement
The correct way to reflect an object from a surface.
You must take into account that the ball is moving between frames and that the collision may have happened at any time during the previous frame. The ball's distance from the wall after the collision is dependent on when during the previous frame it hit the wall. This is important if the ball moves slowly or quickly.
var dx = 10; // delta x velocity of object in pixels
var wx = 10; // width of object in pixels
var px = 90; // position of object in pixels
var wallX = 105; // position of wall
px += dx; // move the ball. Its position is now 100.
// its right side is at px + wx = 110.
// test if it has it the wall
if(px+wx > wallX){
dx = -dx; // reflect delta x
// The object is 5 pixel into the wall.
// The object has hit the wall some time during the last frame
// We need to adjust the position as the ball may have been
// traveling away from the wall for some time during the last frame.
var dist = (px+wx)-wallX; // get the distance into the wall
px -= dist*2; // the object hit the wall at position 95 and has been
// traveling away since then so it is easy to just
// subtract 2 times the distance the ball entered the wall
// the above two lines can be done in one
// px -= ((px+wx)-wallX)*2;
}
Why it matters
Below is a simulation of a ball bouncing inside the canvas.
To illustrate that the ball is moving between frames it has been motion blurred to show its motion between frames. Please note this is not the perfect solution as the bounce is assumed to occur while the ball is in linear motion while infact it is in freefall and under constant acceleration. But it still conserves energy.
In the correct test the height the ball bounces back to, stays around the same over time. No energy is lost or gained.
Right click to turn off the inter frame adjustment and you will notice that the ball begins to decrease its height each frame. This is because at each collision the ball loses a little energy because it motion during the previous frame is not taken into account when positioning it after the collision test. It will settle down to a constant rate when the collision occurres at precisely the frame time. When that will be is very hard to determine in advance.
Left click to slow the simulation frame rate, left click again to return to normal.
The code below is not really part of the answer, it is there to demonstrate the effect of not correctly adjusting the position during collision test on the overall accuracy of the simulation.
// helper functions. NOT part of the answer
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var mouseButton = 0;
canvas.addEventListener('mousedown',function(event){mouseButton = event.which;});
canvas.addEventListener('mouseup' ,function(){mouseButton = 0;});
canvas.addEventListener("contextmenu", function(e){ e.preventDefault();}, false);
var currentSurface = ctx;
var createImage = function (w, h) {// create an canvas image of size w,h and attach context 2d
var image = document.createElement("canvas");
image.width = w;
image.height = h !== undefined?h:w;
currentSurface = image.ctx = image.getContext("2d");
return image;
}
var setColour = function (fillC, strokeC, lineW) {
currentSurface.fillStyle = fillC !== undefined ? fillC : currentSurface.fillStyle;
currentSurface.strokeStyle = strokeC !== undefined ? strokeC : currentSurface.strokeStyle;
currentSurface.lineWidth = lineW !== undefined ? lineW : currentSurface.lineWidth;
}
var circle = function(x,y,r,how){
currentSurface.beginPath();
currentSurface.arc(x,y,r,0,Math.PI*2);
how = how.toLowerCase().replace(/[os]/g,"l"); // how to draw
switch(how){
case "f": // fill
currentSurface.fill();
break;
case "l":
currentSurface.stroke();
break;
case "lf":
currentSurface.stroke();
currentSurface.fill();
break;
case "fl":
currentSurface.fill();
currentSurface.stroke();
break;
}
}
function createGradImage(size,col1,col2){
var image = createImage(size);
var g = currentSurface.createLinearGradient(0,0,0,currentSurface.canvas.height);
g.addColorStop(0,col1);
g.addColorStop(1,col2);
currentSurface.fillStyle = g;
currentSurface.fillRect(0,0,currentSurface.canvas.width,currentSurface.canvas.height);
return image;
}
function createColouredBall (ballR,col) {
var ball = createImage(ballR*2);
var unit = ballR/100;
setColour("black");
circle(ballR,ballR,ballR,"f");
setColour("hsl("+col+",100%,30%)");
circle(ballR-unit*3,ballR-unit*3,ballR-unit*7,"f");
setColour("hsl("+col+",100%,50%)");
circle(ballR-unit*10,ballR-unit*10,ballR-unit*16,"f");
setColour("White");
circle(ballR-unit*50,ballR-unit*50,unit*16,"f");
return ball;
}
//===================================
// _
// /_\ _ _ ____ __ _____ _ _
// / _ \| ' \(_-< V V / -_) '_|
// /_/ \_\_||_/__/\_/\_/\___|_|
//
// ==================================
// Answer code
// lazy coder variables
var w = canvas.width;
var h = canvas.height;
// ball is simulated 5cm
var pixSize = 0.24; // in millimeters for simulation
// Gravity is 9.8 ms^2 so convert to pixels per frame squared
// Assuming constant 60 frames per second. ()
var gravity = 9800*pixSize/60;
gravity *= 0.101; // because Earth's gravity is stupidly large let's move to Pluto
// ball 5cm
var ballR = (25/pixSize)/2; // radius is 2.5cm for 5cm diamiter ball
var ballX = w/2; // get center of canvas
var ballY = ballR+3; // start at the top
var ballDX = (Math.random()-0.5)*15; // start with random x speed
ballDX += ballDX < 0 ? -5 : 5; // make sure it's not too slow
var ballDY = 0; // star with no downward speed;
var ballLastX = ballX;
var ballLastY = ballY;
//create an image of the Ball
var ball = createColouredBall(ballR,Math.floor(Math.random()*360)); // create an image of ball
// create a background. Image is small as it does not have much detail in it
var background = createGradImage(16,"#5af","#08C");
// time to run for
var runFor = 10*60; // ten secons yimes 60 frames per second
// draws the ball motion blured. This introduces extra complexity
var drawMotionBlur = function(image,px,py,dx,dy,steps){
var i,sx,sy;
sx = dx / steps;
sy = dy / steps;
px -= dx; // move back to start position
py -= dy;
ctx.globalAlpha = 1/(steps*0.8); // set alpha to slightly higher for each step
for(i = 0; i < steps; i+= 1){
ctx.drawImage(image,px+i*sx,py+i*sy);
}
ctx.globalAlpha = 1; // reset alpha
}
// style for text
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
ctx.textAlign = "center";
ctx.lineJoin = "round"; // stop some letters getting ears.
ctx.lineWidth = 3;
ctx.textBaseline = "bottom";
var textCenterX = w/2;
var maxHeight = Infinity;
var lastMaxHeight = ballY;
var slowMotion = false; // slow motion flag
var frameTravel = true; // use frame travel in collision test
var update = function(){
var blurSteps = 10; // motion blur ball render steps
const bSteps = 10;
if(mouseButton === 1){
slowMotion = ! slowMotion;
mouseButton = 0;
}
if(mouseButton === 3){
frameTravel = ! frameTravel;
ballX = w/2; // get center of canvas
ballY = ballR+3; // start at the top
ballDY = 0; // start at 0 y speed
mouseButton = 0;
}
// clear the canvas with background canvas image
ctx.drawImage(background,0,0,w,h);
ballDY += gravity; // accelrate due to grav
// add deltas to ball position
ballX += ballDX;
ballY += ballDY;
// test for collison on left and right walls. Need to
// ajust for motion blur
if (ballX < ballR) {
ballDX = -ballDX; // refect delta x
if (frameTravel) { // if using frame travel time
// blur the outward traveling ball only for the time it has been traveling away
blurSteps = Math.ceil(10 * ((ballX - ballR) / -ballDX));
// get position it should have traveled since
ballX -= (ballX - ballR) * 2;
}else{
ballX = ballR; // move ball to touching wall
blurSteps = 1; // there is no outward motion
}
} else
if (ballX > w - ballR) {
ballDX = -ballDX;
if (frameTravel) { // if using frame travel time
// blur the outward traveling ball only for the time it has been traveling away
blurSteps = Math.ceil(10 * ((ballX - (w - ballR)) / -ballDX));
ballX -= (ballX - (w - ballR)) * 2;
}else{
ballX = w - ballR; // move ball to touching wall
blurSteps = 1; // there is no outward motion
}
}
if (ballY > h - ballR) {
ballDY = -ballDY;
// to show max height
lastMaxHeight = maxHeight;
maxHeight = Infinity;
if (frameTravel) { // if using frame travel time
// blur the outward traveling ball only for the time it has been traveling away
blurSteps = Math.ceil(10 * ((ballY - (h - ballR)) / -ballDY));
ballY -= (ballY - (h - ballR)) * 2;
}else{
ballY = h - ballR; // move ball to touching wall
blurSteps = 1; // there is no outward motion
}
}
// draw the ball motion blured
drawMotionBlur(
ball, // image to draw
ballX - ballR, // offset radius
ballY - ballR,
ballDX * (blurSteps / bSteps), // speed and adjust for bounced
ballDY * (blurSteps / bSteps),
blurSteps // number of blurs
);
// show max height. Yes it is min but everything is upside down.
maxHeight = Math.min(maxHeight,ballY);
lastMaxHeight = Math.min(ballY,lastMaxHeight);
// show max height
ctx.font = "12px arial black";
ctx.beginPath();
ctx.moveTo(0,lastMaxHeight - ballR);
ctx.lineTo(w,lastMaxHeight - ballR);
ctx.stroke();
ctx.fillText("Max height.",40,lastMaxHeight - ballR + 6);
var str = ""; // display status string
if(slowMotion){ // show left click help
str += "10fps."
ctx.fillText("click for 60fps.",textCenterX,43);
}else{
str += "60fps."
ctx.fillText("click for 10fps.",textCenterX,43);
}
if(frameTravel){ // show mode and right click help
str += " Mid frame collision.";
ctx.fillText("Right click for Simple collision",textCenterX,55);
}else{
str += " Simple collision.";
ctx.fillText("Right click for mid frame collision",textCenterX,55);
}
// display help text
ctx.font = "18px arial black";
ctx.strokeText(str,textCenterX,30);
ctx.fillText(str,textCenterX,28);
if(slowMotion){
setTimeout(update,100); // show in slow motion
}else{
requestAnimationFrame(update); // request next frame (1/60) seconds from now
}
// all done
}
update(); // to start the ball rolling
.canC { width:500px; height:500px;}
<canvas class="canC" id="canV" width=500 height=500></canvas>
For a wall running along the X axis at the bottom (Y = 0) of a 650 x 650 field, we would want:
if (player.y <= 20) {
player.y = 20;
}
For a wall running along the Y axis at the left side (X = 0) of a 650 x 650 field, we would want:
if (player.x <= 20) {
player.x = 20;
}
For a wall running along the Y axis at the right side (X = 650) of a 650 x 650 field, we would want:
if (player.x >= 630) {
player.x = 630;
}
For a wall running along the X axis at the top (Y = 650) of a 650 x 650 field, we would want:
if (player.y >= 630) {
player.y = 630;
}
This code is similar to the code I use, if we attach horizontal (h) and vertical (v) velocity attributes to the player object we can multiply them by negative one to get them to bounce off of the wall if the player is going to go beyond the bounds. Or if you want it to stop, set them equal to zero at the wall.
//player.x+player.h gives us the future position of the player
if (player.x+player.h>630||player.x+player.h<0)
{
player.h*=-1;//bounce
//stop player.h=0;
}
if (player.y+player.v>500||player.y+player.v<0)
{
player.v*=-1;
//stop player.v=0;
}
//new player coordinates
player.x+=player.h;
player.y+=player.v;
Hope this helps.
In javascript, is there a way I can create a variable and a function that "simulates" smooth mouse movement? i.e., say the function simulates a user starts from lower left corner of the browser window, and then moves mouse in a random direction slowly...
The function would return x and y value of the next position the mouse would move each time it is called (would probably use something like setInterval to keep calling it to get the next mouse position). Movement should be restricted to the width and height of the screen, assuming the mouse never going off of it.
What I don't want is the mouse to be skipping super fast all over the place. I like smooth movements/positions being returned.
A "realistic mouse movement" doesn't mean anything without context :
Every mouse user have different behaviors with this device, and they won't even do the same gestures given what they have on their screen.
If you take an FPS game, the movements will in majority be in a small vertical range, along the whole horizontal screen.
Here is a "drip painting" I made by recording my mouse movements while playing some FPS game.
If we take the google home page however, I don't even use the mouse. The input is already focused, and I just use my keyboard.
On some infinite scrolling websites, my mouse can stay at the same position for dozens of minutes and just go to a link at some point.
I think that to get the more realistic mouse movements possible, you would have to record all your users' gestures, and repro them.
Also, a good strategy could be to get the coordinates of the elements that will attract user's cursor the more likely (like the "close" link under SO's question) and make movements go to those elements' coordinates.
Anyway, here I made a snippet which uses Math.random() and requestAnimationFrame() in order to make an object move smoothly, with some times of pausing, and variable speeds.
// Canvas is here only to show the output of function
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
var maxX = canvas.width = window.innerWidth;
var maxY = canvas.height = window.innerHeight;
window.onresize = function(){
maxX = canvas.width = window.innerWidth;
maxY = canvas.height = window.innerHeight;
}
gc.onclick = function(){
var coords = mouse.getCoords();
out.innerHTML = 'x : '+coords.x+'<br>y : '+coords.y;
}
var Mouse = function() {
var that = {},
size = 15,
border = size / 2,
maxSpeed = 50, // pixels per frame
maxTimePause = 5000; // ms
that.draw = function() {
if (that.paused)
return;
that.update();
// just for the example
ctx.clearRect(0, 0, canvas.width, canvas.height);
if(show.checked){
ctx.drawImage(that.img, that.x - border, that.y - border, size, size)
}
// use requestAnimationFrame for smooth update
requestAnimationFrame(that.draw);
}
that.update = function() {
// take a random position, in the same direction
that.x += Math.random() * that.speedX;
that.y += Math.random() * that.speedY;
// if we're out of bounds or the interval has passed
if (that.x <= border || that.x >= maxX - border || that.y <= 0 || that.y >= maxY - border || ++that.i > that.interval)
that.reset();
}
that.reset = function() {
that.i = 0; // reset the counter
that.interval = Math.random() * 50; // reset the interval
that.speedX = (Math.random() * (maxSpeed)) - (maxSpeed / 2); // reset the horizontal direction
that.speedY = (Math.random() * (maxSpeed)) - (maxSpeed / 2); // reset the vertical direction
// we're in one of the corner, and random returned farther out of bounds
if (that.x <= border && that.speedX < 0 || that.x >= maxX - border && that.speedX > 0)
// change the direction
that.speedX *= -1;
if (that.y <= border && that.speedY < 0 || that.y >= maxY - border && that.speedY > 0)
that.speedY *= -1;
// check if the interval was complete
if (that.x > border && that.x < maxX - border && that.y > border && that.y < maxY - border) {
if (Math.random() > .5) {
// set a pause and remove it after some time
that.paused = true;
setTimeout(function() {
that.paused = false;
that.draw();
}, (Math.random() * maxTimePause));
}
}
}
that.init = function() {
that.x = 0;
that.y = 0;
that.img = new Image();
that.img.src ="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAABJUlEQVRIic2WXbHEIAyFI6ESKgEJkVIJlYCTSqiESIiESqiEb19gL9Od3f5R5mbmPPHwBTgnIPJfChiAGbCkCQgtG7BpmgAWIALaDDyOI2bGuq40BasqIoKZATgwNAWHEEjHbkBsBhYRVJUYIwBNwVlFaVOwiDDPMylmQ1OwquY7d0CBrglYkuEeidoeOKt61I6Cq0ftKFhqR+0MOKuo2BQsInnndvnOr4JvR+0qWO5G7Q44K0XtOXDf96jqh9z9WXAy1FJ8l0qd+zbtvU7lWs7wIzkuh8SvpqqDi3zGndPQauDkzvdESm8xZvbh4mVZ7k8ud/+aR0C3YPk7mVvgkCZPVrdZV3dHVem6bju1roMPNmbAmq8kG+/ynD7ZwNsAVVz9dL0AhBrZq7F+CSQAAAAASUVORK5CYII=";
that.reset();
}
that.getCoords = function(){
return {x: that.x, y:that.y};
}
that.init()
return that;
}
var mouse = new Mouse()
mouse.draw();
html,body {margin: 0}
canvas {position: absolute; top:0; left:0;z-index:-1}
#out{font-size: 0.8em}
<label for="show">Display cursor</label><input name="show" type="checkbox" id="show" checked="true"/><br>
<button id="gc">get cursor Coords</button>
<p id="out"></p>
Last I heard the browser's mouse position cannot be altered with JavaScript, so the question really has no answer "as is". The mouse position can be locked though. I'm not certain whether it would be possible to implement a custom cursor that allows setting the position. This would include hiding and perhaps locking the stock cursor.
Having something smoothly follow the cursor is quite straight forward. You may be able to reverse this process to achieve what you need. Here's a code snippet which simply calculates the distance between the cursor and a div every frame and then moves the div 10% of that distance towards the cursor:
http://jsfiddle.net/hpp0qb0d/
var p = document.getElementById('nextmove')
var lastX,lastY,cursorX,cursorY;
window.addEventListener('mousemove', function(e){
cursorX = e.pageX;
cursorY = e.pageY;
})
setInterval(function(){
var newX = p.offsetLeft + (cursorX - lastX)/10
var newY = p.offsetTop + (cursorY - lastY)/10
p.style.left = newX+'px'
p.style.top = newY+'px'
lastX = p.offsetLeft
lastY = p.offsetTop
},20)
I'm pretty new to reactive programming (and RxJS) and all these operators are heavy to understand.
Anyway, I've successfully written this function that handles scrolling of the document while dragging something. I now wonder if this can be simplified.
Basically, onMouseDown I need to check the position of the mouse every 10ms and I need the updated clientY when the mouse moved, thats why I setup an Rx.Oberservable.interval(10) which I combine with the mouseMove observer. This will scroll the page, whether you move your mouse or not (as intended).
Here's the code:
handleWindowScrollOnDrag() {
var dragTarget = this.getDOMNode()
var scrollTarget = document.body
var wHeight = window.innerHeight
var maxScroll = document.documentElement.scrollHeight - wHeight
// Get the three major events
var mouseup = Rx.Observable.fromEvent(document, 'mouseup');
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(function (md) {
var y = scrollTarget.scrollTop
var multiplier = 1
// Scroll every 10ms until mouseup when we can
var intervalSource = Rx.Observable.interval(10).takeUntil(mouseup);
// Get actual clientY until mouseup
var movement = mousemove.map(function (mm) {
return {
y: mm.clientY
};
}).takeUntil(mouseup);
return Rx.Observable
.combineLatest(movement, intervalSource, function (s1) {
multiplier = 1
if (s1.y < 100 && y >= 0) {
if (s1.y < 75) multiplier = 3;
if (s1.y < 50) multiplier = 5;
if (s1.y < 25) multiplier = 10;
if (s1.y < 15) multiplier = 20;
y -= (1 * multiplier)
}
if (s1.y > wHeight - 100 && y <= (maxScroll)) {
if (s1.y > wHeight - 75) multiplier = 3;
if (s1.y > wHeight - 50) multiplier = 5;
if (s1.y > wHeight - 25) multiplier = 10;
if (s1.y > wHeight - 15) multiplier = 20;
y += (1 * multiplier)
}
return {
y: y
};
});
});
// Update position
this.subscription = mousedrag.subscribe(function (pos) {
document.body.scrollTop = pos.y
});
},
You can remove the extra takeUntils you only need one on the movement stream, since the combineLatest operator will clean up and dispose of all the underlying subscriptions when it completes.
Next, you can remove some extra state by using .scan to manage accumulated state instead of closure variables.
You can also simplify the event body by simply passing the mouse move's y value instead of incurring the overhead of an object allocation in both the map and the combineLatest operators.
Finally, I would change to use .withLatestFrom instead of combineLatest. Since you are already essentially polling at 100 fps with interval combineLatest would emit even faster if the mouse was moving at the same time.
As a side note, while I am not terribly familiar with how DOM scrolling works (and I don't actually know how well your code works in the wild), spamming the page's scroll value faster than the actual render rate of the page seems like overkill. It might be better to lower the interval rate and use something like jQuery.animate to smooth the scrolling in between.
;tldr
handleWindowScrollOnDrag() {
var dragTarget = this.getDOMNode()
var scrollTarget = document.body;
var wHeight = window.innerHeight;
var maxScroll = document.documentElement.scrollHeight - wHeight;
// Get the three major events
var mouseup = Rx.Observable.fromEvent(document, 'mouseup');
var mousemove = Rx.Observable.fromEvent(document, 'mousemove');
var mousedown = Rx.Observable.fromEvent(dragTarget, 'mousedown');
var mousedrag = mousedown.flatMap(function (md) {
// Scroll every 10ms until mouseup when we can
var intervalSource = Rx.Observable.interval(10, Rx.Scheduler.requestAnimationFrame);
return intervalSource
.takeUntil(mouseup)
.withLatestFrom(mousemove, function (s1, s2) {
return s2.clientY;
})
.scan(scrollTarget.scrollTop, function(y, delta) {
var multiplier = 1;
if (delta < 100 && y >= 0) {
if (delta < 75) multiplier = 3;
if (delta < 50) multiplier = 5;
if (delta < 25) multiplier = 10;
if (delta < 15) multiplier = 20;
y -= (1 * multiplier);
}
if (delta > wHeight - 100 && y <= (maxScroll)) {
if (delta > wHeight - 75) multiplier = 3;
if (delta > wHeight - 50) multiplier = 5;
if (delta > wHeight - 25) multiplier = 10;
if (delta > wHeight - 15) multiplier = 20;
y += (1 * multiplier);
}
return y;
});
});
// Update position
this.subscription = mousedrag.subscribe(function (pos) {
document.body.scrollTop = pos;
});
},
Edit
A mistake in the original actually allows one to simplify the code even further. You can remove the movement stream all together (and with it an extra call to map) by passing mousemove to withLatestFrom and using that result selector to grab the clientY
Additionally I added where you would likely want to add a scheduler if you were trying to synchronize interval with the render loop of the page, though I would still say you probably want to use 15ms (60fps) rather than 10ms, it again comes down to how the scroll rendering is done.
If setting the scroll position will only be updated at the end of each render loop then it will work fine. However, if it greedily recomputes after every set then it would be better to have an intermediary like React to write to Virtual DOM first rather than flushing all changes directly to the DOM.
I am working on a function that will delete a div when you swipe it left...
Here is a div that I would like to delete when it is swiped:
<div class="listItem">Stuff here</div>
I have javascript to determine where on the page I have swiped by using the touchstart and touchend event listeners.
I have created a function called to delete the item:
function swipeLeft(){
//delete item
}
I am determining where I swipe like this:
var touchobj = e.changedTouches[0];
dist = touchobj.pageX - startX
Now is it possible to delete the div with this information?
Yeah, you just calculate the deltas between the div's sizes and swipe distance.
Here is a good example for a generic swipe pattern detection:
http://www.javascriptkit.com/javatutors/touchevents2.shtml
Code:
function swipedetect(el, callback){
var touchsurface = el,
swipedir,
startX,
startY,
distX,
distY,
threshold = 150, //required min distance traveled to be considered swipe
restraint = 100, // maximum distance allowed at the same time in perpendicular direction
allowedTime = 300, // maximum time allowed to travel that distance
elapsedTime,
startTime,
handleswipe = callback || function(swipedir){}
touchsurface.addEventListener('touchstart', function(e){
var touchobj = e.changedTouches[0]
swipedir = 'none'
dist = 0
startX = touchobj.pageX
startY = touchobj.pageY
startTime = new Date().getTime() // record time when finger first makes contact with surface
e.preventDefault()
}, false)
touchsurface.addEventListener('touchmove', function(e){
e.preventDefault() // prevent scrolling when inside DIV
}, false)
touchsurface.addEventListener('touchend', function(e){
var touchobj = e.changedTouches[0]
distX = touchobj.pageX - startX // get horizontal dist traveled by finger while in contact with surface
distY = touchobj.pageY - startY // get vertical dist traveled by finger while in contact with surface
elapsedTime = new Date().getTime() - startTime // get time elapsed
if (elapsedTime <= allowedTime){ // first condition for awipe met
if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met
swipedir = (distX < 0)? 'left' : 'right' // if dist traveled is negative, it indicates left swipe
}
else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
swipedir = (distY < 0)? 'up' : 'down' // if dist traveled is negative, it indicates up swipe
}
}
handleswipe(swipedir)
e.preventDefault()
}, false)
}
//USAGE:
/*
var el = document.getElementById('someel')
swipedetect(el, function(swipedir){
swipedir contains either "none", "left", "right", "top", or "down"
if (swipedir =='left')
alert('You just swiped left!')
})
*/
All you do is adjust the threshold if you need to. Once detected you just remove the div from the DOM.