Bodymovin hover playing animation only once - javascript

Got my animation to trigger play on hover. I got everything to work but when I try to hover to play again, nothing seems to work.
Any ideas what I wrote wrong?
var squares = document.getElementById("test");
var animation = bodymovin.loadAnimation({
container: test,
renderer: "svg",
loop: false,
autoplay: false,
path: "https://dl.dropboxusercontent.com/BodyMovin/squares.json"
});
squares.addEventListener("mouseenter", function () {
animation.play();
});
function playAnim(anim, loop) {
if(anim.isPaused) {
anim.loop = loop;
anim.goToAndPlay(0);
}
}
function pauseAnim(anim) {
if (!anim.isPaused) {
anim.loop = false;
}
}

When the animation reaches it's final frame, it doesn't go back to the first one. That's why if you call play, nothing happens since it has already ended and there is no more to play.
You should do:
squares.addEventListener("mouseenter", function () {
animation.goToAndPlay(0);
});
And if you only want it to replay once it has reached it's final frame, this should work:
var isComplete = true;
svgContainer.addEventListener('mouseenter', function(){
if(isComplete){
squares.goToAndPlay(0);
isComplete = false;
}
})
squares.addEventListener('complete', function(){
isComplete = true;
})

I'm giving a shot on this, although my experience about Bodymovin (and Lottie) is only from React Native world.
This should be quite straight forward to stop animation and restart it when cursor leaves the element:
squares.addEventListener("mouseenter", function () {
animation.play();
});
squares.addEventListener("mouseleave", function () {
animation.gotoAndStop(0);
});

Related

How to freeze Javascript timeouts and BS4/jQuery animations?

