How can I chain animations in anime.js? - javascript

Is there any way to chain animations in anime.js or have queues / groups of animations that I can wait for in order to proceed with other animations?

Each animation with anime returns a promise, so you can use async/await in combination with Promise.all, do remember, though, Promise.all makes it so that all animations run concurrently. For example, let's say you want 3 animations all to run at the same time, then, after that group is done, do another animation:
async function animateLockAndBackground() {
const bigLockAnimation = anime({
targets: '#big-lock',
strokeDashoffset: [0, 5],
easing: 'easeInOutSine',
duration: 250,
easing: 'easeInSine'
}).finished;
const lockLineAnimation = anime({
targets: '#lock-line',
strokeDashoffset: [0, 3],
translateY: [{
value: '-2px',
duration: 350,
easing: 'easeOutExpo'
},
{
value: '2px',
duration: 350,
easing: 'easeOutExpo'
},
{
value: '-2px',
duration: 350,
easing: 'easeOutExpo'
},
],
}).finished;
const innerCircleAnimation = anime({
targets: '#inner-circle',
translateY: [{
value: '-1px',
duration: 250,
easing: 'easeOutExpo'
},
{
value: '1px',
duration: 250,
easing: 'easeOutExpo'
},
{
value: '-1px',
duration: 250,
easing: 'easeOutExpo'
},
{
value: 0,
duration: 250,
easing: 'easeOutExpo'
},
],
}).finished;
await Promise.all([bigLockAnimation, lockLineAnimation, innerCircleAnimation]);
}
animateLockAndBackground().then(() => {
console.log('First animation finished.');
anime({
targets: '.plugins-not-installed-text',
translateY: [{
value: '10px',
duration: 750
}]
});
anime({
targets: '#lock-wrapper',
translateY: [{
value: '-10px',
duration: 750
}]
});
anime({
targets: '#plugins-not-installed-screen',
opacity: 0,
duration: 500,
easing: 'linear'
}).finished.then(() => {
console.log('Second animation finished.');
});
});
#plugins-not-installed-screen {
display: flex;
flex-direction: column;
position: relative;
width: 100%;
}
#plugins-not-installed-screen .upper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
z-index: 2;
padding: 24px 48px;
background-image: url('../Images/component.png');
background-repeat: repeat;
}
.plugins-not-installed-text {
font-size: 15px;
text-align: center;
}
#lock {
display: block;
width: 50px;
height: 65px;
}
#plugins-not-installed-screen #lock {}
#plugins-not-installed-screen #big-lock {
stroke-dasharray: 61 62;
stroke-dashoffset: 5;
/* go to 5 */
}
#plugins-not-installed-screen #lock-line {
stroke-dasharray: 31 33;
stroke-dashoffset: 0;
/* go to 3 */
}
#components-to-install-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.install-component-individual {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
width: 100%;
}
<script src="https://cdn.jsdelivr.net/npm/animejs#3.1.0/lib/anime.min.js"></script>
<div id="plugins-not-installed-screen" class="">
<div class="upper">
<div id="lock-wrapper">
<svg version="1.1" id="lock" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 25 35" style="enable-background:new 0 0 25 35;" xml:space="preserve">
<style type="text/css">
#big-lock{fill:none;stroke:#686868;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
#inner-circle{fill:none;stroke:#686868;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;}
#lock-line{fill:none;stroke:#686868;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;}
</style>
<path id="big-lock" d="M4.4,13.5c-1.2,0.8-2,2.1-2,3.6v4c0,2.8,1.1,5.4,3.1,7.4c1.9,1.9,4.5,2.9,7.2,2.9
c0.1,0,0.2,0,0.3,0c5.5-0.1,10-4.9,10-10.5v-3.8c0.1-1.8-0.9-3.3-2.4-4l-6.5-2.7c-0.8-0.3-1.8-0.4-2.6,0L10.1,11"/>
<circle id="inner-circle" cx="12.7" cy="21.9" r="2.9"/>
<path id="lock-line" d="M7.1,15.1V9.9c0-3.1,2.5-5.6,5.6-5.6h0c3.1,0,5.6,2.5,5.6,5.6v8"/>
</svg>
</div>
<h5 class="plugins-not-installed-text">Plugins not installed.</h5>
</div>
</div>
What, then, if you want to, inside that function, have the lock-line animate at the same time the 2 other elements are animating, so instead of 3 animation timelines, you only have 2? Here:
async function animateLockAndBackground() {
const bigLockAnimation = anime({
targets: '#big-lock',
strokeDashoffset: [0, 5],
easing: 'easeInOutSine',
duration: 250,
easing: 'easeInSine'
}).finished;
const innerCircleAnimation = anime({
targets: '#inner-circle',
translateY: [
{value: '-1px', duration: 250, easing: 'easeOutExpo'},
{value: '1px', duration: 250, easing: 'easeOutExpo'},
{value: '-1px', duration: 250, easing: 'easeOutExpo'},
{value: 0, duration: 250, easing: 'easeOutExpo'},
],
}).finished;
await Promise.all([bigLockAnimation, innerCircleAnimation]);
}
animateLockAndBackground().then(() => {
return anime({
targets: '#lock-line',
strokeDashoffset: [0, 3],
translateY: [
{value: '-2px', duration: 350, easing: 'easeOutExpo'},
{value: '2px', duration: 350, easing: 'easeOutExpo'},
{value: '-2px', duration: 350, easing: 'easeOutExpo'},
],
}).finished;
}).then(() => {
anime({
targets: '.plugins-not-installed-text',
translateY: [
{value: '10px', duration: 750}
]
});
anime({
targets: '#lock-wrapper',
translateY: [
{value: '-10px', duration: 750}
]
});
anime({
targets: '#plugins-not-installed-screen',
opacity: 0,
duration: 500,
easing: 'linear'
});
});
We moved that lock-line animation outside of the original group, made it wait for the group, then whatever else comes after the lock-line animates after.
You should think about an animation as a simple promise that you can chain.

