GSAP staggerFrom negative delay - javascript

Question:
I am wondering if there is a way to add a negative delay to the staggerFrom / staggerTo functions in greensock?
Problem:
I have animation is running too long for my liking. It would be great if my staggered animations could happen as the previous animations are playing to cut down the duration.
Example:
I have put together this codepen to illustrate what I am after: http://codepen.io/nickspiel/pen/LpepvQ?editors=001
You can see in the codepen that I have used negative delays on the basic from timeline functions but this doesn't work for the staggerForm function as the delay parameter is used to delay each element of the jquery collection.

Have you tried to use the new cycle property introduced in the latest v1.18.0 of GSAP?
So you can cycle with delay but passing 0 as stagger value in the staggerTo calls.
Also, you can pass position parameter to staggerTo calls to make them overlap with the previously inserted tween.
Here is the forked pen for an idea.
JavaScript:
...
animateElement = function() {
timeline.from(main, 0.3, { scaleY: '0%', ease: Back.easeOut.config(1.7) })
.staggerFrom(dataBlocks, 0.3, { cycle: { delay: function(index) {
return index * 0.1;
}}, scale: '0%', y: 100, ease: Back.easeOut.config(1.7) }, 0)
.from(lineGraphLines, 1.5, { drawSVG: 0, ease: Power1.easeOut }, '-=0.5')
.from(lineGraphAreas, 1, { opacity: 0, ease: Back.easeOut.config(1.7) }, '-=2.0')
.staggerFrom(lineGraphDots, 0.2, { cycle: { delay: function(index) {
return index * 0.1;
}}, scale: 0, ease: Back.easeOut.config(1.7) }, 0, '-=1.0')
.staggerFrom(donutCharts, 0.6, { cycle: { delay: function(index) {
return index * 0.1;
}}, drawSVG: 0, ease: Power1.easeOut }, 0, '-=2.0')
.from(menuBackground, 0.3, { scaleX: '0%', ease: Back.easeOut.config(1.7) }, '-=6')
.staggerFrom(menuElements, 0.3, { cycle: { delay: function(index) {
return index * 0.1;
}}, scaleX: '0%', ease: Back.easeOut.config(1.7) }, 0, '-=1')
.from(headerBackground, 0.5, { scaleX: '0%', ease: Power1.easeOut }, '-=5.5')
.staggerFrom(headerBoxes, 0.3, { cycle: { delay: function(index) {
return index * 0.1;
}}, scale: '0%', ease: Back.easeOut.config(1.7) }, 0, '-=1.0')
.staggerFrom(headerText, 0.4, { cycle: { delay: function(index) {
return index * 0.1;
}}, scaleX: '0%', ease: Back.easeOut.config(1.7) }, 0, '-=1.0')
.from(headerText, 0.4, { scaleX: '0%', ease: Back.easeOut.config(1.7) }, '-=4');
};
...
This may not be exactly the type of animation you wanted but you'll need to adjust the position parameter in most/all of your tweens as per your liking but I think the main take away for you would be the use of cycle with delay.
Hope this helps in some way.
P.S. You can pass negative stagger values but they have a different meaning. It tells the engine to start the staggered animation from the last element.

I asked this same question on the Greensock GitHub repo and had the following response:
Absolutely - that's already baked in. I'm not sure if you're using
TweenMax or one of the timeline classes, so I'll show you both:
TweenMax.staggerTo(elements, 1, {x:100, delay:2}, 0.01);
// - OR -
var tl = new TimelineLite();
tl.staggerTo(elements, 1, {x:100, delay:2}, 0.01);
// - OR -
var tl = new TimelineLite();
tl.staggerTo(elements, 1, {x:100}, 0.01, "label+=3");
In my case the last option worked flawlessly.

Related

animation bug on quick mouseover mouseout

