Cut jQuery Animate Function Short and Revert to Start on Keypress - javascript

I have a simple dropping ball animation, which I'm using jQuery animate function for. The ball starts at bottom: 600 and falls until it reaches the ground, at bottom: 90.
function ballbounce() {
$('.football').animate({
'bottom': 90
}, bouncetime, 'easeOutBounce', function() {
});
}
When the spacebar is pressed, I want to cut the falling animation short and have the ball go up again. I'm trying this by detecting the keypress, getting the current bottom value of the ball, adding 300px to that value, then setting it as the new bottom value on the ball. But I want the falling animation to resume from that point.
$(document).keydown(function(e) {
switch(e.which) {
case 32: // spacebar
var ballOffset = $('.football').offset();
var currentBallPosition = $(window).height() - ballOffset.top - $('.football').height();
var newBallPosition = currentBallPosition + 300;
$('.football').css("bottom", newBallPosition);
break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action
});
I can't get it working, I'm not sure where I'm going wrong. The ball's bottom is only being updated if I press the spacebar after the animation is complete.
This is for a basic keepy uppy soccer game. So the ball drops and you press spacebar to kick the ball back up in the air again. Is this even a good solution?

I would call .stop() on the element, then reset it to it's original position. You could store the original position as a data attribute on the element itself before the animation then use that to reset it later like this:
function ballbounce(selector,bouncetime) {
var $ball= $(selector);
$ball.data('original-top',$ball.position().top )
$ball.animate({ top: '+550px'}, bouncetime, "easeOutBounce", function() {
//animation complete
});
return $ball;
}
var $ballReference=ballbounce('.football',5000);
$(document).keydown(function(e) {
switch(e.which) {
case 32: // spacebar
$ballReference.stop().css({top:$ballReference.data('original-top')});
break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action
});
.football{
width:100px;
height:100px;
background-color:#ccc;
position:absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<div class="football"></div>

Related

Why the key event causes snappy transitions?

I can't understand why two quick button clicks cause the div shown to transition slowly from its last position to the initial position (as I want) but two quick key events to make it snap to the initial position.
In the following code, the button click and a window key event initially transition the div to the right by 500px. On the next firing, they take it back to its original position. Quick firing will cause the div to quickly go to some position on the right and come back again.
With two button clicks I face no problem. But with two quick right-arrow key clicks I notice snappy behaviour. Any suggestions will really be appreciated, because I couldn't find any explanation to this strange behaviour.
var ele = document.getElementById("f");
var key = true;
var buttons = document.getElementsByTagName("button")
var counter = 0;
var c = [0,500];
function demo() {
counter = !counter + 0;
navigateSlider();
}
// code to account for KEY
if (key) {
window.onkeydown = function (e) {
console.log(e.keyCode);
if (e.keyCode == 39) {
buttons[0].click();
}
}
}
function navigateSlider() {
ele.style.transition = "1s ease";
ele.style.transform = "translateX(" + c[counter] + "px)";
}
<div id="f" style="background-color: grey; border-radius: 10px;border: 1px solid black; padding: 50px;display: inline-block"></div>
<br><br>
<button onclick="demo()">Go Right</button>
Can't say exactly why but if you trigger click event with '0' timeout it works better.
if (e.keyCode == 39) {
setTimeout(function(){
buttons[0].click();
}, 0);
}
It definitely seems handling keyboard events somehow interrupts CSS transitions in Chrome and Firefox.

onmousedown/onmouseup function not working

I have a rectangle I am trying to move using javascript. I can get it to move left and right using functions, but when I hold the mouse down it doesn't continue to move. Here is my html code:
<button onmousedown="moveLeft();" onmouseup="stopMove();">LEFT</button>
<button onmousedown="moveRight();"
onmouseup="stopMove();">RIGHT</button><br><br>
Here is my javascript code (the conditional statements are just so the rectangle doesn't move off of the canvas):
function moveLeft () {
xx-=20;
if (xx<0) {
xx=0;
}
}
function moveRight() {
xx+=20;
if (xx>400) {
xx=400;
}
}
function stopMove () {
xx+=0;
}
When you press a mouse button, the mousedown event will fire just once. It does not continuously fire while you have the button held down. What you'll need to do is use a timer to begin the movement and then, on mouseup, stop the timer.
Also, don't use inline HTML event handlers (onmouseover, onmouseout, etc.). Here's why.
Lastly, since your moveLeft and moveRight functions are essentially the same thing, they can be combined into one function that simply takes in an argument that determines the direction.
Here's a working example:
// Get references to the HTML elements you'll need to interact with:
var btnLeft = document.getElementById("btnLeft");
var btnRight = document.getElementById("btnRight");
var box = document.getElementById("box");
// Set up event handlers for the HTML elements in JavaScript, using modern
// standards, not in the HTML. Both the left and right buttons need to
// call the same function, but with a different argument value. This is
// why the events are being bound to another function that wraps the actual
// call to the move function (so that an argument can be passed).
btnLeft.addEventListener("mousedown", function(){ move(-20); });
btnRight.addEventListener("mousedown", function(){ move(20); });
btnLeft.addEventListener("mouseup", stopMove);
btnRight.addEventListener("mouseup", stopMove);
// The counter needs to be accessible between calls to move
var xx = 0;
// We'll start an automatic timer, but we'll need to stop it later
// so we need a variable set up for that.
var timer = null;
// Only one function is needed to address the move operation
// The argument is what determines the direction
function move(amount) {
// Stop any previous timers so that we don't get a runaway effect
clearTimeout(timer);
xx += amount;
// Check the new position to see if it is off the visible page
if (xx < 0){
xx = window.innerWidth;
} else if(xx > window.innerWidth){
xx = 0;
}
// Just displaying the current value
box.textContent = xx;
// Move the box to the new location
box.style.left = xx + "px";
// Run the timer after a 250 millisecond delay and have
// it call this function again. Assign the variable to
// the timer.
timer = setTimeout(function() { move(amount) }, 250);
}
function stopMove () {
xx += 0;
// Stop the timer
clearTimeout(timer);
}
#box {
background-color:blue;
border:2px solid black;
color:yellow;
text-align:center;
font-weight:bold;
padding-top:.5em;
margin-top:3em;
/* Everything above is just for display and not actually needed.
But in order to move the box and to be able to have a consistent
size for it, the following is required: */
position:absolute;
height:50px;
width:50px;
}
<button id="btnLeft">LEFT</button>
<button id="btnRight">RIGHT</button>
<div id="box"></div>
Since the mouse evet does not fire while it is down, you need to fire the event manually until you detect the other event. So to do this, you need to either use setInterval or setTimeout
var moveTimer,
xx = 200;
function move (dir) {
xx+=20*dir;
if (xx<0) {
xx=0;
} else if (xx>400) {
xx=400
}
stopMove()
moveTimer = window.setTimeout(move.bind(this,dir), 100)
document.getElementById("out").value = xx;
}
function stopMove () {
if(moveTimer) window.clearTimeout(moveTimer);
moveTimer = null
}
window.addEventListener("mouseup", stopMove)
<button onmousedown="move(-1);">LEFT</button>
<button onmousedown="move(1);">RIGHT</button>
<input type="number" id="out"/>

Photoswipe 4.0: Initiate 'swipe to next' programatically

SITUATION
I have been trying to trigger the 'slide to next picture' animation when the NEXT button is clicked, but i have not found a solution for this.
There is an ongoing discussion about this on GitHub, but it is only about adding the option for a slide animation, not about how to actually do it with PS as it is right now.
There was an option for it in 3.0 but as 4.0 is a complete rewrite it does not work anymore.
QUESTION
Instead of just 'jumping' to the next/prev picture when an arrow is clicked, i need the 'slide transition' that is also used when swiping/dragging the image.
There is no option to trigger that, so how can i manually trigger this effect with JS?
PhotoSwipe Slide Transitions
So, I added slide transitions to Photoswipe, and it's working nicely without disturbing native behavior.
http://codepen.io/mjau-mjau/full/XbqBbp/
http://codepen.io/mjau-mjau/pen/XbqBbp
The only limitation is that transition will not be applied between seams in loop mode (for example when looping from last slide to slide 1). In examples I have used jQuery.
Essentially, it works by simply adding a CSS transition class to the .pswp__container on demand, but we need to add some javascript events to prevent the transition from interfering with Swipe, and only if mouseUsed. We also add a patch so the transition does not get added between loop seams.
1. Add the below to your CSS
It will be applied on-demand from javascript when required.
.pswp__container_transition {
-webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
}
2. Add javascript events to assist in assigning the transition class
This can go anywhere, but must be triggered after jQuery is loaded.
var mouseUsed = false;
$('body').on('mousedown', '.pswp__scroll-wrap', function(event) {
// On mousedown, temporarily remove the transition class in preparation for swipe. $(this).children('.pswp__container_transition').removeClass('pswp__container_transition');
}).on('mousedown', '.pswp__button--arrow--left, .pswp__button--arrow--right', function(event) {
// Exlude navigation arrows from the above event.
event.stopPropagation();
}).on('mousemove.detect', function(event) {
// Detect mouseUsed before as early as possible to feed PhotoSwipe
mouseUsed = true;
$('body').off('mousemove.detect');
});
3. Add beforeChange listener to re-assign transition class on photoswipe init
The below needs to be added in your PhotoSwipe init logic.
// Create your photoswipe gallery element as usual
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
// Transition Manager function (triggers only on mouseUsed)
function transitionManager() {
// Create var to store slide index
var currentSlide = options.index;
// Listen for photoswipe change event to re-apply transition class
gallery.listen('beforeChange', function() {
// Only apply transition class if difference between last and next slide is < 2
// If difference > 1, it means we are at the loop seam.
var transition = Math.abs(gallery.getCurrentIndex()-currentSlide) < 2;
// Apply transition class depending on above
$('.pswp__container').toggleClass('pswp__container_transition', transition);
// Update currentSlide
currentSlide = gallery.getCurrentIndex();
});
}
// Only apply transition manager functionality if mouse
if(mouseUsed) {
transitionManager();
} else {
gallery.listen('mouseUsed', function(){
mouseUsed = true;
transitionManager();
});
}
// init your gallery per usual
gallery.init();
You can just use a css transition:
.pswp__container{
transition:.3s ease-in-out all;
}
This might not be ideal for performance on mobile, but I just add this transition in a media query and allow users to use the swipe functionality on smaller screens.
I finally bit the bullet and spent some time making this work as nobody seemed to have a solution for that, not here neither on GitHub or anywhere else.
SOLUTION
I used the fact that a click on the arrow jumps to the next item and triggers the loading of the next image and sets the whole slide state to represent the correct situation in an instant.
So i just added custom buttons which would initiate a slide transition and then triggered a click on the original buttons (which i hid via CSS) which would update the slide state to represent the situation i created visually.
Added NEW next and prev arrows
Hid the ORIGINAL next and prev arrows via css
Animated the slide myself when the NEW next or prev arrows were clicked
Then triggered the click on the ORIGINAL next or prev arrows programmatically
So here is the code:
HTML
// THE NEW BUTTONS
<button class="NEW-button-left" title="Previous (arrow left)">PREV</button>
<button class="NEW-button-right" title="Next (arrow right)">NEXT</button>
// added right before this original lines of the example code
<button class="pswp__button pswp__button--arrow--left ...
CSS
pswp__button--arrow--left,
pswp__button--arrow--right {
display: none;
}
NEW-button-left,
NEW-button-right {
/* whatever you fancy */
}
JAVASCRIPT (helper functions)
var tx = 0; // current translation
var tdir = 0;
var slidepseactive = false;
// helper function to get current translate3d positions
// as per https://stackoverflow.com/a/7982594/826194
function getTransform(el) {
var results = $(el).css('-webkit-transform').match(/matrix(?:(3d)\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))(?:, (-{0,1}\d+)), -{0,1}\d+\)|\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))\))/)
if(!results) return [0, 0, 0];
if(results[1] == '3d') return results.slice(2,5);
results.push(0);
return results.slice(5, 8);
}
// set the translate x position of an element
function translate3dX($e, x) {
$e.css({
// TODO: depending on the browser we need one of those, for now just chrome
//'-webkit-transform': 'translate3d(' +String(x) + 'px, 0px, 0px)'
//, '-moz-transform': 'translate3d(' +String(x) + 'px, 0px, 0px)'
'transform': 'translate3d(' +String(x) + 'px, 0px, 0px)'
});
};
JAVASCRIPT (main)
// will slide to the left or to the right
function slidePS(direction) {
if (slidepseactive) // prevent interruptions
return;
tdir = -1;
if (direction == "left") {
tdir = 1;
}
// get the current slides transition position
var t = getTransform(".pswp__container");
tx = parseInt(t[0]);
// reset anim counter (you can use any property as anim counter)
$(".pswp__container").css("text-indent", "0px");
slidepseactive = true;
$(".pswp__container").animate(
{textIndent: 100},{
step: function (now, fx) {
// here 8.7 is the no. of pixels we move per animation step %
// so in this case we slide a total of 870px, depends on your setup
// you might want to use a percentage value, in this case it was
// a popup thats why it is a a fixed value per step
translate3dX($(this), tx + Math.round(8.7 * now * tdir));
},
duration: '300ms',
done: function () {
// now that we finished sliding trigger the original buttons so
// that the photoswipe state reflects the new situation
slidepseactive = false;
if (tdir == -1)
$(".pswp__button--arrow--right").trigger("click");
else
$(".pswp__button--arrow--left").trigger("click");
}
},
'linear');
}
// now activate our buttons
$(function(){
$(".NEW-button-left").click(function(){
slidePS("left");
});
$(".NEW-button-right").click(function(){
slidePS("right");
});
});
I used info from those SE answers:
jQuery animate a -webkit-transform
Get translate3d values of a div?
The PhotoSwipe can do this by itself when you use swipe gesture. So why not to use the internal code instead of something that doesn't work well?
With my solution everything works well, the arrow clicks, the cursor keys and even the loop back at the end and it doesn't break anything.
Simply edit the photoswipe.js file and replace the goTo function with this code:
goTo: function(index) {
var itemsDiff;
if (index == _currentItemIndex + 1) { //Next
itemsDiff = 1;
}
else { //Prev
itemsDiff = -1;
}
var itemChanged;
if(!_mainScrollAnimating) {
_currZoomedItemIndex = _currentItemIndex;
}
var nextCircle;
_currentItemIndex += itemsDiff;
if(_currentItemIndex < 0) {
_currentItemIndex = _options.loop ? _getNumItems()-1 : 0;
nextCircle = true;
} else if(_currentItemIndex >= _getNumItems()) {
_currentItemIndex = _options.loop ? 0 : _getNumItems()-1;
nextCircle = true;
}
if(!nextCircle || _options.loop) {
_indexDiff += itemsDiff;
_currPositionIndex -= itemsDiff;
itemChanged = true;
}
var animateToX = _slideSize.x * _currPositionIndex;
var animateToDist = Math.abs( animateToX - _mainScrollPos.x );
var finishAnimDuration = 333;
if(_currZoomedItemIndex === _currentItemIndex) {
itemChanged = false;
}
_mainScrollAnimating = true;
_shout('mainScrollAnimStart');
_animateProp('mainScroll', _mainScrollPos.x, animateToX, finishAnimDuration, framework.easing.cubic.out,
_moveMainScroll,
function() {
_stopAllAnimations();
_mainScrollAnimating = false;
_currZoomedItemIndex = -1;
if(itemChanged || _currZoomedItemIndex !== _currentItemIndex) {
self.updateCurrItem();
}
_shout('mainScrollAnimComplete');
}
);
if(itemChanged) {
self.updateCurrItem(true);
}
return itemChanged;
},