Try the timeline function: https://animejs.com/documentation/#timelineBasics
You can do all the promise stuff yourself if you want but anime already handles it so there really isn't any point in it. Making a timeline function chains animations together and you can even force it to before the previous animation finishes by adding a time to the .add function .add({ //your animation }, time) or relative offsets with .add({ //your animation }, -/+ = time) (*plus or minus not both)

Related

GSAP 3 Scroll Trigger on leave animation reverse

I have used GSAP 3.0 ScrollTrigger in one of my websites everything is working fine but I want my animations to be reversed when leaving the section or div. I know there is a callback call onLeave but I am not able to figure out how to use it. I am new to the scroll trigger. I am pasting the code sample for you to understand.
armor animation config
let armor = gsap.timeline({
scrollTrigger: {
trigger: '.armor-animation',
pin: true,
pinSpacing: true,
anticipatePin: .5,
scrub: 1,
start: "top 150px",
end: "+=1500",
toggleActions: "play reverse none none",
// scroller: ".smooth-scroll"
// markers: true,
onLeave: () => {
// armor.from(".layer-1", .8, {
// translateY: 200,
// opacity: 0
// }, .5)
// console.log("leave");
}
}
});
armor section animation
armor.from(".layer-1", .8, {
translateX: -200,
opacity: 0
}, .5)
.from(".layer-2", .8, {
translateY: 200,
opacity: 0
}, .6)
.from(".layer-3", .8, {
translateY: -200,
opacity: 0
}, .6)
.from(".crack-effect", 2, {
translateY: -200,
opacity: 0
}, 2)
.from(".method h1", 2, {
translateX: 200,
opacity: 0
}, .6);

How to scroll new page to top on load on click?

