jQuery mobile multiple events like keyup/keydown - javascript

I am currently developing an html5 game for both mobile and non-mobile.
The way the UI works right now for mobile, is that when you hold down on the right side of the screen, the character moves right. Hold down the left end and the character goes left. Tap the middle of the screen to shoot and swipe up to jump. All of these work fine.
However, I would like to have it where while moving right, the user can also tap the screen to shoot, or swipeup to jump. For example, in the non-mobile version, while holding down the right arrow key, if you press the top arrow at the same time you will jump while moving right. The events dont block each other. However, with touch events it seems like you can only fire one at a time. If im holding the right side to run, and then tap, ill stop running. As a matter of fact, I wont shoot either, so it seems like the two events just cancel each other out.
Im not sure if this is a semantic error, or if its not possible, but any suggestions/help would be much appreciated. If it's not possible to do this with jQuery events, perhaps there is some inventive way I could accomplish this with JS or a framework?
Here is the code for the events, if it helps at all:
$(document).on('touchstart', '#game', function(e) {
//x coordinate of touch
var touchX = e.originalEvent.touches[0].pageX;
`enter code here` //what part of the canvas are we touching?
var leftSide = touchX <= .10 * width; //left 10% of width
var rightSide = touchX >= .90 * width; //right 10% of width
if (leftSide || rightSide) {
//if they are jumping or shooting they cant do anything but jump unless they are in the air
if ((player.currentAction !== 'jumping' || (player.currentAction === 'jumping' && (player.y === groundLocation || !noObstacleCollisions(player))))) {
var action;
if (leftSide) action = 'moving left';
else if (rightSide) action = 'moving right';
player.action(action);
player.lastAction = action;
//dont add duplicate actions, since keyup is constantly called when held down
if (!inArray(player.currentActions, action)) {
player.currentActions.push(action);
}
}
}
});
//when tap is released update current actions
$(document).on('touchend', '#game', function() {
//if we're moving left or right stop when we let go of the key
//also make sure we're either on the ground or on a platform when we do that
//we dont want to break midair trajectory
var movingLeftOrRight = player.vx !== 0;
if (movingLeftOrRight && (!noObstacleCollisions(player) || player.y === groundLocation)) {
player.action('standing'); //stop player
}
//remove action associated with key
if (player.vx > 0) removeFromArray(player.currentActions, 'moving right');
if (player.vx < 0) removeFromArray(player.currentActions, 'moving left');
if (movingLeftOrRight) player.lastAction = null;
});
//jump up when swiping up
$(document).on('swipeup', '#game', function() {
if ((player.currentAction !== 'jumping' || (player.currentAction === 'jumping' && (player.y === groundLocation || !noObstacleCollisions(player)))))
player.action('jumping');
});
//fire on middle screen tap (not touchstart to not conflict with jumping)
$(document).on('tap', '#game', function(e) {
var leftSide = e.pageX <= .10 * width; //left 10% of width
var rightSide = e.pageX >= .90 * width; //right 10% of width
if (!(leftSide || rightSide)) {
player.action('shooting');
}
});
Thank you!

Related

How to make jquery setinterval movement in mobile smoother?

Sorry for bad english.
Here's link to site.
My task is to create site with no scroll. When user clicks to right part of screen car starts to move forward. When it reaches middle of screen - car stops and fixed content-block starts to move in opossite direction. if user moves cursor to left side of screen (while holding mouse button clicked) car should move backward.
Desktop version works as expected. But mobile version is slow. (it's not exactly slow, it's not as smooth as desktop i guess)
What can i do fix this problem?
On touchstart event i get event.pageX value to check what part of screen user touched. (so i would know what direction car should move) and store this value in variable "mousePos". Then i call setInterval with movement function
On touchend event i clear interval to stop car from moving.
On touchmove i will rewrite "mousePos" var with new event.pageX value. For example: user clicked, car starts to move, if user moved cursor to left i will use this var to check direction and turn car back.
In mouseMove function i will check car position and decide what action should be done - either move car or move background and i'll check if it reached start of end points
events:
$(document).on('mousedown touchstart', '.mouse-click', function(event){
clicking = true;
mousePos = event.pageX;
if ( event.target.className == 'helper' ) {
showModal();
} else {
moveStuff = setInterval(mouseMove, 1);
}
});
$(document).on('mouseup touchend', function(){
clicking = false;
carLights.removeClass('blink');
clearInterval(moveStuff);
})
$(document).on('mousemove touchmove', '.mouse-click', function(event){
if(clicking == false) {
return;
} else {
mousePos = event.pageX;
}
});
function:
function mouseMove() {
let offset = parseInt( mainContent.css('left') );
let offsetCar = parseInt( car.css('left') );
if ( mousePos > middleScreen ) {
carLights.removeClass('blink');
if ( offset < - ( contentWidth ) ) {
return;
} else {
rotateWheelsForward();
if ( offsetCar < middleScreen ) {
car.css('left', (offsetCar + mouseSpeed) + 'px');
} else {
mainContent.css('left', (offset - mouseSpeed) + 'px');
}
}
} else if ( mousePos < middleScreen ) {
carLights.addClass('blink');
if ( offset > 0 ) {
carLights.removeClass('blink');
return;
} else {
rotateWheelsBackward();
if ( offsetCar > minCarLeft ) {
car.css('left', (offsetCar - mouseSpeed) + 'px');
} else {
mainContent.css('left', (offset + mouseSpeed) + 'px');
}
}
}
}
So how can i make movement smoother in mobile? (i use iphone 5s safari, but tested in iphone 6, still works bad)
What changes should i implement to this code?
I suggest to use transform rather than position eg: left, top cause thats really affect layer reflow-repaint. Use requestAnimationFrame (wisely) to perform smooth event animation like scroll, mouseup, or any other event.
Then use will-change: transform; to element which will "transformed" on the future. This will creates new layer and prepare for the element changes later.
In my case, relative position impact reflow or green flash on the rendering tool chrome. So I prefer use fixed/absolute position to prevent this.
Here's some great article for you to get Leaner, Meaner, Faster Animations with requestAnimationFrame and how to achieving 60 fps animations with css
Hope this help ;)