detecting collision between two images in jquery

i have this game :http://jsfiddle.net/Qfe6L/5/
i am trying to detect when a shuriken hit an enemy so when it hit it the enemy should disappear and the score should be increased by 1 what i searched for is that i should calculate the position of the two images to check whether their is a collision but i don't seem i can do that any help from you guys ?
$(window).keypress(function (e) {
if (e.which == 32) {
CreateChuriken();
$("#Shuriken" + Shurikengid).animate({ left: '+=300px' }, 'slow');
if ($(".Shuriken").css('left') == $(".Enemy").css('left'))
{ alert("Met"); }
}
});
You need to check for collision in each animation step. Fortunately jQuery .animate() has a progress option, which you can pass a function to be called every frame.
$("#Shuriken" + Shurikengid).animate(
{ left: '+=300px' },
{ duration : 'slow',
progress: function(){
/* collision detection here */
}
}
);
Keep in mind that
if ($(".Shuriken").css('left') == $(".Enemy").css('left'))
will only compare position of first projectile and first enemy, while there are more of them on the screen. You need to iterate over every projectile and compare its powition with every enemy to find a colliding pair, like:
$('.Shuriken').each( function(){
var sOffset = $(this).offset();
$('.Enemy').each( function(){
var eOffset = $(this).offset();
if( sOffset.left == eOffset.left ){
/* boom! */
}
});
});
The above is close, but still won't work. Animation doesn't progress by 1px each frame, so you may go from Shuriken at 100px left and Enemy at 101px left at one frame to Shuriken at 102px left and Enemy at 99px left in the next one. They'll pass each other, but won't meet at the same point. So you'd need to round these values to, say, nearest 10s which will give you a bigger tolerance. You sholud also compare the vertical positions.
Updated fiddle: http://jsfiddle.net/Qfe6L/8/
(Fixed vertical posiotion of Enemies for easier testing).
Edit:
as suggested by #Kasyx it would be better to move all of this out of animation function and create a Game Loop and Scene Graph. Scene graph would keep track of elements' positions, and within Game Loop you'd check for collisions, then call a rendering function which would draw elements on screen based on the scene graph.
At the moment you’re running your hit check function once, directly after you’ve started the animation. What you need to be doing is running it every frame to see the intersect. Luckily jQuery provides a callback handler for this: $.animate’s step option. If you pass a second object into $.animate you can specify both your duration and step function like so:
$(window).keypress(function (e) {
if (e.which == 32) {
CreateChuriken();
$("#Shuriken" + Shurikengid).animate({
left: '+=300px'
}, {
duration: 'slow',
step: function(){
if ($(".Shuriken").css('left') == $(".Enemy").css('left')) {
alert("Met");
}
}
});
}
});
As you’re calling your step function once every frame you’ll want to cache your selectors inside ($('.Shuriken'), $('.Enemy')) first:
$(window).keypress(function (e) {
if (e.which == 32) {
var shuriken, enemy;
CreateChuriken();
shuriken = $('.Shuriken');
enemy = $('.Enemy');
$("#Shuriken" + Shurikengid).animate({
left: '+=300px'
}, {
duration: 'slow',
step: function(){
if (shuriken.css('left') == enemy.css('left')) {
alert("Met");
}
}
});
}
});