I'm using barba.js to fade new pages in. Code must be placed between the barba wrapper div. When I click a link to a new page though, the new page fades in at the same position as the previous page was in. So if the user clicks a link, the new page is loading in near , which I don't want.
How can I get the new page to load in at the top?
I found the answer on stack about scrollRestoration function but I still don't understand how to make this work.
Could somebody help me to compile the js code below, add some css or html to fix this issue?
$(window).scrollTop(0);
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
function delay(n) {
n = n || 2000;
return new Promise((done) => {
setTimeout(() => {
done();
}, n);
});
}
function pageTransition() {
var tl = gsap.timeline();
tl.to(".animate-this", {
duration: .2, opacity: 0, delay: 0, y: 30, //scale:0, delay: 0.2,
});
tl.to(".animate-this", {
duration: 0, opacity: 0, delay: 0, scale:0, //scale:0, delay: 0.2,
});
tl.fromTo(".loading-screen", {width: 0, left: 0}, {
duration: 1,
width: "100%",
left: "0%",
ease: "expo.inOut",
delay: 0.3,
});
tl.to("#svg-1", {
duration: 1, opacity: 0, y: 30, stagger: 0.4, delay: 0.2,
});
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "100%",
ease: "expo.inOut",
delay: 0, //CHANGE TIME HERE END TRANS
});
tl.set(".loading-screen", { left: "-100%", });
tl.set("#svg-1", { opacity: 1, y: 0 });
}
function contentAnimation() {
var tl = gsap.timeline();
tl.from(".animate-this", { duration: .5, y: 30, opacity: 0, stagger: 0.4, delay: 0.2 });
}
$(function () {
barba.init({
sync: true,
transitions: [
{
async leave(data) {
const done = this.async();
pageTransition();
await delay(2500);
done();
},
async enter(data) {
contentAnimation();
},
async once(data) {
contentAnimation();
},
},
],
});
});
Have a look:
https://pasteboard.co/Ja33erZ.gif
Here also a codepen to check my issue (html,css):
https://codepen.io/cat999/project/editor/AEeEdg
scrollRestoration shouldn't be necessary, this can be done by resetting scroll position during the transition:
tl.fromTo(".loading-screen", {width: 0, left: 0}, {
duration: 1,
width: "100%",
left: "0%",
ease: "expo.inOut",
delay: 0.3,
onComplete: function() {
window.scrollTo(0, 0);
}
});
Working example here: https://codepen.io/durchanek/project/editor/ZWOyjE
This should be what you're looking for:
barba.hooks.enter(() => {
window.scrollTo(0, 0);
});
(can be found in the docs)

Framer-motion, delay rotateY while animating x

I'm working with Framer-motion and i'm trying to find a way to delay the the animation of rotateY while the x animates to a specific position then start the rotateY.
Is this possible in Framer motion ?
Example:
const variants = {
flip: {
rotateY: 0,
x: -20,
scale: 1,
transition: {
ease: "easeInOut",
duration: 1.2
}
},
hidden: {
rotateY: 180,
x: 150,
scale: 0.5,
transition: {
ease: "easeInOut",
duration: 1
}
}
};
You can configure a transition per property. This allows you to add the necessary delay to rotateY:
const duration = 1.2;
const variants = {
flip: {
rotateY: 0,
x: -20,
scale: 1,
transition: {
ease: "easeInOut",
duration,
rotateY: {
delay: duration,
duration
}
}
},
hidden: {
rotateY: 180,
x: 150,
scale: 0.5,
transition: {
ease: "easeInOut",
duration,
rotateY: {
delay: duration,
duration
}
}
}
};
See this CodeSandbox.
#amann's post above didn't wor for me in Sep 2020 v2.65
I had to update all relevant properties in the transition to ensure they ran in succession:
transition: {
x: {
ease: "easeInOut",
duration: duration
},
rotateY: {
duration: duration,
delay: duration
}
}
Full example:
import * as React from "react";
import { motion } from "framer-motion";
import styled from "styled-components";
const duration = 1.2;
const variants = {
flip: {
rotateY: 180,
x: 150,
transition: {
x: {
ease: "easeInOut",
duration: duration
},
rotateY: {
duration: duration,
delay: duration
}
}
},
hidden: {
rotateY: 0,
x: -20,
transition: {
x: {
ease: "easeInOut",
duration: duration
},
rotateY: {
duration: duration,
delay: duration
}
}
}
};
const Box = styled(motion.div)`
background: white;
border-radius: 30px;
width: 150px;
height: 150px;
`;
export const Example = (props) => {
return (
<motion.div
initial={false}
animate={props.toggle ? "flip": "hidden"}
>
<Box variants={variants} />
</motion.div>
)
}
Codesandbox Demo
You can give specific values for each transition. Therefore, you can control them individually like so:
transition={{
rotate: { duration: 1 },
scale: { duration: 0.2 }
}}

jQuery animations step by step