Prevent vertical scroll on swipe -vanilla JS

I wrote this code to add swipe function for an image slider. The slider is working correctly.
However when i perform a right or left swipe there is some vertical scrolling which is distracting and annoying.
I'm storing the reference to touchstart in the touch object.
And on touchend event, if vertical distance (lenY) is more than 50, i trigger preventDefault on the touchstart.
This isn't working.
Simplest option is to call preventDefault directly on touchStart. But the image slider occupies a large part of the mobile screen making scrolling down the page tricky.
I need to pass the lenY (vertical distance) to the touch start handler to prevent default action.
function triggerTouch() {
"use strict";
var tZone = document.getElementById('sl-m'),
touch = {},
startX = 0,
startY = 0,
endX = 0,
endY = 0;
if (tZone) {
tZone.addEventListener('touchstart', function (e) {
startX = e.changedTouches[0].screenX;
startY = e.changedTouches[0].screenY;
// store reference to touch event
touch.start = e;
}, false);
tZone.addEventListener('touchend', function (e) {
endX = e.changedTouches[0].screenX;
endY = e.changedTouches[0].screenY;
var lenX = Math.abs(endX - startX);
var lenY = Math.abs(endY - startY);
// check if user intended to scroll down
if (lenY < 50 && lenX > 50) {
touch.start.preventDefault();
e.preventDefault();
swipe(tZone, startX, endX);
}
}, false);
}
}
Since i haven't got an answer i am posting my own answer, hoping someone can provide the correct implementation.
I ended up using the css overflow property to temporarily disable vertical scroll.
This works perfectly though there is a small side effect. Once you swipe through the image slider, the scroll is disabled.
A swipe upwards is required to restore scroll to the page. Its not noticeable but i still want to figure the right way.
var touch = {};
window.onload = function () {
"use strict";
document.body.addEventListener("touchstart", touchHandler);
document.body.addEventListener("touchend", touchHandler);
};
function touchHandler(e) {
"use strict";
var el = e.target;
if (el.parentNode.id === "sl-m") {
if (e.type === "touchstart") {
touch.startX = e.changedTouches[0].screenX;
touch.startY = e.changedTouches[0].screenY;
} else {
touch.endX = e.changedTouches[0].screenX;
touch.endY = e.changedTouches[0].screenY;
touch.lenX = Math.abs(touch.endX - touch.startX);
touch.lenY = Math.abs(touch.endY - touch.startY);
if (touch.lenY < 20) {
// disable scroll
document.body.style.overflowY = "hidden";
// do swipe related stuff
swipe(el.parentNode);
} else {
// enable scroll if swipe was not intended
document.body.style.overflowY = "scroll";
}
}
} else {
// keep scroll enabled if touch is outside the image slider
document.body.style.overflowY = "scroll";
}
}
I want to share the solution that works for me. The above solution did not work on ios. I am sorry for my English. I do not know english.
function stop(e){
e=e || event;
e.preventDefault;
}
window.onscroll=stop(); //-->Yes, we will use it ..
For example, where you will use;
function move(event){
var finish=event.touches[0].clientX;
var verticalFinish=event.touches[0].clientY;
var diff=finish-strt;
var verticalDiff=verticalStrt-verticalFinish;
var f;
if(diff<0 && (Math.abs(diff)>Math.abs(verticalDiff)/3)){
f=verticalDiff+widthOffset;
slayt[x].style.left=diff+"px";
slayt[x].style.transition="none";
slayt[y].style.left=f+"px";
slayt[y].style.transition="none";
window.onscroll=stop(); //-->we used it here :)
}
else if(diff>0 && (Math.abs(diff)>Math.abs(verticalDiff)/3)){
f=diff-widthOffset;
slayt[x].style.left=diff+"px";
slayt[x].style.transition="none";
slayt[z].style.left=f+"px";
slayt[z].style.transition="none";
window.onscroll=stop();//-->we used it here :)
}
}
but there is a small problem. cancels if there is another function related to scrolling. return true; it does not work. I also write twice if I have a function related to the slider inside and outside the touchend.
function end(event){
//"touchend" related codes...
//bla bla
window.onscroll=function(){m=window.pageYOffset;console.log(m);if(m>=850)
{buton.style.display="block";}else{buton.style.display="none";}}
}
If it is useful, I will be happy...
Update :
I typed wrong. I want to fix. Actually, the scroll event cannot be canceled unfortunately. So the event we canceled above, scroll is not a vertical scroll event. All events.
window.onscroll=stop(); // ==>improper use
stop(); // ==> actually - Correct usage
It just needs to be written so stop().
html,
body {
overflow: hidden;
}
Did you try this?

