I'm trying to add animation to my website using the Element.animate() method in Javascript but realized that animation events aren't triggered when the animation is ran.
Here is my code:
window.onload = function () {
let list = document.querySelector(".list");
let btn = document.querySelector(".btn");
// animation events
list.addEventListener('animationstart', () => {
console.log('start')
});
list.addEventListener('animationend', () => {
console.log('end');
});
btn.onclick = () => {
list.animate([
// keyframes
{ transform: 'translateY(0px)' },
{ transform: 'translateY(-24px)' }
], {
// timing options
duration: 1000,
fill: "forwards"
});
}
}
In the code above, the animation runs but the animation event is not triggered. Can anyone explain why this is not working or tell me how to fix it?
You are not using a CSS animation (triggered by css classes). The animationstart/end (or transitionstart/end) Events are only fired, if an CSS-defined-animation or -transition is used.
Take a look at the example at HTMLElement: animationstart event.
Instead of the start/end-events, you can access the Animation-instance directly :
btn.onclick = () => {
// animation started
let animation = list.animate(...);
animation.onfinish = () => {
// animation ended
};
};
Animation.onfinish
Related
I'm trying to scroll to a particular card when a button is clicked. I'm using scrollIntoView for that, but its not working.
.js file :
document.getElementById("general_details_edit").addEventListener("click", function(){
this.style.display = "none";
document.getElementById("general_details_card").classList.remove("d-none");
document.getElementById("general_details_card").classList.add("d-block");
console.log("start");
document.getElementById("general_details_card").scrollIntoView({behavior : 'smooth'});
console.log("end")
//not working
All of the other javascript code is working. How can i resolve this?
I get the console messages "start" and "end" as well.
Also, when i run the same line from browser's console, it works.
EDIT :
Vijay's comment about using a timeout worked for me. But how i can wait for the element to load instead of waiting for a specific amount of time?
Current code :
const scroll = (el) => {
setTimeout(function () { el.scrollIntoView( {behavior : 'smooth', block : 'end'}); }, 500);
}
One way would be to change the waiting time to zero milliseconds once the DOM is fully loaded.
var mSeconds = 500;
window.addEventListener('DOMContentLoaded', function(event) {
mSeconds = 0;
});
const scroll = (el) => {
setTimeout(function () {
el.scrollIntoView( {behavior : 'smooth', block : 'end'});
}, mSeconds);
}
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);
});
for some reason ,I need to delay click and preventDefault for some time when scroll page end.So I write something like this :
// const disableClickDuringScrollHandler=(e)=> {
// e.preventDefault();
// };
this.scrollHandler = e => {
console.log('scrolling');
const disableClickDuringScrollHandler = (e) => {
e.preventDefault();
};
document.addEventListener('click', disableClickDuringScrollHandler);
window.clearTimeout(this.scrollTimer);
this.scrollTimer = window.setTimeout(() => {
console.log('scroll end');
document.removeEventListener('click', disableClickDuringScrollHandler);
}, 300);
}
window.addEventListener('scroll', this.scrollHandler);
I also has been write a codepen: https://codepen.io/zhangolve/pen/gRNMoX
my question is when I put disableClickDuringScrollHandler outside of the scrollHandler ,removeEventListener can work well,but when I put disableClickDuringScrollHandler inside of the scrollHandler ,removeEventListener doesn't seem to work .
I have tried so many times to found why but failed.So I get here to ask your help.
The problem is that each time the user scrolls, you create a new disableClicksDuringScroll closure and add it as a click listener. When this timer runs, it removes the latest click listener, but not the previous ones (because they're different closures, so they aren't equal to the function you're removing this time).
You should define disableClicksDuringScroll function just once, outside the scroll handler, since it doesn't refer to any local variables here. Then when you call removeEventListener it will find this handler.
You can also use a variable so you only add the click listener once, when scrolling starts, not every time you reset the timer.
this.disableClickDuringScrollHandler = (e) => {
e.preventDefault();
};
this.inScroll = false;
this.scrollHandler = e => {
console.log('scrolling');
if (!this.inScroll) {
document.addEventListener('click', this.disableClickDuringScrollHandler);
this.inScroll = true;
}
window.clearTimeout(this.scrollTimer);
this.scrollTimer = window.setTimeout(() => {
this.inScroll = false;
console.log('scroll end');
document.removeEventListener('click', disableClickDuringScrollHandler);
}, 300);
}
window.addEventListener('scroll', this.scrollHandler);
What I need to achieve is if we click on submit button, there is particular div should show up.
Here is my code:
http://jsfiddle.net/7tn5d/
But if I click on submit button multiple times, the function calls sort of queue up and run one after other.
Is there a way to invalidate other onclicks when current animation is running?
Code:
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", {}, 1000);
animating = 0;
});
});
To prevent it from performing the action multiple times, simple cease the previous animation. So:
$('#submit_cont').stop().show("blind",{},1000);
However, I have noticed that you have attempted to prevent the animation from running, if an animation is already running. Although it takes 1 second or 1000 milliseconds to show the div, the execution of the condition does not pause until the animation is complete. You must define a function to run after the animation is complete, like so:
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", 1000, function() { animation = 0; });
});
});
Hope that helped...
You almost got it right with the semaphore! It's just that, in jQuery's show(), you would have to put the semaphore reset as an argument. Here's the fixed version - http://jsfiddle.net/snikrs/xe5A3/
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", 1000, function() {
animating = 0;
});
});
});
You can use the :animated selector to check:
$(function () {
$("#submit_tab").click(function (e) {
var $cont = $("#submit_cont");
if (!$cont.is(':animated')) {
$cont.show("blind", {}, 1000);
}
});
});
Now if you stick with the external semaphore idea then its better to stick that on the elemnt with .data() instead of using a global variable:
$(function () {
$("#submit_tab").click(function (e) {
var $cont = $('#submit_cont'),
animating = $cont.data('isAnimating');
if (animating) {
return;
} else {
$cont.data('isAnimating', 1);
$("#submit_cont").show("blind", 1000, function() { $cont.data('isAnimating', 0); });
}
});
});
Something like this (see documentation) :)
$("#submit_cont").show("blind", function(){
animating = 0;
});
You can add a $("#submit_cont").clearQueue(); after the animation finished :
$("#submit_tab").click(function (e) {
$("#submit_cont").show("blind", 1000, function() {
$("#submit_cont").clearQueue();
});
});
Updated JSFiddle
I found a different solution for this, which in my opinion looks cleaner:
var tab = $("submit_tag");
tab.on("click", function(){
var cont = $("submit_cont");
var animating = tab.queue("fx").length;
if(animating === 0){
cont.show("blind", {}, 1000);
}
});
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);
}
});