I use TweenMax to animate divs with multiples .mouseover but I'd like one to complete before starting another one.
In this JSFiddle, you can see the divs overlap if going to fast on the links.
Is there an easy solution for this ?
$(document).ready(function() {
var blocPrototypo = $("#wrap-image-menu");
$("#prototypo").mouseover(function() {
TweenLite.to(blocPrototypo, 1.4, {
backgroundColor: "#24d390",
ease: Circ.easeInOut
});
TweenMax.to(blocPrototypo, 0.5, {
width: "39vw",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
var allExcept = $(".all-img-menu").not(document.getElementById("img-prototypo"));
TweenMax.to(allExcept, 0.9, {
left: "0px",
opacity: 0
});
TweenMax.to($("#img-prototypo"), 0.7, {
opacity: "1",
width: "55vw",
left: "-90px",
ease: Expo.easeOut,
delay: "0.65"
});
TweenMax.to($("#line-pagination"), 0.5, {
width: "76px",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
$("#current-page").fadeOut(function() {
$(this).text("01").fadeIn(1000);
});
});
$("#esadvalence").mouseover(function() {
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut
});
TweenMax.to(blocPrototypo, 0.5, {
width: "39vw",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
var allExcept = $(".all-img-menu").not(document.getElementById("img-esadvalence"));
TweenMax.to(allExcept, 0.9, {
left: "0px",
opacity: 0
});
TweenMax.to($("#img-esadvalence"), 0.7, {
opacity: "1",
width: "55vw",
left: "-90px",
ease: Expo.easeOut,
delay: "0.65"
});
TweenMax.to($("#line-pagination"), 0.5, {
width: "76px",
ease: Circ.easeInOut,
repeat: 1,
yoyo: true
});
$("#current-page").fadeOut(function() {
$(this).text("02").fadeIn(1000);
});
});
});
TweenLite and TweenMax have onComplete callbacks that can be used to await completion prior to beginning the next operation:
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut,
onComplete: function() {
// perform next operation
}
});
There is also params you can pass via onCompleteParams, which you could perhaps use to indicate to some generic function the next operation you'd want to execute.
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut,
onCompleteParams: [{ prop1: value1, prop2: value2 }],
onComplete: function(someParams) {
// perform next operation using passed params
}
});
Another approach could be using Promise or jQuery Deferred in combination with the onComplete event callback such as:
function foo() {
return new Promise(function(resolve, reject) {
TweenLite.to(blocPrototypo, 1.5, {
backgroundColor: "#e12a1c",
ease: Power1.easeOut,
onComplete: function() {
return resolve(true);
}
});
});
}
foo().then(function() {
// perform next operation
});
Hopefully that helps!
Related
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}`);
}
I'm new to barba.js so just trying to get my head round it.
When a page transition is performed, my browser page scroll stays where it was. Is this intentional?
Is it possible to have it so after the transition the next page is loaded at the top like normal?
Here is my code
function pageTransition() {
var tl = gsap.timeline();
tl.to('ul.transition li', { duration: .5, scaleY: 1, transformOrigin: "top left", stagger: .2 })
tl.to('ul.transition li', { duration: .5, scaleY: 0, transformOrigin: "top left", stagger: .1, delay: .1})
}
function contentAnimation() {
var tl = gsap.timeline();
tl.from('.hero', { duration: 1, translateY: 50, opacity: 0 })
}
function delay(n){
n = n || 2000;
return new Promise(done => {
setTimeout(() => {
done();
}, n)
});
}
barba.init({
sync: true,
transitions: [{
async leave(data) {
const done = this.async();
pageTransition();
await delay(500);
done();
},
async enter(data) {
contentAnimation();
},
async once(data) {
contentAnimation();
}
}]
});
After checking through the docs again I found the hooks.
barba.hooks.enter(() => {
window.scrollTo(0, 0);
});
This solves the issue if anyone else is having the same issue.
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)
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);
});
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.