I have a small problem on my jQuery animation.
I want a button which change the width of a div from 100% to 72.5%. Next to this div could be another div which could fill the rest space (width 22.5%, 5% margin)
It could do following things step by step:
Div 1 get a border
div 1 change his width
div 2 get opacity and change his width
Here is my code:
var hidden = true;
function openEditMenu(){
var em = document.getElementById("editMenu");
var rf = document.getElementById("reportField");
if(hidden){
$({alpha:0}).animate({alpha:1}, {
duration: 1500,
step: function(){
rf.style.border ="1px solid rgba(0,0,0,"+this.alpha+")";
}
});
$("#editMenu").css({opacity: 0.0, visibility: "visible"}).animate({opacity: 1.0},1000);
$(em).animate({width: "22.5%"},{ duration: 1000, queue: false });
$(rf).animate({width: "72.5%"},{ duration: 1000, queue: false });
$(rf).animate({marginRight: "5%"},{ duration: 1000, queue: false });
}
else{
$({alpha:1}).animate({alpha:0}, {
duration: 1000,
step: function(){
rf.style.border ="1px solid rgba(0,0,0,"+this.alpha+")";
}
});
$(em).animate({width: "0%"},{ duration: 1000, queue: false });
$(rf).animate({width: "100%"},{ duration: 1000 });
$(rf).animate({marginRight: "0%"},{ duration: 1000, queue: false });
$("#editMenu").css({opacity: 1.0, visibility: "visible"}).animate({opacity: 0.0},1000);
}
hidden = !hidden;
}
Like you can see in the example now everything happens on the same time. But I want that everything works step by step.
var hidden = true;
function openEditMenu() {
var em = document.getElementById("editMenu");
var rf = document.getElementById("reportField");
if (hidden) {
$({
alpha: 0
}).animate({
alpha: 1
}, {
duration: 1500,
step: function() {
rf.style.border = "1px solid rgba(0,0,0," + this.alpha + ")";
}
});
$("#editMenu").css({
opacity: 0.0,
visibility: "visible"
}).animate({
opacity: 1.0
}, 1000);
$(em).animate({
width: "22.5%"
}, {
duration: 1000,
queue: false
});
$(rf).animate({
width: "72.5%"
}, {
duration: 1000,
queue: false
});
$(rf).animate({
marginRight: "5%"
}, {
duration: 1000,
queue: false
});
} else {
$({
alpha: 1
}).animate({
alpha: 0
}, {
duration: 1000,
step: function() {
rf.style.border = "1px solid rgba(0,0,0," + this.alpha + ")";
}
});
$(em).animate({
width: "0%"
}, {
duration: 1000,
queue: false
});
$(rf).animate({
width: "100%"
}, {
duration: 1000
});
$(rf).animate({
marginRight: "0%"
}, {
duration: 1000,
queue: false
});
$("#editMenu").css({
opacity: 1.0,
visibility: "visible"
}).animate({
opacity: 0.0
}, 1000);
}
hidden = !hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="display:inline-flex;width:100%;">
<div id="reportField" style="width:100%; height:50px; background-color:blue;">
a
</div>
<div id="editMenu" style="width:0%; height:50px; background-color:red; visibility: hidden;">
b
</div>
</div>
<button onclick="openEditMenu()">
</button>
I know this solution:
$('#id').animate(
{ left: new value },
duration, function() {
// Animation complete.
});
But it doesnt works on my alpha animation.

change code to select more classes in javascript

Look at this piece of code:
<script src="progressbar.js"></script>
<script>
var bar = new ProgressBar.Line(containera, {
strokeWidth: 4,
easing: 'easeInOut',
duration: 1400,
color: '#FFEA82',
trailColor: '#eee',
trailWidth: 1,
svgStyle: {width: '100%', height: '100%'},
text: {
style: {
// Text color.
// Default: same as stroke color (options.color)
color: '#999',
position: 'absolute',
right: '0',
top: '30px',
padding: 0,
margin: 0,
transform: null
},
autoStyleContainer: false
},
from: {color: '#FFEA82'},
to: {color: '#ED6A5A'},
step: (state, bar) => {
bar.setText(Math.round(bar.value() * 40));
}
});
bar.animate(1.0);
</script>
The above code selects element with class "containera" and does something with them. I want change my code so it will select bellow classes too:
containerb,containerc,containerd,containere,containerf
but I don't like to repeat my code for every class. I hope you help me :) Thank you.
Why don't you wrap your configuration in a function and call it for every container you have? Could work along those lines:
var yourContainers = ['containerA','containerB']
function createProgressbars = function(container){
return new ProgressBar.Line(container, {
strokeWidth: 4,
easing: 'easeInOut',
duration: 1400,
color: '#FFEA82',
trailColor: '#eee',
trailWidth: 1,
svgStyle: {width: '100%', height: '100%'},
text: {
style: {
// Text color.
// Default: same as stroke color (options.color)
color: '#999',
position: 'absolute',
right: '0',
top: '30px',
padding: 0,
margin: 0,
transform: null
},
autoStyleContainer: false
},
from: {color: '#FFEA82'},
to: {color: '#ED6A5A'},
step: (state, bar) => {
bar.setText(Math.round(bar.value() * 40));
}
});
}
yourContainers.forEach(function(container){
createProgressbars(container).animate(1.0);
});

Categories