I'm new to ScrollMagic so not sure if there's something obvious that I'm overlooking.
I've set up a codepen which illustrates what I'm trying to achieve, and an alternative version that almost does what I want, except there's a huge gap that I can't seem to get rid of.
Essentially looking to have 3 slides that are stacked on top of each other. When the user scrolls, the slides one-by-one transition up to reveal the one underneath.
When scrolling past the final slide, the remaining content on the page should scroll up as if it's attached to the bottom of the final slide, and should from then on function like a normal page.
Currently, the container with all the slides in it transparently overlaps the rest of the body content until the final slide has disappeared off the top of the viewport, and it's ScrollMagic that is doing this.
The pink bar in the codepen is intended to show where the bottom of the slide container finishes.
Here's the relevant code:
function initController() {
controller = new ScrollMagic.Controller({
globalSceneOptions: {
triggerHook: "onLeave"
}
});
controller.scrollTo(function(pos) {
TweenMax.to(window, 1, {
scrollTo: {
y: pos,
autoKill: true
}
});
});
}
function initAnimation() {
wipeAnimation = new TimelineMax();
$.each(ui.slides, function(i, slide) {
wipeAnimation
.add(TweenMax.to(slide, 2000, {y: '0'}))
.add(TweenMax.fromTo(slide, 5000, {y: '0'}, {
y: '-100%',
onComplete: function() {
if (i < ui.slideCount - 1) { // don't run on last slide
updateActiveSlide(ui.slides[i + 1]); // activate next slide
}
},
onReverseComplete: function() {
updateActiveSlide(slide);
}
}));
});
}
function initScene() {
scene = new ScrollMagic.Scene({
triggerElement: ui.el,
duration: '100%'
})
.setTween(wipeAnimation)
.setPin(ui.el, {
pushFollowers: false
})
.addTo(controller);
}
This is designed by default to stop the pinned elements overlapping the following elements in the document.
You need set the 'pushFollowers' property to false:
function initScene() {
scene = new ScrollMagic.Scene({
triggerElement: ui.el,
duration: '100%'
})
.setTween(wipeAnimation)
.setPin(ui.el, {
pushFollowers: false
})
.addTo(controller);
}
See documentation: http://janpaepke.github.io/ScrollMagic/docs/ScrollMagic.Scene.html#setPin
// pin element and push all following elements down by the amount of the pin duration.
scene.setPin("#pin");
// pin element and keeping all following elements in their place. The pinned element will move past them.
scene.setPin("#pin", {pushFollowers: false});
Working example: https://codepen.io/alexgill/pen/MyOMKP (forked from your codepen)
Related
I am using Bodymovin in combination with ScrollMagic and GSAP to animate through series of images as you scroll back and forth. Everything works great, except, when I reach the end it doesn't stay on the final image, but goes white.
Libraries that I load first:
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.9/lottie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>
Then my code:
var controller = new ScrollMagic.Controller();
var animation = bodymovin.loadAnimation({
container: document.getElementById('hero-anim'),
animationData: animationframes,
renderer: 'svg',
autoplay: false,
});
var tl = new TimelineMax();
tl.to({frame:0}, 1, {
frame: animation.totalFrames-1,
onUpdate:function(){
animation.goToAndStop((Math.round(this.progress() * 60)), true)
},
ease: Linear.easeNone
})
var lottieScene = new ScrollMagic.Scene({
duration: '100%',
offset: 600
})
.setPin("#hero-anim")
.setTween(tl)
.addTo(controller);
Any ideas what could be causing that? Looking at browser's Inspect Element, it basically adds display:block to the image that is supposed to be currently visible and applies display:none to the previous image, but at the end, they all have display:none, and it doesn't stay visible
RESOLVED
As Zach pointed out, I tried Math.floor and at first it didn't help, but afterwards I tried adjusting animation.goToAndStop((Math.floor(this.progress() * 60)), true) to * 59 (one frame less than the total frame count and now it works, perfectly. Math.round with * total frame count or even * total frames - 1 didn't work, strangely.
I'm trying to use ScrollMagic to control a TimelineMax which moves the playhead of a Lottie animation.
So, as the user scrolls, the animation plays relative to the speed and direction of the scroll. I'm so close and need a little help to bring the effect home.
First, I include my Libraries
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.9/lottie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>
Then...
// init scrollmagic
var controller = new ScrollMagic.Controller();
Now, the animation setup...
// Manage Animation
var animation = bodymovin.loadAnimation({
container: document.getElementById('lottie'), // Required
path: '/static/animations/animation.json', // Required
renderer: 'svg', // Required
loop: false, // Optional
autoplay: false, // Optional
name: "Welcome to Awesomeness", // Name for future reference. Optional.
});
This is where I'm struggling:
// Setup Timeline
var lottieControl = new TimelineMax({ repeat: -1, yoyo: true }); // <-- don't loop and let the timeline go back and forth
lottieControl.to({ frame:0 }, 1, { // <-- is this right? I'm telling the timeline to start at frame 0
onUpdate:function(){
animation.goToAndStop(Math.round(this.target.frame), true) // <-- move the playback head of the animation to the target frame that has been rounded and use frames not time (true)
},
ease:Linear.easeNone
})
Finally, bring it all back together...
// Attach to scroll
var lottieScene = new ScrollMagic.Scene({
duration: '80%',
offset: 1
})
.setPin("#header-scroll")
.setTween(lottieControl)
.addTo(controller);
For the life of me, I can't see if I'm using the goToAndStop method right or not. Thanks for your time.
After hours of testing I found the answer. I was looking at the wrong thing. I needed to tie the timeslines progress to the frame to go to. In this instance, there are 300 frames, thus the multiplication by the number of frames in the animation.
Here's the revised code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.9/lottie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>
<script>
// init scrollmagic
var controller = new ScrollMagic.Controller();
// Manage Animation
var animation = bodymovin.loadAnimation({
container: document.getElementById('lottie'), // Required
path: '/static/animations/scroll_animation.json', // Required
renderer: 'svg', // Required
loop: false, // Optional
autoplay: false, // Optional
name: "Welcome to Awesomeness",
});
// Setup Timeline
var tl = new TimelineMax();
tl.to({frame:0}, 1, {
frame: animation.totalFrames-1,
onUpdate:function(){
animation.goToAndStop((Math.round(this.progress() * 300)), true)
},
ease: Linear.easeNone
})
// Attach to scroll
var lottieScene = new ScrollMagic.Scene({
duration: '100%',
offset: 1
})
.setPin("#header-scroll")
.setTween(tl)
.addTo(controller);
</script>
So I have this GSAP timeline, which should first animate the fade-in text, and onComplete it should trigger the Vivus.js constructor which it does. However, the SVG element is visible before the animation occurs which is not a desired effect. I have tried to manipulate it somehow but the issue is still here - what could I be missing..?
The desired effect would be to fade in while drawing itself..
Here's a pen: https://codepen.io/anon/pen/ELGawo
function initialAnimation() {
var introText = $(".text-intro"),
tlIntro = new TimelineLite({ onComplete: introFadeIn });
tlIntro.from(introText, 1, { autoAlpha: 0 });
}
// Fade in and draw elements
function introFadeIn() {
var graphic1 = $(".graphic1");
tlIntrofadeIn = new TimelineLite({ onComplete: gr1Animate });
tlIntrofadeIn
.from(graphic1Elem, 1, { autoAlpha: 0 });
}
function gr1Animate() {
new Vivus(
"gr1",
{
type: "delayed",
onReady: function(myVivus) {
myVivus.el.style.visibility = "inherit";
}
},
function(obj) {
obj.el.style.visibility = "visible";
}
);
}
initialAnimation();
I'm not familiar with Vivus, but GSAP has a tool (DrawSVGPlugin) that does the same thing (and much more) as a Club GreenSock benefit and it integrates seamlessly, so your 30-ish lines of code could be condensed to 3:
https://codepen.io/GreenSock/pen/de8f2fa2a6813213d0e258113b2b15bd/?editors=0010
var introTL = new TimelineLite({delay:0.5});
introTL.from(".text-intro, #gr1 circle, #gr1 text", 1, {autoAlpha:0})
.from("#gr1 path", 2, {drawSVG:"0%", autoAlpha:0});
If you have any other questions, I'd encourage you to check out the GSAP forums at https://greensock.com/forums/. It's a fantastic community (not that Stack Overflow isn't - it's just that the GreenSock forums are totally dedicated to GSAP-related questions). Happy animating!
I'm working on a project with a push menu. When the content div slides over, the menu buttons have a little animation as they enter the screen. It doesn't take long or anything but the issue I'm having is if the user opens and closes the menu quickly a bunch of times in a row, the items on the list begin disappearing and reappearing in the wrong order. I think this is because the new animation calls are canceling out old ones and in general screwing up the order of things.
Ideally, I'd like the animations to always behave properly (i.e. if the menu is opening, clear all previous animations and play the opening animation only).
But I'd be satisfied if I could at least get each element to queue its animations properly so that the elements in the menu don't randomly disappear upon opening the menu.
Here is a fiddle of the problem:
http://jsfiddle.net/9t10zr6m/1/
The top div is transparent because it normally has a background image. And also because I thought it might be easier to see the problem with the menu if you could see what was going on underneath the top div.
here is the relevant jQuery code:
$(document).ready(function() {
$(".expandable-content").on('click', function(){
$(this).children(".internal-content").slideToggle();
});
$(".menu-button").on('click', function(){
var position = $(".blue-box").css("left");
if( position == "0px") {
$(".blue-box").velocity({left: "250px"}, 500);
$('.side-nav-link').finish();
$('.side-nav-link').velocity('transition.slideUpIn', { stagger: 50 });
} else {
$(".blue-box").velocity({left: "0px"}, 500);
$('.side-nav-link').finish();
$('.side-nav-link').velocity('transition.slideDownOut', { stagger: 50 });
}
});
});
and the relevant html:
<div class="blue-box">
<h1>Julian Ptak</h1>
<h2>Kempis Coder. Simplicity. Purity.</h2>
<div class="menu-button"><img class="button-icon" src="img/menu-icon.png"><p class="button-text">Menu</p></div>
</div>
<div class="red-box">
<ul class="side-nav">
<li class="side-nav-link">Home</li>
<li class="side-nav-link">Work</li>
<li class="side-nav-link">Hobbies</li>
<li class="side-nav-link">Writings</li>
<li class="side-nav-link">Code</li>
<li class="side-nav-link">Contact</li>
</ul>
</div>
How do you make jQuery enqueue animations? Or only play the right animation for the right click and skipping all previous ones?
I tried .finish() and .stop() but neither seemed to fix my problem. Any ideas? Do those not work with velocity.js?
Very long question!!
I don't get what you want, but according to some keywords in your question i will give you some Velocity features:
Velocity add animations to its queue by default!
$elm.velocity({ /* 1 */ },{});
$elm.velocity({ /* 2 */ },{});
in this example at the end of first animation second animation will start.
$elm.velocity({ /* 1 */ },{});
$elm.velocity({ /* 2 */ },{queue: false});
in this example both animations start together.
$elm.velocity('stop', true).velocity({ /* 1 */ },{});
in this example velocity('stop', true) clear the $elm queue then next animation immediately start.
Be careful to use the delay param for velocity
for example :
jQuery(function($){
var $pen = jQuery('#pen');
$arrow1 = jQuery('#arrow1');
$arrow2 = jQuery('#arrow2');
$pdf = jQuery('#pdf');
$screen = jQuery('#screen');
$pen
.velocity("fadeIn", {
duration: 1500,
complete:function(elements){
$arrow1.velocity("fadeIn", { duration: 1500});
}
});
});
The callback start after the end of the animation
in the meantime
$pen.velocity("fadeIn", { duration: 1500 });
$arrow1.velocity("fadeIn", { duration: 1500});
});
the two animations start at the same time, so if you want a timeline, write a delay for start after the end of the first animation
$pen.velocity("fadeIn", {
duration: 1500
});
$arrow1.velocity("fadeIn", { duration: 1500,delay:1500});
});
Nevermind with the ui Pack you've a great explanation here
Ui pack sequence
But for make the same thing, you can't use "fadeIn" directly
here we go
var $pen = jQuery('#pen');
$arrow1 = jQuery('#arrow1');
$psd = jQuery('#psd');
$arrow2 = jQuery('#arrow2');
$screen = jQuery('#screen');
var sequenceFade = [
{ e: $pen, p: { opacity: 1 , display: "block"}, o:{duration :1500}},
{ e: $arrow1, p: { opacity: 1 , display: "block"}, o: {duration: 1500}},
{ e: $psd, p: { opacity: 1 , display: "block"}, o: {duration: 1500}},
{ e: $arrow2, p: { opacity: 1 , display: "block"}, o: {duration: 1500}},
{ e: $screen, p: { opacity: 1 , display: "block"}, o: {duration: 1500}}
];
$.Velocity.RunSequence(sequenceFade);
How would one make a slide and fade in animation like in seen in the green and pink boxes on sharethis.com? In particular I like the one in the blue box with the arrows. Are there a set of JavaScript codes or CSS effects?
The easiest would probably be to use a library like this:
http://janpaepke.github.io/ScrollMagic/
I see that for the arrows effect the css-height property is animated when you scroll. This is done in javascript. But you can also achieve this effect through CSS3-transitions.
Update: Slide and wipe effects from the demo page:
// ani
var pinani = new TimelineMax()
// wipe
.add(TweenMax.to("#wipe", 1, {
width: "100%"
}))
// slide
.add(TweenMax.to("#slide", 1, {
top: "0%",
ease: Bounce.easeOut,
delay: 0.2
}));
// pin
new ScrollScene({
triggerElement: "section#pin",
duration: 1100
})
.on("progress", function () {
// keep centered even though width changes
$("#wipe h3").width($("#pin>h3").width());
})
.setTween(pinani)
.setPin("section#pin")
.addTo(controller);