I have an HTML page that has timeouts. I want to freeze them when you press a button (#pauseButton) and then resume when you press it again, preferably freezing all BS4 and jQuery animations also.
<button id="pauseButton"></button>
<script>
$(document).ready(function(){
setTimeout(function() {
alert("This is an alert")
},10000);
$("#pauseButton").click(function(){
// Pause timeouts and page
});
});
</script>
EDIT
I have been notified that there is a possible duplicate answer, so I am now focusing on pausing animations and other page elements.
That answer shows how to pause timeouts only.
There are many ways to solve this issue. Many of them are mentioned in this question as mentioned by #EmadZamout in the comments.
But, if you are looking for an easy and maybe an alternate way to solve this. Try this. Here I am using requestAnimationFrame to solve the issue
let ran = Date.now(); // contains the last updated time
let time = 0; // time in seconds
let paused = false; // store the state
const func = () => {
if (!paused && Date.now() - ran > 1000) {
time++;
ran = Date.now();
console.log('now')
}
if (time === 8)
return alert('This works!');
requestAnimationFrame(func);
}
func();
document.querySelector('button').addEventListener('click', () => paused = !paused);
<button>Change state</button>
For stopping all the animations of the website, you need to manually stop each animation.
For stopping a jQuery animation, you can use .stop() helper. An example:
let paused = false; // state of the animation
let dir = 'down'; // to store the direction of animation so that the next time continues in the correct direction
let timeDown = 2000; // to animate properly after resuming
let timeUp = 2000; // to animate properly after resuming
// the initial calling of the animation
(function() {
slideDown();
})();
// function which resumes the animation
function animate() {
switch (dir) {
case 'up':
slideUp();
break;
case 'down':
slideDown();
break;
}
}
// a function to animate in the uppward direction
function slideUp() {
dir = 'up'; // setting direction to up
timeDown = 2000; // resetting the duration for slideDown function
$('div').stop().animate({
left: 0
}, {
duration: timeUp,
complete: slideDown, // calling slideDown function on complete
progress: function (animation, progress, ms) {
timeUp = ms; // changing the duration so that it looks smooth when the animation is resumed
}
}); // actual animation
}
// a function to animate in the downward direction
function slideDown() {
dir = 'down'; // setting direction to down
timeUp = 2000; // resetting the duration for slideDown function
$('div').stop().animate({
left: 200
}, {
duration: timeDown,
complete: slideUp, // calling slideUp function on complete
progress: function (animation, progress, ms) {
timeDown = ms; // changing the duration so that it looks smooth when the animation is resumed
}
}); // actual animation
}
// button click event listener
$('button').click(function() {
if (paused)
animate(); // to resume the animation
else
$('div').stop(); // to stop all the animations on the object
paused = !paused; // toggling state
});
div {
position: relative;
width: 100px;
height: 100px;
background: dodgerblue;
}
<button>Pause</button>
<div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
For bootstrap, I don't think you have any bootstrap animations which needed to be paused in this scenario which you have mentioned since bootstrap animations depend on user interactions. If you want to prevent user interaction, you can put an overlay over the website says "Paused". Or, if you don't want to do that you can use CSS property pointer-events: none to disable all the pointer events.
Now for CSS animations, you can set a property called animation-play-state to paused.
If you want to change the state of the animations to paused when the user is not on the page (As I understood for your updated questions) you can use the new visibilityState API for that. There is an event visibilitychange which is fired when there a change in visibility.
document.addEventListener("visibilitychange", function() {
console.log( document.visibilityState );
document.querySelector('div').innerHTML = document.visibilityState;
});
<div>
Try opening a different tab or change the focus to another app
</div>

prevent animation double click issue

Hi I have problem with my slider please visit this site and check http://paruyr.bl.ee/
after click on my arrows it becomes work in an asynchronous way, ones it changes very fast and then slow and it repeats.
I think it is from start slider and stop slider.
var sliderPrev = 0,
sliderNext = 1;
$("#slider > img").fadeIn(1000);
startSlider();
function startSlider(){
count = $("#slider > img").size();
loop = setInterval(function(){
if (sliderNext>(count-1)) {
sliderNext = 0;
sliderPrev = 0;
};
$("#slider").animate({left:+(-sliderNext)*100+'%'},900);
sliderPrev = sliderNext;
sliderNext=sliderNext+1;
},6000)
}
function prev () {
var newSlide=sliderPrev-1;
showSlide(newSlide);
}
function next () {
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
function stopLoop () {
window.clearInterval(loop);
}
function showSlide(id) {
stopLoop();
if (id>(count-1)) {
id = 0;
} else if(id<0){
id=count-1;
}
$("#slider").animate({left:+(-id)*100+'%'},900);
sliderPrev = id;
sliderNext=id+1;
startSlider();
};
$("#slider, .arrows").hover(function() {
stopLoop()
}, function() {
startSlider()
});
function onlyNext () {
var newSlide=sliderPrev+1;
onlyShowSlide(newSlide);
}
function onlyShowSlide(id) {
if (id>(count-1)) {
id = 0;
} else if(id<0){
id=count-1;
}
$("#slider").animate({left:+(-id)*100+'%'},900);
sliderPrev = id;
sliderNext=id+1;
};
I think the best option would be to check if the animation is in progress and prevent the action if it is, something like this:
function prev () {
if(!$('#slider').is(":animated"))
{
var newSlide=sliderPrev-1;
showSlide(newSlide);
}
}
function next () {
if(!$('#slider').is(":animated"))
{
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
}
To illustrate the difference between this and just sticking a stop() in, check this JSFiddle. You will notice some choppy movements if you click multiple times in the stop() version.
What I would do is add a class to your slider when the animation starts and remove the class when it finishes:
$("#slider").animate({left:+(-id)*100+'%'}, {
duration: 900,
start: function() {
$('#slider').addClass('blocked');
},
complete: function() {
$('#slider').removeClass('blocked');
}
});
Now check on each click event if the slider is blocked or not:
function next () {
if (!$('#slider').hasClass('blocked')) {
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
}
This is a very simple solution, I'm sure there is a better one.
EDIT: As marcjae pointed out, you could stop the animations from queuing. This means when you double click, the slideshow still will move 2 slides. With my approach the second click will be ignored completely.
You can use a variable flag to control if the animation is still being done, or simply use .stop() to avoid stacking the animation.
$("#pull").click(function(){
$("#togle-menu").stop().slideToggle("slow");
});
It is occurring because your animations are being queued.
Try adding:
.stop( true, true )
Before each of your animation methods. i.e.
$("#slider").stop( true, true ).animate({left:+(-id)*100+'%'},900);
The answers about stop are good, but you have a bigger issue that is causing the described behavior. The issue is here:
$("#slider, .arrows").hover(function() {
stopLoop()
}, function() {
startSlider()
});
You have bound this to the .arrows as well as the #slider and the arrows are contained within the slider. So, when you mouse out of an arrow and then out of the entire slider, you are calling start twice in a row without calling stop between. You can see this if you hover onto the arrow and then off of the slider multiple times in a row. The slides will change many times after 6 seconds.
Similarly, consider the case of a single click:
Enter the `#slider` [stopLoop]
Enter the `.arrows` [stopLoop]
Click the arrow [stopLoop]
[startSlider]
Leave the `.arrows` [startSlider]
Leave the `#slider` [startSlider]
As you can see from this sequence of events, startSlider is called 3 times in a row without calling stopLoop inbetween. The result is 3 intervals created, 2 of which will not be stopped the next time stopLoop is called.
You should just have this hover on the #slider and more importantly, add a call to stopLoop as the first step in startSlider. That will ensure that the interval is always cleared before creating a new one.
$("#slider").hover(function() {
stopLoop()
}, function() {
startSlider()
});
function startSlider(){
stopLoop();
/* start the slider */
}

This action will actually increase or decrease a webpage performance?

I created this piece of code to increase my webpage performance.
If autoplay.v.mystart is true, the sliding and animations of 2 slideshows will not be played,I made condition on it. My aim is to stop the animations while user is scrolling and reactivate it while user stopped scrolling, I think it will reduce a webpage load, to make a webpage scroll smoother, as I listened to people say stop unused animations or hide things that's unused. However, I see it didn't go smoother, but a bit more laggy. Is it using scroll event listener and timer/cleartimeout will take up a lot of resources too? What is the best way to accomplish my aim , to reduce my webpage load? I am thinking should I remove this code?That will be a waste,I can't decide
var saviour = {
'$mywrapper' : $('#wrapper'),
'mychecked':false,
run : function(){
var wrapper_timer;
saviour.$mywrapper.scroll(function(){
if(saviour.mychecked==false){
saviour.mychecked = true;
autoplay.v.mystart = false;
clearTimeout(wrapper_timer);
setTimeout(function(){saviour.mychecked=false},1000);
wrapper_timer = setTimeout(function(){
autoplay.v.mystart = true;
console.log('autoplay restart')
},4000);
console.log('check');
}
});
}
}
saviour.run();
First, here's a jQuery addon that provides 'scrollstart' and 'scrollstop' events, based on this, which was written for an early version of jQuery and needed to be modernized.
(function($, latency) {
var special = $.event.special;
special.scrollstart = {
setup: function() {
var timer;
function handler(evt) {
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'scrollstart';
$.event.handle.apply(this, arguments);
}
timer = setTimeout(function() {
timer = null;
}, latency);
};
$(this).on('scroll.start', handler);
},
teardown: function() {
$(this).off('scroll.start');
}
};
special.scrollstop = {
setup: function() {
var timer;
function handler(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
timer = null;
evt.type = 'scrollstop';
$.event.handle.apply(_self, _args);
}, latency);
};
$(this).on('scroll.stop', handler);
},
teardown: function() {
$(this).off('scroll.stop');
}
};
})(jQuery, 300);
This version :
Replaces .bind() and .unbind() with .on() and .off(), plus associated simplification.
Allows the latency to be specified as a parameter to the self-executing function wrapper.
With 'scrollstart' and 'scrollstop' event detection in place, the application snippet for starting and stopping the animation can be as simple as this :
$(window).on ('scrollstart', function(e) {
allowAnim = false;
stopAnim();
}).on ('scrollstop', function(e) {
allowAnim = true;
anim();
}).trigger('scrollstop');
where anim() and stopAnim() are your functions for starting and stopping animation(s) and allowAnim is a boolean var in an outer scope.
You may want to adjust the latency. I found 300 to be about the minimum acceptable value, and very responsive. Larger A higher value will be less responsive but will better prevent the animation from restarting in mid-scroll.
DEMO

Javascript "while hovered" loop

Can anybody help me on this one...I have a button which when is hovered, triggers an action. But I'd like it to repeat it for as long as the button is hovered.
I'd appreciate any solution, be it in jquery or pure javascript - here is how my code looks at this moment (in jquery):
var scrollingposition = 0;
$('#button').hover(function(){
++scrollingposition;
$('#object').css("right", scrollingposition);
});
Now how can i put this into some kind of while loop, so that #object is moving px by px for as #button is hovered, not just when the mouse enters it?
OK... another stab at the answer:
$('myselector').each(function () {
var hovered = false;
var loop = window.setInterval(function () {
if (hovered) {
// ...
}
}, 250);
$(this).hover(
function () {
hovered = true;
},
function () {
hovered = false;
}
);
});
The 250 means the task repeats every quarter of a second. You can decrease this number to make it faster or increase it to make it slower.
Nathan's answer is a good start, but you should also use window.clearInterval when the mouse leaves the element (mouseleave event) to cancel the repeated action which was set up using setInterval(), because this way the "loop" is running only when the mouse pointer enters the element (mouseover event).
Here is a sample code:
function doSomethingRepeatedly(){
// do this repeatedly when hovering the element
}
var intervalId;
$(document).ready(function () {
$('#myelement').hover(function () {
var intervalDelay = 10;
// call doSomethingRepeatedly() function repeatedly with 10ms delay between the function calls
intervalId = setInterval(doSomethingRepeatedly, intervalDelay);
}, function () {
// cancel calling doSomethingRepeatedly() function repeatedly
clearInterval(intervalId);
});
});
I created a sample code on jsFiddle which demonstrates how to scroll the background-image of an element left-to-right and then backwards on hover with the code shown above:
http://jsfiddle.net/Sk8erPeter/HLT3J/15/
If its an animation you can "stop" an animation half way through. So it looks like you're moving something to the left so you could do:
var maxScroll = 9999;
$('#button').hover(
function(){ $('#object').animate({ "right":maxScroll+"px" }, 10000); },
function(){ $('#object').stop(); } );
var buttonHovered = false;
$('#button').hover(function () {
buttonHovered = true;
while (buttonHovered) {
...
}
},
function () {
buttonHovered = false;
});
If you want to do this for multiple objects, it might be better to make it a bit more object oriented than a global variable though.
Edit:
Think the best way of dealing with multiple objects is to put it in an .each() block:
$('myselector').each(function () {
var hovered = false;
$(this).hover(function () {
hovered = true;
while (hovered) {
...
}
},
function () {
hovered = false;
});
});
Edit2:
Or you could do it by adding a class:
$('selector').hover(function () {
$(this).addClass('hovered');
while ($(this).hasClass('hovered')) {
...
}
}, function () {
$(this).removeClass('hovered');
});
var scrollingposition = 0;
$('#button').hover(function(){
var $this = $(this);
var $obj = $("#object");
while ( $this.is(":hover") ) {
scrollingposition += 1;
$obj.css("right", scrollingposition);
}
});

Stopping .click() listener until fadeIn() is finished in jQuery

$('#div1_button').click(function() {
$('#div0').fadeOut(function(){
$('#div1').fadeIn();
});
});
When a user clicks div1_button the previously selected div0 fades out and div1 fades in. If the user goes click crazy and clicks div2 before div1 is finished fading in then div2 begins to fade in and eventually div1 fades out, but they stack on top of each other until div1 is finished fading in then fades out. How can I stop the .click() event until the clicked div is finished fading in.
Something like
var div1_bclick_inprogress = false;
$('#div1_button').click(function() {
if (!div1_bclick_inprogress) {
div1_bclick_inprogress = true;
$('#div0').fadeOut(function(){
$('#div1').fadeIn(function(){
div1_bclick_inprogress = false;
});
});
}
});
but you may have to experiment a bit with the details
USE :animated ..
http://api.jquery.com/animated-selector/
Here: an example
$("#div1_button").click(function() {
if (!$(this).parent().children().is(':animated')) {
$('#div0').fadeOut(function(){
$('#div1').fadeIn();
});
}
return false;
});
You can stop animations by using the jQuery .stop() function.
http://api.jquery.com/stop/
$('#div1_button').click(function() {
$('#div0').stop(true, true).fadeOut(function(){
$('#div1').stop(true, true).fadeIn();
});
});
While this is not exactly what you requested, it's definitely what I would've done.
don't you think that is better to stop the fadeIn/fadeOut and change the direction as the user requested?
in this case:
$('#div1_button').click(function() {
var state = $(this).data("state");
$(this).data(state, !state);
var d0 = $("#div0").stop(),
d1 = $("#div1").stop();
if (state) {
d0.fadeOut(function() {
d1.fadeIn();
});
} else {
d0.fadeIn(function() {
d1.fadeOut();
});
}
});
or something like this
div1_click_handler = function()
{
$('#div1_button').unbind('click', div1_click_handler);
$('#div0').fadeOut('slow', function()
{
$('#div1').fadeIn('slow', function()
{
$('#div1_button').click(div1_click_handler);
});
});
});
$('#div1_button').click(div1_click_handler);
You could create an external boolean value that each click value checks before fading. i.e.
var noFading = true;
$('#div1_button').click(function() {
if (noFading) {
noFading = false;
$('#div0').fadeOut(function(){
$('#div1').fadeIn(function() { noFading = true; });
});
}
});
Use jQuery.data to store a flag. Set the flag after the first click, and ignore clicks until the flag is unset by the fade finishing:
$('#div1_button').click(function() {
if ($('#div1').data('disableClick') === true) return false;
$('#div1').data('disableClick', true);
$('#div0').fadeOut(function(){
$('#div1').fadeIn(function() {
$('#div1').data('disableClick', false);
});
});
});

Categories