I have a webgame I made in pure javascript using processing.js
It works fine on desktop but I want it to work on mobile.
It's on this website: https://www.ricarddokifuri.tk/mlg/
I found a function to detect swipes
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)
}
I can activate and detect which direction was swiped like this
swipedetect(el, function(swipedir){
if (swipedir == 'left')
alert('You just swiped left!')
});
My function to move the player is like this
var movePlayer = function(){
if(keyCode === LEFT){
x -= 3.5*scaless;
}
if(keyCode === RIGHT){
x += 3.5*scaless;
}
if(keyCode === UP){
y -= 3.5*scaless;
}
if(keyCode === DOWN){
y += 3.5*scaless;
}
};
if(keyPressed){
movePlayer();
}
How could I make the function that detects a swipe, simulate a keypress?
If you want to reuse your movePlayer function both for the keyboard and for swipes, then I would probably make it accept an argument and then call it either from a keypress event handler with the event keycode either from the swipe handler with an appropriate argument.
So basically, according to this: https://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
The keycodes for up,right,down,left are:
UP: 38
RIGHT: 39
DOWN: 40
LEFT: 37
so you could modify your move player like so:
var movePlayer = function(keyCode){
if(keyCode === LEFT){
x -= 3.5*scaless;
}
if(keyCode === RIGHT){
x += 3.5*scaless;
}
if(keyCode === UP){
y -= 3.5*scaless;
}
if(keyCode === DOWN){
y += 3.5*scaless;
}
};
From your code snippet, I am assuming that LEFT, RIGHT, UP and DOWN are constant values, somewhere else in your program, so then I would modify my swipe detect function like:
swipedetect(el, function(swipedir){
if (swipedir == 'left'){
movePlayer(LEFT);
}else if(swipedir == 'right'){
movePlayer(RIGHT);
}else if(swipedir == 'up'){
movePlayer(UP);
}else if(swipedir == 'down'){
movePlayer(DOWN);
}
//you can have the default case in an else here
});
Then, you would need to modify the keypress code that calls movePlayer when the user uses the keyboard to pass the code of the key that was pressed i.e. by doing movePlayer(event.keyCode) in your keypress event handler.
Related
I want the movement to be smooth upon pressing a key, the object does an initial movement and then starts moving completely when holding down the button.
//movement speed
var xSpeed = 0.5;
var zSpeed = 0.5;
document.addEventListener("keydown", onDocumentKeyDown, false);
function onDocumentKeyDown(event) {
var keyCode = event.which;
if (keyCode == 87) { //W KEY
car.position.z -= zSpeed;
} else if (keyCode == 83) { //S KEY
car.position.z += zSpeed;
} else if (keyCode == 65) { //A KEY
car.position.x -= xSpeed;
} else if (keyCode == 68) { //D KEY
car.position.x += xSpeed;
} else if (keyCode == 32) {
car.position.set(0, 0, 0);
}
};
Trying to get a GLTF imported model to be controlled on a plane with keyboard controls smoothly.
Tried wrapping the program in a loop and made the state of the keyboard available inside the scope of that loop but couldn't get it working
You could try an approach where the velocity is modified upon keyup/keydown, and the loop just modifies the car's position based on the velocity.
// initial movement speed
var xSpeed = 0;
var zSpeed = 0;
document.addEventListener("keydown", onDocumentKeyDown, false);
document.addEventListener("keydown", onDocumentKeyUp, false);
function onDocumentKeyDown(event) {
var keyCode = event.which;
if (keyCode == 87) { //W KEY
zSpeed = -0.5;
} ... // do so for the rest of the keys
};
function onDocumentKeyUp(event) {
var keyCode = event.which;
if (keyCode == 87) { //W KEY
zSpeed = 0;
} ... // do so for the rest of the keys
};
function loop() {
car.position.z += zSpeed;
car.position.x += xSpeed;
window.requestAnimationFrame(loop);
}
If you are interested in more eased movement, e.g. the car doesn't just start moving at constant velocity but ramps up and slows down, consider having the keyup/keydown modify an acceleration factor, and the loop modifies the velocity.
This is a good in-depth article by Daniel Shiffman about attempting to represent the real world with code:
https://natureofcode.com/book/chapter-1-vectors/
I'm trying to control video playback depending on where my mouse is on screen.
I've split the width of the window into 3 areas - left, centre and right. This works fine so far.
When the mouse is in the 'left' I want to specify to jump to a certain time, same for centre and right.
The issue I'm having is that every time the mouse moves the video playback restarts, I want it to change only when it changes from 'left' to 'right' or 'centre'. I've made a current_pos and new_pos variable but can't figure out how to update them properly.
Many thanks!
PS have left out the video code for now, just trying to get the position working.
var viewport_width = document.documentElement.clientWidth;
var viewport_height = document.documentElement.clientHeight;
var current_pos;
var new_pos;
function getMousePos(e) {
return {x:e.clientX,y:e.clientY};
}
document.onmousemove=function(e) {
var mousecoords = getMousePos(e);
//left
if (mousecoords.x < viewport_width/3){
current_pos = 'left';
//centre
}else if (mousecoords.x > viewport_width/3 && mousecoords.x < viewport_width*.66){
current_pos = 'centre';
//right
}else {
current_pos = 'right';
}
console.log(current_pos);
};
You need to check whether current_pos has been changed every time you attempt to change it.
var viewport_width = document.documentElement.clientWidth;
var viewport_height = document.documentElement.clientHeight;
var current_pos;
function getMousePos(e) {
return {
x: e.clientX,
y: e.clientY
};
}
document.onmousemove = function(e) {
var mousecoords = getMousePos(e);
//left
if (mousecoords.x < viewport_width / 3) {
if(current_pos != 'left') {
current_pos = 'left';
// Play your video from the desired point
}
//centre
} else if (mousecoords.x > viewport_width / 3 && mousecoords.x < viewport_width * .66) {
if(current_pos != 'centre') {
current_pos = 'centre';
// Play your video from the desired point
}
//right
} else {
if(current_pos != 'right') {
current_pos = 'right';
// Play your video from the desired point
}
}
console.log(current_pos);
};
I am developing a cross-platform mobile application using Cordova. I have an HTML page with a table. I have a button add row when clicked adds a new row to my table. I need an iOS-like swipe action to have a delete action. I used touchstart event to swipe a static row in my table. That works fine but not with dynamically created table rows. How to do this swipe action?
This is the code I have so far:
HTML
<div class="fortable">
<table id="cashreg">
<tr>
<td class="tablecellbackground"><div class="del" id="d1" ><p>Delete</p></div><div id="cells" class="cells"><div class="denom"><p>Start Time</p></div><div class="cnt"><p>Select Item</p></div><div class="tots"><p>Employee</p></div><div class="tots1"><p>Price</p></div></div></td>
</tr>
</table>
<input type="button" class="addmore">
</div>
JavaScript
For adding a row to the table:
$(".addmore").click(function(){
var rows = $("#cashreg tr").length+1;
$("#cashreg").append('<tr><td class="tablecellbackground"><div class="cells"><div class="denom"><p>Start Time</p></div><div class="cnt"><p>Select Item</p></div><div class="tots"><p>Employee</p></div><div class="tots1"><p>Price</p></div><div class="del"><p>Delete</p></div></div></td></tr>');
});
For iOS-like swipe:
window.addEventListener('load', function(){
var el = document.getElementsByClassName('cells')[0];
ontouch(el, function(evt, dir, phase, swipetype, distance){
var touchreport = ''
touchreport += '<b>Dir:</b> ' + dir + '<br />'
touchreport += '<b>Phase:</b> ' + phase + '<br />'
touchreport += '<b>Swipe Type:</b> ' + swipetype + '<br />'
touchreport += '<b>Distance:</b> ' + distance + '<br />'
if(dir=="left"){
left=parseInt(($(".cells").css("top")).replace ( /[^\d.]/g, '' ))-distance;
$(".cells").css("left","-"+left+"px")
}
if(dir=="right"){
if($(".cells").css("left")== "-166px"){
//left=parseInt(($(".cells").css("top")).replace ( /[^\d.]/g, '' ))-distance;
$(".cells").css("left","0px")
}
}
if(dir=="none"){
// document.getElementById("hi").value=el.pageX;
}
})
}, false)
function ontouch(el, callback){
var touchsurface = el,
dir,
swipeType,
startX,
startY,
distX,
distY,
threshold = 50, //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
elapsedTime,
startTime,
handletouch = callback || function(evt, dir, phase, swipetype, distance){}
touchsurface.addEventListener('touchstart', function(e){
var touchobj = e.changedTouches[0]
dir = 'none'
swipeType = 'none'
dist = 0
startX = touchobj.pageX
startY = touchobj.pageY
startTime = new Date().getTime() // record time when finger first makes contact with surface
handletouch(e, 'none', 'start', swipeType, 0) // fire callback function with params dir="none", phase="start", swipetype="none" etc
e.preventDefault()
}, false)
touchsurface.addEventListener('touchmove', 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
if (Math.abs(distX) > Math.abs(distY)){ // if distance traveled horizontally is greater than vertically, consider this a horizontal movement
dir = (distX < 0)? 'left' : 'right'
handletouch(e, dir, 'move', swipeType, distX) // fire callback function with params dir="left|right", phase="move", swipetype="none" etc
}
else{ // else consider this a vertical movement
dir = (distY < 0)? 'up' : 'down'
handletouch(e, dir, 'move', swipeType, distY) // fire callback function with params dir="up|down", phase="move", swipetype="none" etc
}
e.preventDefault()
// prevent scrolling when inside DIV
}, false)
touchsurface.addEventListener('touchend', function(e){
var touchobj = e.changedTouches[0]
distX = touchobj.pageX - startX
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
swipeType = dir // set swipeType to either "left" or "right"
}
else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
swipeType = dir // set swipeType to either "top" or "down"
}
}
// Fire callback function with params dir="left|right|up|down", phase="end", swipetype=dir etc:
handletouch(e, dir, 'end', swipeType, (dir =='left' || dir =='right')? distX : distY)
e.preventDefault()
if(dir=="left"){
if(distX>-100){
$(".cells").css("left","0px")
}
else if(distX<-50 && distX>-100){
$(".cells").css("left","-166px")
}
else{
$(".cells").css("left","-166px")
}
}
}, false)
}
How to make this swipe apply to a newly added table row?
You should use event delegation on() to deal with new elements added dynamically by js :
$("body").on('touchstart click','#cashreg tr', function(e){
})
Hope this helps.
The problem is that you bind your cells elements with the event ontouch in the load function. It means that only the elements present on load will be bound. You need to register the ontouch event on your newly created row element as well.
With your example, you could try to use the ontouch html attribute directly, i.e.:
<div class="cells" ontouchstart="...">
or simply bind the touch event to your body and check for the cells class in the handler:
var el = document.getElementsByTagName('body')[0];
ontouch(el, function(evt, dir, phase, swipetype, distance){
if($(evt.target).hasClass("cells")){
// your code
}
});
How can I make a repeated scrollBy call smoother like when animating with jQuery's animate scrollTop?
Currently it is jumpy, the page jumps to and from the different scroll positions. How can I make it smoother?
Here is the scrollBy code:
window.scrollBy(0, -10*(scrollCount ? scrollCount<0 ? -1 : 1 : 0)) , 600*x); })(i);
And here is the for loop that contains it:
for(var i = 0; i < Math.abs(scrollCount); i++){
(function(x){
setTimeout(
window.scrollBy(0, -10*(scrollCount ? scrollCount<0 ? -1 : 1 : 0))
, 600*x); })(i);
}
}
Usage
First add this to your page.
scrollByAnimated = function(scrollY, duration){
var startTime = new Date().getTime();
var startY = window.scrollY;
var endY = startY + scrollY;
var currentY = startY;
var directionY = scrollY > 0 ? 'down' : 'up';
var animationComplete;
var count = 0;
var animationId;
if(duration === undefined){
duration = 250;//ms
}
//grab scroll events from the browser
var mousewheelevt=(/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel" //FF doesn't recognize mousewheel as of FF3.x
//stop the current animation if its still going on an input from the user
var cancelAnimation = function () {
if(animationId!==undefined){
window.cancelAnimationFrame(animationId)
animationId=undefined;
}
}
if (document.attachEvent) {
//if IE (and Opera depending on user setting)
document.attachEvent("on"+mousewheelevt, cancelAnimation)
} else if (document.addEventListener) {
//WC3 browsers
document.addEventListener(mousewheelevt, cancelAnimation, false)
}
var step = function (a,b,c) {
var now = new Date().getTime();
var completeness = (now - startTime) / duration;
window.scrollTo(0, Math.round(startY + completeness * scrollY));
currentY = window.scrollY;
if(directionY === 'up') {
if (currentY === 0){
animationComplete = true;
}else{
animationComplete = currentY<=endY;
}
}
if(directionY === 'down') {
/*limitY is cross browser, we want the largest of these values*/
var limitY = Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight );
if(currentY + window.innerHeight === limitY){
animationComplete = true;
}else{
animationComplete = currentY>=endY;
}
}
if(animationComplete===true){
/*Stop animation*/
return;
}else{
/*repeat animation*/
if(count > 500){
return;
}else{
count++;
animationId = window.requestAnimationFrame(step);
}
}
};
/*start animation*/
step();
};
Then use it;
scrollByAnimated(100, 250);// change in scroll Y, duration in ms
Explanation
Here's a more robust version than your original code suggested you needed. Additional features include stop scrolling at the top and bottom of the page, uses requestAnimationFrame().
It also only supports scrolling up and down because thats all I need at this time. You'd be a cool, cool person if you added left and right to it.
It has only been tested in Chrome, so your milage may vary.
This code leverages requestAnimationFrame(). First scrollByAnimated() sets variables, then runs step() which loops until the duration has been reached.
At each frame it calculates the animation's completeness as a number from 0 to 1. This is the difference between the startTime and now, divided by duration.
This completeness value is then multiplied by the requested scrollY. This gives us our amount to scroll for each frame. Then we add the startY position to achieve a value that we can use window.scrollTo() to set the frame's position, rather than increment the current position.
try to use animate like this
$('html,body').animate({scrollTop: currentoffset},400);
I have a carousel that is working just fine for all browsers and all devices except for ipad/iphone. When I swipe the carousel, it will use jqueries easing and bounce several times before stopping. The only way to make it behave, as it does in all other browsers, is to have an alert message pop up after swiping, then it works perfectly.
[code]
$("#CarouselWrap").bind("touchmove", function(event){
if(navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
whichWayMovingX[1] = event.originalEvent.touches[0].pageX;
whichWayMovingY[1] = event.originalEvent.touches[0].pageY;
}else{
whichWayMovingX[1] = event.originalEvent.changedTouches[0].pageX;
whichWayMovingY[1] = event.originalEvent.changedTouches[0].pageY;
}
if(whichWayMovingX[0] > whichWayMovingX[1]){
if(navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)){
alert("left");
moveLeft();
}else{
moveLeft();
}
}else{
if(navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)){
alert("right");
moveRight();
}else{
moveRight();
}
}
});
[/code]
The moveLeft and moveRight functions are used with the arrows on the left and right of the carousel, so I know that these work, but only for onclick events.
[code]
switch(amountToMove) {
case -1011:
$("#CarouselFeed").animate({marginLeft: amountToMove},{duration: 'slow', easing: 'easeOutBack', wipe:'true'});
[/code]
Why would this code work so well for onclick, but not for touchmove?
I have tried to combine the binds of touchstart, touchend and touchmove - nada
I have tried to use touchmove mousemove - diddlesquat
I have tried to use a setTimeout thinking that I had to wait for the last event - nothing
Please help, this is driving me nuts.
Found the solution via some code for addressing the default behavior of a swipe.
First, set up the listeners:
if(document.getElementById("CarouselFeed")){
$("#CarouselFeed").bind("touchstart", function(event){
touchStart(event,"CarouselFeed")
});
$("#CarouselFeed").bind("touchend", function(event){
touchEnd(event);
});
$("#CarouselFeed").bind("touchmove", function(event){
touchMove(event);
});
}
I found the following code on a web site, that thanked two other web sites for their code and just as that web site changed the code a little to suit their needs, I have done the same:
// TOUCH-EVENTS SINGLE-FINGER SWIPE-SENSING JAVASCRIPT
// Courtesy of PADILICIOUS.COM and MACOSXAUTOMATION.COM
// redefined a few things to make this applicable to our needs
// this script can be used with one or more page elements to perform actions based on them being swiped with a single finger
var triggerElementID = null; // this variable is used to identity the triggering element
var fingerCount = 0;
var startX = 0;
var startY = 0;
var curX = 0;
var curY = 0;
var deltaX = 0;
var deltaY = 0;
var horzDiff = 0;
var vertDiff = 0;
var minLength = 72; // the shortest distance the user may swipe
var swipeLength = 0;
var swipeAngle = null;
var swipeDirection = null;
// The 4 Touch Event Handlers
// NOTE: the touchStart handler should also receive the ID of the triggering element
// make sure its ID is passed in the event call placed in the element declaration, like:
//
var touchStart = function(event,passedName){
// disable the standard ability to select the touched object
// event.preventDefault();
// get the total number of fingers touching the screen
fingerCount = event.originalEvent.touches.length;
// since we're looking for a swipe (single finger) and not a gesture (multiple fingers),
// check that only one finger was used
if(fingerCount == 1){
// get the coordinates of the touch
startX = event.originalEvent.touches[0].pageX;
startY = event.originalEvent.touches[0].pageY;
// store the triggering element ID
triggerElementID = passedName;
}else{
// more than one finger touched so cancel
touchCancel(event);
}
}
var touchMove = function(event){
event.preventDefault();
if ( fingerCount == 1 ){
curX = event.originalEvent.touches[0].pageX;
curY = event.originalEvent.touches[0].pageY;
}else{
touchCancel(event);
}
}
var touchEnd = function(event){
// event.preventDefault();
// check to see if more than one finger was used and that there is an ending coordinate
if (fingerCount == 1 && curX != 0){
// use the Distance Formula to determine the length of the swipe
swipeLength = Math.round(Math.sqrt(Math.pow(curX - startX,2) + Math.pow(curY - startY,2)));
// if the user swiped more than the minimum length, perform the appropriate action
if(swipeLength >= minLength){
caluculateAngle();
determineSwipeDirection();
processingRoutine();
touchCancel(event); // reset the variables
}else{
touchCancel(event);
}
}else{
touchCancel(event);
}
}
var touchCancel = function(event){
// reset the variables back to default values
fingerCount = 0;
startX = 0;
startY = 0;
curX = 0;
curY = 0;
deltaX = 0;
deltaY = 0;
horzDiff = 0;
vertDiff = 0;
swipeLength = 0;
swipeAngle = null;
swipeDirection = null;
triggerElementID = null;
}
var caluculateAngle = function(){
var X = startX-curX;
deltaX = X;
var Y = curY-startY;
var Z = Math.round(Math.sqrt(Math.pow(X,2)+Math.pow(Y,2))); //the distance - rounded - in pixels
var r = Math.atan2(Y,X); //angle in radians (Cartesian system)
swipeAngle = Math.round(r*180/Math.PI); //angle in degrees
if (swipeAngle < 0) {swipeAngle = 360 - Math.abs(swipeAngle);}
}
var determineSwipeDirection = function(){
if( (swipeAngle <= 45) && (swipeAngle >= 0) ){
swipeDirection = 'left';
}else if( (swipeAngle <= 360) && (swipeAngle >= 315) ){
swipeDirection = 'left';
}else if( (swipeAngle >= 135) && (swipeAngle <= 225) ){
swipeDirection = 'right';
}else if( (swipeAngle > 45) && (swipeAngle < 135) ){
swipeDirection = 'down';
}else{
swipeDirection = 'up';
}
}
var processingRoutine = function(){
var swipedElement = document.getElementById(triggerElementID);
if( swipeDirection == 'left' ){
moveLeft();
}else if( swipeDirection == 'right' ){
moveRight();
}else if( (swipeDirection == 'up') || (swipeDirection == 'left') ){
moveLeft();
}else if( (swipeDirection == 'up') || (swipeDirection == 'right') ){
moveRight();
}else if( (swipeDirection == 'down') || (swipeDirection == 'left') ){
moveLeft();
}else if( (swipeDirection == 'down') || (swipeDirection == 'right') ){
moveRight();
}
}
One note, I have this swipe working on a carousel that has banners. In order for the links for the banners to work, you have to comment out the event.preventDefault() within the touchStart and touchEnd functions.
And that is all it takes