Custom buttons to replace browser scrolls -- hold and continuous scroll?

I have custom buttons that replaces the browser scrollbar. The idea is so that scrolling oversize elements in a page wouldn't result to a dozen scroll bar on a page.
See: https://jsfiddle.net/bwgxs6ng/
Since I must show some code sample (according to some SO error message), see this:
$('.right').on('click', function(event) {
var target = $(".image-container");
var current_x = target.scrollLeft();
if( target.length ) {
event.preventDefault();
$(target).animate({
scrollLeft: current_x+100
}, 500);
}
});
It's very simple, basically it takes current scroll position of the parent, and add x to it based on the direction that's clicked.
However, going further, I want it to imitate the hold and continuous scroll, but I'm not sure how to do it.
1) What is the mouse hold event called? (OK, this part is answered, it's called MouseDown as someone point out of the duplicate)
2) What is the continuous scrolling called, and how can I do something that'd imitate the browser's continuous scroll?
You can just call .animate() repeatedly (with easing set to linear, for smooth movement) inside your setInterval() callback. Just arrange for the interval to be equal to the animation duration, so that the next animation starts just when the previous one ends.
Or, better yet, make the interval shorter (say, 50 ms or less) and just call .prop() instead of .animate(), effectively performing your own animation. (This is how jQuery implements animation internally, anyway.)
Anyway, here's how I'd rewrite your code to support smooth continuous scrolling:
var speed_x = 0, speed_y = 0;
var timer = null;
var target = $(".image-container");
function scroll() {
if (speed_x == 0 && speed_y == 0) return;
var current_x = target.scrollLeft();
var current_y = target.scrollTop();
target.prop({
scrollLeft: current_x - speed_x,
scrollTop: current_y - speed_y
});
}
$('.control').on('mouseover mouseout', function (event) {
var $this = $(this);
var speed = (event.type == 'mouseover' ? 10 : 0)
if ($this.hasClass('left')) speed_x = +speed;
if ($this.hasClass('right')) speed_x = -speed;
if ($this.hasClass('up')) speed_y = +speed;
if ($this.hasClass('down')) speed_y = -speed;
}).on( 'mousedown', function () {
scroll();
if (timer !== null) clearInterval(timer);
timer = setInterval(scroll, 50);
return false;
});
$(document).on('mouseup', function () {
if (timer !== null) clearInterval(timer);
timer = null;
});
Note how the animation is started and stopped in the mousedown and mouseup handlers, but the direction of movement is set on mouseover and mouseout. This allows you to change the scrolling direction while holding the mouse down, by dragging the cursor from one edge to another.
(For bonus points, add divs with e.g. class="control up left" in the corners of the scroll area, so that holding the mouse down over those corners will allow you to scroll diagonally. The JS code above already supports it.)
you need to set an interval on mousedown, and clear the interval on mouseup, as done in this fiddle for left and right.
The relevant code change is that we removed the click event and replaced it with
$('.left').on('mousedown', function(event) {
... scroll code ...
interval = setInterval(function(){
... scroll code ...
},500);
})
.on('mouseup',function(){clearInterval(interval);});

Preventing the default action only for a left swipe/right swipe event