Is there any way to fix an animation bug when I quickly mouseover/mouseout an element?
This is the live version of the website - https://daphne-rebuild.netlify.app/
When I quickly hover on and off the circle, it becomes buggy. Happens all the time so I'm really hoping there's some kind of fix.
<div class="hover-circle" #mouseover="hoverCircle" #mouseout="leaveCircle">
<div class="circle"></div>
<span>Enter</span>
</div>
hoverCircle(e) {
gsap.to(".hover-circle .circle", {
duration: 1,
scale: 1.3,
ease: "power4.out"
});
gsap.to(`.home-${this.currentComponent}`, {
delay: 0.1,
duration: 1,
scale: 1.05,
ease: "power4.out"
});
},
leaveCircle() {
gsap.to(".hover-circle .circle", {
duration: 0.5,
scale: 1,
ease: "power4.inOut"
});
gsap.to(`.home-${this.currentComponent}`, {
duration: 0.5,
scale: 1,
ease: "power4.inOut"
});
},
The problem is you placed a delay on hoverCircle. If hoverCircle and leaveCircle happen within less than 0.1s, the delayed animation from hoverCircle will actually get executed after the animation from leaveCircle. Removing the delay will probably fix the problem.
I can't be sure from the code you posted, but you might also want to stop any currently playing animations on your element (both when entering and leaving). To do that, use...
gsap.killTweensOf(target)
..., documented here.
In your example, it would probably look like:
hoverCircle() {
gsap.killTweensOf(".hover-circle .circle");
gsap.to(".hover-circle .circle", {
duration: 1,
scale: 1.3,
ease: "power4.out"
});
gsap.killTweensOf(`.home-${this.currentComponent}`);
gsap.to(`.home-${this.currentComponent}`, {
duration: 1,
scale: 1.05,
ease: "power4.out"
});
},
leaveCircle() {
gsap.killTweensOf(".hover-circle .circle");
gsap.to(".hover-circle .circle", {
duration: 0.5,
scale: 1,
ease: "power4.inOut"
});
gsap.killTweensOf(`.home-${this.currentComponent}`);
gsap.to(`.home-${this.currentComponent}`, {
duration: 0.5,
scale: 1,
ease: "power4.inOut"
});
}
If you have this in multiple places, you might want to refactor into a method called stopAnimations:
hoverCircle() {
this.stopAnimations();
gsap.to(".hover-circle .circle", {
duration: 1,
scale: 1.3,
ease: "power4.out"
});
gsap.to(`.home-${this.currentComponent}`, {
duration: 1,
scale: 1.05,
ease: "power4.out"
});
},
leaveCircle() {
this.stopAnimations();
gsap.to(".hover-circle .circle", {
duration: 0.5,
scale: 1,
ease: "power4.inOut"
});
gsap.to(`.home-${this.currentComponent}`, {
duration: 0.5,
scale: 1,
ease: "power4.inOut"
});
},
stopAnimations() {
gsap.killTweensOf(".hover-circle .circle");
gsap.killTweensOf(`.home-${this.currentComponent}`);
}

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 to a Label in GSAP scrollTrigger scrub Timeline

So I'm having this problem I have a scrollTrigger Timeline and i want a button to scroll to take me to a specific position.
I tried using seek but it didnt work even ScrollTo plugin lack a support for this
I have a gsap Timeline
const tl = gsap.timeline({
paused: true,
scrollTrigger: {
trigger: '.home',
start: 'top top',
end: 'bottom+=1000 top',
pin: true,
scrub: 0.5,
markers: true
},
defaults: {
duration: 2
}
})
// Main home animation
.to(['.left'], {
clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',
ease: 'power1.out'
}, 'scene')
// Name scaling animation
.to(['.name-title'], {
scale: 0,
ease: 'power1.out'
}, 'scene')
.from(document.getElementById('achievements'), {
autoAlpha: 0
}, '-=1.5')
// Achievements reveal animation
.from(document.getElementById('achievements').querySelectorAll('.row'), {
motionPath: {
path: [
{
x: 0,
y: 100
}
]
},
autoAlpha: 0,
stagger: 0.2,
ease: 'power3.out'
}, '-=2')
.addLabel('achievements')
// Hide achievements animation
.to(document.getElementById('achievements').querySelectorAll('.row'), {
motionPath: {
path: [
{
x: 0,
y: -100
}
]
},
autoAlpha: 0,
stagger: 0.2,
ease: 'power3.in'
}, '+=2')
and i have a button in my home that should take me to achievements section but i cant find any proper solution.
So i deviced my own workaround solution (P.S: im using Vue)
scrollTo () {
const pos = Math.ceil(document.body.scrollHeight * (this.loadTl.labels.achievements / this.loadTl.duration()))
gsap.to(window, { duration: 2, scrollTo: pos, ease: 'linear' })
}
loadTl here is a computed property for my Timeline i Found the position of achievements and used ScrollToPlugin to scroll to that pixel.
But im looking for a better way of doing this.

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)