Move image to specified minimum value

I have the code
$(document).keydown(function (key) {
switch (parseInt(key.which, 10)) {
case 38://up
mObj.animate({
top: "-=100px"
});
mObj.animate({
top: "+=" + change + "px"
});
break;
default:
break;
}
});
Which moves my image up and down correctly upon the up button, given the up button is not pressed again while Mario is in the air. I would like to remedy this condition (not being able to click up while Mario is in the air) through a method such as the attempted in lines 43-46 of my code found here.
In essence I want to allow Mario to simply fall all the way back down to a set minimum level when not being moved up. Any help on getting it to do this or pointing out why my alternate method (the one commented out in lines 43-46) doesn't work?
As an immediate solution, you could try using this:
$(document).ready(function () {
var jumping = false;
$(document).keydown(function (key) {
switch (parseInt(key.which, 10)) {
case 38://up
if (!jumping) {
jumping = true;
var jumpUp = new $.Deferred();
var jumpDown = new $.Deferred();
mObj.animate({
top: "-=100px"
}, {
done: function () {
jumpUp.resolve();
}
});
mObj.animate({
top: "+=" + change + "px"
}, {
done: function () {
jumpDown.resolve();
}
});
$.when(jumpUp, jumpDown).done(function () {
jumping = false;
});
}
break;
default:
break;
}
});
});
http://jsfiddle.net/wuY9V/1/
It basically just has a "state" of whether or not Mario is jumping. When the "up" arrow is pressed, Mario is jumping. As soon as the animations that move him up and then back down, are done, Mario is no longer jumping. So the function ignores the "up" key when Mario is jumping. I'm sure there's a more elegant way (like built into .animate) but this is what I came up with.
So obviously to test it, try pressing the "up" key at any point during Mario's jumping...it shouldn't do anything.
I didn't look into it too much, but I'm wondering why you have a setInterval for moving Mario? The way I would've done it is only listened for the keydown event. If it's the "up" arrow, animate him to move up and then back down (like you have, and like I've assisted with here). If it's the left arrow, animate him to move left a set distance. If it's the right arrow, animate him to move right a set distance. Is there a reason you have the setInterval to constantly check for a key pressed? Maybe I'm missing something.

Categories

Resources