I am using event.preventDefault but after some checks, I want the event to continue.
For eg, For the
touchmove
event I am using event.preventDefault() as I don't want the browser to scroll horizontally, but after determining the direction of swipe, if the direction turns out to be up/down, whatever event i have prevented, i want to nullify the effect. Is that possible?
My scenario
This is my html
<div id="teazer" class="globalTeaser" ontouchcancel="touchCancel(event);" ontouchmove="touchMove(event);" ontouchend="touchEnd(event,'gTeaser');" ontouchstart="touchStart(event,'teazer');">
This is my js after removing the irrelevant part
function touchMove(event) {
if(fingerCount==1){
event.preventDefault();
if ( event.touches.length == 1 ) {
curX = event.touches[0].pageX;
curY = event.touches[0].pageY;
}
else {
touchCancel(event);
}
}else {
touchCancel(event);
alert("hi");
}
}
function touchEnd(event,eventType) {
//I get the swipe direction here,after some calculations
//I get the directions fine,now
//if the swipe direction is up/down whatever event i have prevented in touch move I need to nullify
}
Please note, if anyone is gonna suggest window.scrollBy, for some reason its not working for me. Any other solutions I am happy to try.
I don't know how your code behaves exactly, but this is the general principle:
// Keep a reference for last point both events can access
var start;
$( 'body' ).on( {
// On start, assign the event data to start
touchstart : function touchStart( touchstartEvent ){
start = touchstartEvent.touches[ 0 ];
},
touchmove : function touchMove( touchmoveEvent ){
// The difference between both events positions
var delta = {};
// The end event's position
var end = touchmoveEvent.touches[ 0 ];
// Calculate the offset compared to start for this move event
delata.pageX = start.pageX - end.pageX;
delata.pageY = start.pageY - end.pageY;
// If pageX is less than or greater than 0, it means there has been horizontal movement,
// and this if condition will pass
if( delta.pageX ){
touchmoveEvent.preventDefault();
}
}
} );
This isn't perfect, because in practice it's very difficult to create a perfect downward or upward swipe — my finger might move a couple of pixels left or right even if the main movement is downwards. But then you have the problem of preventing horizontal motion but not vertical! In any case, the general principal for testing different properties of a touch event and only preventing default in certain conditions is illustrated.
My touchMove() function has been rewritten as follows
function touchMove(event) {
if (event.touches.length == 1) {
curX = event.touches[0].pageX;
curY = event.touches[0].pageY;
if (Math.abs((curX - startX)) > 10) {
//default acion is prevented only if the
//finger count is one and change in
//x coordinatesis greater than 10 px
event.preventDefault();
}
}
else {
touchCancel(event);
}
}
This code will make sure of two things
1)Pinch to zoom is not affected
2)default action for vertical swipe is not affected

Prevent scrolling when done too quickly with jQuery

In order to prevent mousewheel scrolling to scroll the entire page when reaching the top/bottom of an element with its own scrollbars, I'm using Brandon Aaron's Mousewheel plugin.
This works fine, as long as I don't scroll too quickly. It seems that when scrolling really quickly, two events will pass the "I haven't reached the top/bottom" check yet and will both be executed. However, one of them will then scroll the element to the top/bottom and the next one will then scroll the entire page, which was what I was trying to prevent.
I'm currently doing this
$('.popupContents').bind('mousewheel', function (e, d) {
var t = $(this);
if (d > 0 && t.scrollTop() === 0) {
e.preventDefault();
} else if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) {
e.preventDefault();
}
});
(As posted in Prevent scrolling of parent element? )
How do I make it so that the function properly stops all events at the top/bottom even when the user scrolls quickly?
I ended up manually tracking the desired scroll position and disallowing the normal scroll event altogether.
var wantedScrollTop = 0;
$('.popupBody').bind('mousewheel', function (e, d) {
var t = $(this);
var scrollTo;
if (d > 0) {
scrollTo = Math.max(0, wantedScrollTop - 30);
} else if (d < 0) {
scrollTo = Math.min(t.get(0).scrollHeight - t.innerHeight(), wantedScrollTop + 30);
}
if (typeof scrollTo !== "undefined") {
wantedScrollTop = scrollTo;
t.scrollTop(wantedScrollTop);
//t.stop().animate({ scrollTop: wantedScrollTop + 'px' }, 150);
}
e.preventDefault();
});
d is the scroll direction, so I'm manually keeping track of the wanted scroll position here. In my case there is only one popup at a time, so I didn't bother sticking the wantedScrollTop in a data- attribute or something similar on the element, which could be useful when youdo have multiple elements that need to track their own scroll position.
It is not doing a fluent scroll like your browser would, but it will change the vertical scroll position by 30 pixels for each time the scrollwheel triggers the event. I left the commented out line in the code to show how that could be achieved. However, for me this resulted in scrolling which feeled very lagged when scrolling quickly.

Categories