GreenSock TimelineMax starting early

I have been trying to get the animation to fire off on click, I have created the animated via TimelineMax. I can't see why but it keeps firing off the first instance of this animation, there are 5 .blackout-panel and it plays the animation automatically for the first .blackout-panel, but I have no idea why - I want the full animation to be controlled by on click.
var showBarsTL = new TimelineMax({ paused: true, onComplete: killAnimation });
$('.portfolio-panel').on("click", function () {
showBarsTL.play()
});
showBarsTL.add (TweenMax.staggerTo(".blackout-panel", 0, {y:100+ '%', onStart: "", onStartParams:["{self}"], ease: Power1.easeInOut}, 0.1275, 0));
showBarsTL.add (TweenMax.from(".blackout-panel", 0, {css:{top:"auto", bottom:"0"}}));
showBarsTL.add (TweenMax.staggerTo(".blackout-panel", 0, {css:{height:"0", bottom:"0", top:"auto"}, delay: 0.1275, ease: Power1.easeInOut}, 0.1275));
function killAnimation() {
showBarsTL.clear();
}
I have created a codePen to demonstrate this: http://codepen.io/Tekkie/pen/XpBOYV/?editors=1010
Any guidance would be appreciated. Also my killAnimation function doesn't seem to be getting fired.
EDIT: 1 - I have resolved the pausing issue by putting a delay. My new JS is as follows:
var $blackoutPanels = $('.blackout-panel');
var showBarsTL = new TimelineMax({ paused: true });
showBarsTL
.add (TweenMax.staggerTo($blackoutPanels, **0.1275**, {y:100+ '%', onStart: "", onStartParams:["{self}"], ease: Power1.easeInOut}, 0.1275, 0))
.add (TweenMax.from($blackoutPanels, 0, {css:{top:"auto", bottom:"0"}}))
.add (TweenMax.staggerTo($blackoutPanels, 0, {css:{height:"0", bottom:"0", top:"auto"}, delay: 0.1275, ease: Power1.easeInOut}, 0.1275));
$('.portfolio-panel').on("click", function () {
showBarsTL.play();
});
EDIT: 2 - I have resolved all issues using the following JS:
var $blackoutPanels = $('.blackout-panel');
var showBarsTL = new TimelineMax({
paused: true
});
showBarsTL
.add(TweenMax.staggerFrom($blackoutPanels, 0, {
yPercent: -100,
top: -100 + "%",
onStartParams: ["{self}"],
ease: Power0.easeInOut
}, 0, 0))
.add(TweenMax.staggerTo($blackoutPanels, 0.3, {
yPercent: 0,
top: 0,
onStartParams: ["{self}"],
ease: Power1.easeInOut
}, 0.125, 0))
.add(TweenMax.to($blackoutPanels, 3, {
yPercent: +100,
top: +100 + "%",
onStartParams: ["{self}"],
delay: 0.3,
ease: SlowMo.ease.config(0.4, 1, false)
}, 0.3, 0))
.add(TweenMax.to($blackoutPanels, 0, {
yPercent: -100,
top: -100 + "%",
delay: 0.375
}));
$('.portfolio-panel').on("click", function() {
showBarsTL.restart(false, false);
});
Try to set the pause like this:
var showBarsTL = new TimelineMax({onComplete: killAnimation }).paused(true);
and in your function:
$('.portfolio-panel').on("click", function () {
showBarsTL.paused(false);
});

Categories