Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...

Search the Community

Showing results for 'overwrite'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Personal Website


Twitter


CodePen


Company Website


Location


Interests

  1. Hi guys! I come humbly in front of you with few drops of hope left, after 5 full days of switching between possible solutions to get a consistent ScrollTrigger behavior on a Gatsby site. Getting directly to you is my last resort, as every google and gsap forum link regarding ScrollTrigger and Gatsby is already visited. 😒 I cannot get a CodePen reproducing the exact issue so I'll try my best to describe it here. Shortly, the problem seems to be, as I suspect, that the ScrollTrigger does not refresh itself when Javascript pops into the browser on top of the SSR-ed html/css bundle. Here's what i did. I created several projects with different versions for dependencies, but i will stick to the simplest one with all dependencies up to date.It's a gatsby with material-ui plugin added, who's exact structure can be found here: https://github.com/mui-org/material-ui/tree/master/examples/gatsby There are no other plugins added, nor any other configs/plugins changed. I rendered the component that will contain the ScrollTrigger (AboutBlock) in the AboutPage page: about.js const AboutPage = () => { return ( <AboutBlock /> ) } export default AboutPage This is the component where i try to animate some elements on reveal when scrolled into view: aboutBlock.js import gsap from "gsap"; import ScrollTrigger from 'gsap/ScrollTrigger'; import animateReveal from "./gs_reveal"; export default function AboutBlock() { gsap.registerPlugin(ScrollTrigger) const revealRefs = useRef([]) revealRefs.current = [] useLayoutEffect(() => { let scrollTriggers = [] scrollTriggers = animateReveal(revealRefs.current) return () => { scrollTriggers.forEach(t => t.kill(true)) } }, []); const addToRevealRefs = el => { if (el && !revealRefs.current.includes(el)) { revealRefs.current.push(el); } }; return ( <Grid container> <Grid item width={{ xs: '100%', sm: '80%', md: '35%' }} pl={{ xs: 0, md: '2.5%' }} mt={{ xs: 60, sm: 0 }}> <Grid container direction="column" alignItems={{ xs: "flex-start", sm: "flex-end" }}> <Grid item mt={{ xs: 0, md: '10vh' }} id="acum"> <Typography variant="h5" textAlign={{ xs: "left", sm: "right" }} ref={addToRevealRefs} className='gs_reveal_fromRight'> NOW WE ARE IN </Typography> </Grid> <Grid item> <Typography variant="h6" textAlign={{ xs: "left", sm: "right" }} ref={addToRevealRefs} className='gs_reveal_fromRight'> LOCATION </Typography> </Grid> <Grid item mt="10vh" id="hi"> <Typography variant="h5" textAlign={{ xs: "left", sm: "right" }} ref={addToRevealRefs} className='gs_reveal_fromRight'> SAY HI </Typography> </Grid> <Grid item className='toughts'> <Typography variant="h6" textAlign={{ xs: "left", sm: "right" }} ref={addToRevealRefs} className='gs_reveal_fromRight'> TELL US YOUR THOUGHTS </Typography> </Grid> </Grid> </Grid> </Grid> } HTML is longer and crowded, I left a part to get the idea of the structure and styling approach (MUI's sx - emotion). And finally, this is the animateReveal function: gs_reveal.js import ScrollTrigger from 'gsap/ScrollTrigger'; import gsap from 'gsap'; export default function animateReveal(elements) { const triggers = [] elements.forEach(function (elem) { hide(elem) let tr = ScrollTrigger.create({ trigger: elem, id: elem.id, end: 'bottom top', markers: true, onEnter: function () { animateFrom(elem) }, onEnterBack: function () { animateFrom(elem, -1) }, onLeave: function () { hide(elem) } }); triggers.push(tr) }); return triggers; } function animateFrom(elem, direction) { direction = direction || 1; let x = 0, y = direction * 100; if (elem.classList.contains("gs_reveal_fromLeft")) { x = -100; y = 0; } else if (elem.classList.contains("gs_reveal_fromRight")) { x = 100; y = 0; } else if (elem.classList.contains("gs_reveal_fromBelow")) { y = -100 } elem.style.transform = "translate(" + x + "px, " + y + "px)"; elem.style.opacity = "0"; gsap.fromTo(elem, { x: x, y: y, autoAlpha: 0 }, { duration: 1.25, x: 0, y: 0, autoAlpha: 1, ease: "expo", overwrite: "auto", delay: elem.classList.contains("gs_delay") ? 0.3 : 0, }); } function hide(elem) { gsap.set(elem, { autoAlpha: 0 }); } The ScrollTrigger markers are misplaced when page loads, and might move (get more misplaced) on hard reloading page, depending on the current scroll position in the moment of reloading, even though the scroll position is not preserved on reload (always is scrolled on top). - The markers are placed on the correct position on resizing, as expected. I followed gsap official docs on react and react-advanced and tried: grabbing the html elements to animate on scroll inside animateReveal() by let elements = gsap.utils.toArray(".gs_reveal"); Assigning to each element a useRef() and use the .current value for each in animateReveal() grabbing html elements using gsap's selector utility gsap.utils.selector changing to simpler animation on scroll, like just a fade refreshing ScrollTrigger in different moments useLayoutEffect(() => { ScrollTrigger.refresh(true) // or ScrollTrigger.refresh() ... }, []); 6. Lifting ScrollTrigger logic to parent about.js page 7. Assigning scrollTrigger to a timeline triggered by the to-be-reveal element 8. Use useEffect() instead of useLayoutEffect() (recommended anyway for ScrollTrigger) 7. Other who-knows-what unsuccessful twists. I suspected a rehydration error, when the static generated code does not match the client side one. But the only JS that could cause a mismatch is the gsap related one, and it does not seem an SSR issue. I checked if the CSS and HTML elements are being properly SSR-ed, by preventing JS from running in the browser. All looking fine. This is both a SSR issue (gatsby build) and a development issue (no SSR). As i said on point 5, setting a ScrollTrigger.refresh() when component is mounted does not work, but delaying this with a 1-2 seconds in a setTimeout successfully solves the issue useLayoutEffect(() => { setTimeout(() => { ScrollTrigger.refresh(true) }, 2000); }, []); This is hard to be accepted as a solution, since i cannot rely on a fixed value to 'guess' when DOM is properly rendered in the eyes of the ScrollTrigger, not to mention the glitches that might occur. So, the question is 'WHY?', why animating with ScrollTrigger from within useLayoutEffect, which is not triggered on the server anyway and should mark the 'component is successfully mounted' moment, seems to not wait for the DOM being completely painted, even though nothing is generated dynamically! There are quite of threads on this forum regarding gatsby, and none seemed to have a clear cause-outcome-solution. Is this battle lost, should i move on? Do you have any suggestions? Thanks so much for your time reading this, it means so much to me!
  2. @OSUblake for the case of Angular, I have the refresh in the OnInit hook method. This is just after registering the plugins and before using the scrollTo plugin. A brief below. ngOnInit() { gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollToPlugin); ScrollTrigger.refresh(); // refresh here ... gsap.to(window, { scrollTo: { y: this.triggers[i].trigger, autoKill: false }, overwrite: true, duration: 1, ease: 'expo.out' ... }); } I am guessing the ScrollTrigger.refresh() worked for my case because the elements used with the scroll plugin are the triggers on my ScrollTrigger config. For context, It is for the implementation I was assisted with here:
  3. Hi all, I cannot find what is causing my GSAP in Safari is buggy , laggy, choppy... well what else word can I describe haha. One of the forum links itself looks laggy in Safari. I attached the mp4 video file: left - chrome, right - safari. Here is my GSAP code for reference: function animateFrom(elem, direction) { direction = elem.getAttribute("data-reveal-direction") ?? 0.2; const delay = elem.getAttribute("data-reveal-delay") ?? 0; let x = 0, y = direction * 100; if (elem.classList.contains("js-reveal_fromLeft")) { x = -100; y = 0; } else if (elem.classList.contains("js-reveal_fromRight")) { x = 100; y = 0; } console.log("x: ", x); console.log("y: ", y); // gsap.set(elem, { x: x, y: y, autoAlpha: 0 }); elem.style.transform = "translate(" + x + "px, " + y + "px)"; elem.style.opacity = "0"; const tl = gsap.timeline({ scrollTrigger: { trigger: elem, once: true, markers: true, }, }); tl.to(elem, { duration: 1.25, x: 0, y: 0, autoAlpha: 1, ease: "power2.out", // overwrite: "auto", delay: delay, }); } function hide(elem) { gsap.set(elem, { autoAlpha: 0 }); } // Scroll reveal gsap.utils.toArray(".js-reveal").forEach(function (elem) { animateFrom(elem); }); gsap-safari-laggy.mp4
  4. Just one quick [untested] idea: liveSnap: {x: (value) => { // animate to the snapped value gsap.to(target, {x: gsap.utils.snap(100, value), duration: 0.3, overwrite: true}); // but return the unsnapped value return value; }},
  5. tldr. it feels that possibly timelines due to their nature of taking in element values straight away (not at the time of playing), might be the cause of glitch because values are constantly changing especially that its 3 timelines controlling elements. I am trying with overwrite both auto and true, but it's not working correctly. There are two problems in which focusing on the second is more important. The first one is the issue of inputing, in this case hovering, as Blake mentioned. There are methods to solve this, one being what Blake suggested. The result should strive to avoid triggering, hovering on/off , in a consecutive way. Here especially as hovering off reverses() the timeline. The second is really the overwriting issue. What seems a viable solution to me possibly presents a cornerstone mechanics for interactive control of multiple elements. On each hover, a timeline is created with .to(). At that moment timeline renders and picks up the current values in order to create the interpolation. And it starts interpolating. On a new hover, the same thing is repeated, the then-active timeline is paused/killed, and a new one is taking over. This seems plausible. In terms of Promise.All, it seems to me that it is better suited for chaining timelines/animations. Here it is taking over method, for multiple elements at the same time. Is this possibly not well suited for GSAP timelines? I pondered possibly trying to do this with JS/classes/CSS/transition. A bigger overkill would be canvas I presume.
  6. If you're animating the same properties in different timelines you can use overwrite:true Possibly even https://greensock.com/docs/v3/GSAP/Timeline/invalidate() Scaling/moving on hover is always a tricky one though because inevitably you'll find a spot whilst hovering where the element will get 'stuck' scaling up and down and in and out of your mouse 'hit area' Blake will be able to advise more on the react side of things.
  7. Hi, I would like to reset the scroll position before navigating away from a page. ScrollPlugin seems to remember the position from the previous page when on a new page. I have a sample usage: gsap.to(window, { scrollTo: { y: this.triggers[i].trigger, autoKill: false }, overwrite: true, duration: 1, ease: 'expo.out' });
  8. Hi all, In the CodePen I've set up a basic toggle that opens and closes a div. As part of the animation, the padding also expands from 0px to 20px. The 20px is however currently hard coded. What's the best way to store the divs original padding and refer to this for future animations? As currently if for example the button is toggled before the animation completes, the padding would return for example 10px if it wasn't hard coded. I was wondering if gsap had any way to store such properties? Or just storing it in a data attribute on the div is a fine solution.
  9. Well, CodePen has been down for a while so I can't provide a forked demo but here's how I'd do the JS: let n = 15; let parallax = []; // we'll store the animations in here. let clamp = gsap.utils.clamp(0, 1); let currentX = 0; let snap = gsap.utils.pipe(gsap.utils.snap(450), gsap.utils.clamp(n * -450, 0)); // Set #slides width for draggable bounds gsap.set('#slides', {width:n*450}); // Populate slide boxes for (var i=0; i<n; i++){ var box = document.createElement('div'), img = new Image(), link = document.createElement('a'); gsap.set(box, { width:400, height:600, overflow:'hidden', position:'absolute', top:50, left:i*450, attr:{ class:'box b'+i }, background:'#333' }); gsap.set(img, { position:'absolute', left:-300,//-i*50, attr:{src:'https://picsum.photos/id/'+(i+10)+'/700/600/'} }); parallax[i] = gsap.to(img, {x: 300, ease: "none", paused: true}); gsap.set(link, { position:'absolute', textAlign:'center', width:105, height:70, paddingTop:'7px', top:490, left:-25, rotation:90, fontSize:'45px', color:'#000', background:'#fff', mixBlendMode:'lighten', textDecoration:'none', innerHTML:'<span style="font-size:20px">IMG </span>'+(i+1), attr:{ class:'imgLink', href:'https://picsum.photos/id/'+(i+10)+'/700/600/', target:'_blank' }, }); box.appendChild(img); box.appendChild(link); slides.appendChild(box); } // Make #slides draggable Draggable.create('#slides', { type:'x', bounds: {left: innerWidth/2, width:1}, zIndexBoost: false, onDrag:updateParallax, inertia: true, onRelease: function() { currentX = this.endX }, onThrowUpdate: updateParallax, snap: snap }) function updateParallax() { // parallax should start from the right edge of the screen and we know that the #slides starts with its left edge centered, thus we add innerWidth/2 let x = gsap.getProperty('#slides', 'x') + window.innerWidth / 2, // convert the position in the viewport (right edge of viewport to -400 because that's when the right edge of the element would go off-screen to the left) into a progress value where it's 0 at the right edge and 1 when it reaches the left edge normalize = gsap.utils.mapRange(window.innerWidth, -400, 0, 1); // apply the clamped value to each animation parallax.forEach((animation, i) => animation.progress(clamp(normalize(x + i * 450)))); } updateParallax(); // Update draggable bounds onResize window.addEventListener('resize', ()=>{ Draggable.get("#slides").applyBounds({left: innerWidth/2, width:1}) }); // Previous & next buttons $('#prev, #next').on('click', function(e) { let nextX = snap(currentX + (e.currentTarget.id === "next" ? -450 : 450)); if (nextX !== currentX) { gsap.to("#slides", {x: nextX, duration: 0.3, onUpdate: updateParallax, overwrite: true}) currentX = nextX; } }); $('#prev, #next').on('mouseenter', (e)=>{ gsap.to('#'+e.currentTarget.id + ' circle', {attr:{r:22}, ease:'expo', overwrite: true}) }); $('#prev, #next').on('mouseleave', (e)=>{ gsap.to('#'+e.currentTarget.id + ' circle', {attr:{r:20}, ease:'expo', overwrite: true}) }); // Img Link rollover/out behavior $('.imgLink').on('mouseenter', (e)=>{ gsap.to(e.currentTarget, {x:10, duration:0.3, ease:'power3', overwrite: true}) }); $('.imgLink').on('mouseleave', (e)=>{ gsap.to(e.currentTarget, {x:0, duration:0.3, ease:'power4.inOut', overwrite: true}) }); The general idea is: You only want the parallax effect to exist while each individual element is inside the viewport (not the entire movement of the #slides). I created a simple linear animation of x from 0 to 300 for EACH element. Paused. Dumped them into an Array. The updateParallax() function loops through each one and sets the progress() according to its position (which we know because they're 450px apart). It's all based on the viewport so that progress would be 0 when it's on the far right edge of the screen and 1 when the element's right edge reaches the left edge of the viewport. I also made the following improvements: I applied inertia with snapping directly on the draggable so it's super smooth and users can flick it. The logic in the next/previous buttons allows users to click the buttons quickly and it still works (instead of ignoring clicks while animation is running). Sorry, that's my pet peeve when the interface ignores user clicks. I hope that helps, Tom!
  10. Welcome to the forums, @inkorporation I read your question a few times and I'm sorry but I don't understand what you're asking. Can you please try to explain? What exactly isn't "fluid"? I didn't see any jerky animation. But I did see very pixelated letters on some devices because you're scaling up so much. Also, do NOT enable scroll-behavior: smooth in your CSS because it interferes with ScrollTrigger functionality. I would also recommend setting overwrite: true because if you scroll quickly back and forth, you may be creating conflicting tweens since you're doing them dynamically inside the onEnter. expoScale() won't work if you don't load the EasePack, just so you know. That's one of the few eases that aren't in the core. Lastly, I don't see why you're doing y: "0%" - you never animate y to any other value, right? And if your goal is to use percentage-based "y" values, it's typically best to use yPercent instead of y so that you can layer them properly. I suspect you might be better off creating ONE timeline that contains all the [staggered] animations and just hook that one timeline up to one ScrollTrigger with scrub: true (or a number).
  11. I'm still pretty fuzzy on what you're asking here, but here are a few comments in case they're helpful: When you set overwrite: true on a tween, that overwriting logic only runs ONCE, immediately when you create the tween. It basically says "find all other tweens of the same target and kill them right now." With overwrite: "auto", it runs once the first time the animation renders and it finds only other active tweens of the same target(s) and kills only the individual conflicting properties. It would be very very bad for performance if it tried to run the overwrite logic every single time that animation rendered/played. We obsess about performance around here because animation is the most performance-sensitive part of UX. It sounds like maybe you're expecting that every time you .play() one timeline, it will kill any other timelines but that is NOT true. When something is overwritten, it's dead. Gone. It doesn't come back later. The first time a tween renders, it records the start/end values so that it can very quickly interpolate between them. Again, that happens ONCE. It sounds like you might be expecting that to happen every time you play() a timeline. So, for example, if box.x is 0 and then you have a .to(box, {x: 100}) in a timeline, the first time it renders it will record "okay, we're going from 0 to 100. Lock that in". So now every time that timeline plays again, it ALWAYS goes from 0 to 100. However, let's say you set box.x to 200 somewhere else in your code and then you .play() this timeline again - you shouldn't expect it to animate from 200 to 100 (instead of 0 to 100) because it already locked in the starting value of 100. You can .invalidate() a tween/timeline to force it to flush any pre-recorded values and then it'll re-record them the next time it renders. If you have multiple timelines that affect the same properties of the same targets, you can easily prevent them from conflicting by calling .pause() on the ones that shouldn't be playing, and .play() the one that should. Again, if you want them to flush any recorded starting values, just call .invalidate(). I cannot imagine why it would take days to set up a minimal demo - all we need is the most basic possible illustration. Hopefully that wouldn't take more than 10 minutes. Maybe 2 buttons and 2 timelines with a couple of tweens in each that affect a single <div> or something(?) We're eager to help and I'm sure that minimal demo will go a long way to allowing us to get you the answer you're looking for.
  12. I was looking at this reference link here: // Set overwrite on a tween gsap.to(".line", { x: 200, overwrite: true }); // Set overwrite globally for all tweens gsap.defaults({ overwrite: true }); // Set overwrite for all tweens in a timeline const tl = gsap.timeline({ defaults: { overwrite: true } }); Maybe I need to set the overwrite in a difference place than a the begining of the tween. Basically seems like the timelines are conflicting each other, and not terminating the movements of the previous timeline. There are other problems , but it is better to keep them in a separate thread.
  13. Looks to me, like what you are actually doing with that tween is to tween him to his final position on the y-axis again, which is below/outside of that section and since you have overwrite set to true that will overwrite any other tweens on that parachute-guy, thus he won't be visible anymore after that. Since you have a scrub set, the timeline will play in reverse anyway when scrolling back up, so I do not see a real necessity for that tween. Some sidenotes: the toggleActions you have in that same scrollTrigger does not do anything. toggleActions and scrub are exclusive properties, they don't work alongside each other. When you have both set, the scrub will always be the one taking control of your scroll-animations for that ScrollTrigger. ScrollTriggers do not have a duration property. The duration - i.e. the amount of scroll - is being defined by the start and end of a ScrollTrigger. So maybe what you meant instead of duration: "150%" is end: "+=150%" ? let parachute = new TimelineMax({ ... }) is old syntax. I would suggest using the newer GSAP3 syntax for timelines: let parachute = gsap.timeline({ ... })
  14. I read your question a few times and I'm lost. Sorry You'll significantly improve your chances of getting a good answer if you isolate things as much as possible in a minimal demo. You shouldn't need 100 lines of JS just to show the challenge you're struggling with. Just a couple of <div> elements and a timeline or two with minimal tweens is ideal, just to show the concept. Strip out everything you possibly can. You also might want to look at this (although since I don't understand your question it may not be relevant here): By the way, there's no such thing as overwrite: true on a timeline. That's a tween special property. You can set defaults on a timeline, though, that get passed to all the child tweens. gsap.timeline({defaults: {overwrite: "auto", ease: "none"}}) That can save you some typing.
  15. Saaaay.... Is there a way to tied the timeline to the START and the END of the first tween? If I use the fromTo method it seems as if cylrod jumps to the from position regardless of where he might be located. If I use the the to method and cylrod starts from a ramdon position, then the rest of tweens fall behind thinking cylrod is always starting from the begining. The "<" is good the start motions in relation to the first tween... but it doesn't do anything to tell the rest of the tweens that the first tween already reached his finished point.. var extend = gsap.timeline({paused: true, overwrite:true}); extend.fromTo(cylrod, {x:0, duration:5, ease: "none"}, {x:200, duration:5, ease: "none"}); extend.to(pline1, {opacity:0, ease: "none"}, "<"); extend.to(rvarrow, {y:0, duration: 0.1, ease: "none"}, "<"); extend.to(sola, {opacity:1, ease: "none"}, "<"); extend.to(aline1, {opacity:1, ease: "none"}, "<"); extend.to(aline2, {opacity:0, ease: "none"}, "<"); extend.to(aline1, {opacity:0, ease: "none"}, "+=0.1"); extend.to(rvarrow, {y:-14, duration: 0.1, ease: "none"}, "<"); extend.to(aline2, {opacity:1, ease: "none"}, "<"); extend.to(sola, {opacity:0, ease: "none"}, "+=1"); extend.to(aline2, {opacity:0, ease: "none"}, "<"); extend.to (pline1, {opacity:1}, "<"); extend.to (bline1, {opacity:0}, "-=5"); extend.to (bline2, {opacity:0}, "-=5"); extend.to (solb, {opacity:0}, "-=5"); https://codepen.io/Elindo586/pen/xxrbWyY
  16. Do I need to wrap them in some sort of brackets? If so, how do I do that? The timing and sequence for each timeline is now exactly how I wanted it. I added overwrite:true for each timeline, so it would kill any conflicts with other timelines. and I added .kill() for each button so it would kill the other buttons. var start = gsap.timeline ({paused:true, overwrite: true}); btnstop.onclick = () => stop.play(); extend.kill retract.kill The problem is that the buttons and timelines are not killing the actions of previous timelines, even after I added specific details to do this job.. ALSO, for some reason the timelines are working only once per each loading of the page...
  17. Ok, this might sound silly, but I haven't been able to time the tweens properly. What would be the values to activate some tweens at the START, and some tweens at the END from the first tween in the timeline? The first tween has a duration of 4 seconds from start to end. I have an example below of 11 tweens. all which should be timed in relation to the start and and the end of the very first tween which has a duration of 4 seconds.... tweens 2,3,4, should activate at the same time as the START of tween 1 tweens 5, 6, 7 should activate at the same time as the END of tween 1 tweens 8,9,10,11 should activate at the START of tween 1 (These are items I want to keep OFF, but are included in this timeline because they might have been activated from previous timelines) var retract = gsap.timeline({paused:true, overwrite: true}); retract.to(cylrod, {x:0, duration:4, ease: "none"}); retract.to(solb, {opacity:1}, "-=4"); retract.to(bline1, {opacity:1},"-=4"); retract.to(rvarrow, {y:0, ease: "none", duration: 0.1}, "-=4"); retract.to(bline1, {opacity:0}, "+=4"); retract.to(bline2, {opacity:1}, "-=4"); retract.to(rvarrow, {y:-14, ease: "none", duration: 0.1}, "=4"); retract.to (pline1, {opacity:0}, "-=4"); retract.to(aline1, {opacity:0}, "-=4"); retract.to(aline2, {opacity:0}, "-=4"); retract.to(sola, {opacity:0}, "-=4");
  18. Thanks!!! I think the overwrite:true will help on some of the conflict. As far as turning the opacity off, I wrote another line in the same tween after turning it off after a few seconds of turning it on. extend.to(aline1, {opacity:1}, "-=5"); extend.to(aline2, {opacity:1}, "+=5"); extend.to(aline1, {opacity:0}, "+=01"); The timing still pretty bad, so I need to study how to adjust it to turn them on and off in relation to the movement of cylrod on top. This coding thing is taking longer than I was thinking, but if I get to work through some of these rules I would be able to do animate a few more circuits..
  19. You definitely shouldn't be using WebKitCSSMatrix like that - it would be much cleaner (and more compatible) to use a simple gsap.getProperty(".ticker-wrapper", "x") but you don't even need to do that. Here's a different approach that leverages a simple xPercent animation: https://codepen.io/GreenSock/pen/rNwaLvw?editors=0010 $(document).ready( function(){ $('ul li').wrapAll('<span class="ticker-items">'); var tickerWidth = $('ul').width(); var spanWidth = $('ul span').width(); $('ul span').clone().appendTo('ul'); $('ul span').wrapAll('<span class="ticker-wrapper">'); var speed = 50, tl = gsap.timeline(); tl.fromTo(".ticker-wrapper", { // intro (from far right edge) xPercent: tickerWidth / spanWidth / 2 * 100 }, { xPercent: 0, duration: tickerWidth / speed, ease: "none" }).to(".ticker-wrapper", { // loop xPercent: -50, ease: "none", duration: spanWidth / speed, repeat: -1 }); $('ul').on('mouseenter', () => gsap.to(tl, {timeScale: 0, overwrite: true})); $('ul').on('mouseleave', () => gsap.to(tl, {timeScale: 1, overwrite: true})); }); Notice that in the mouseenter/mouseleave I'm actually animating the timeScale of the timeline so that it gradually slows down or speeds up instead of suddenly stopping/starting. I just think that feels nicer, but you're welcome to do a simple tl.pause() and tl.play() if you prefer. However, I think the problem you mentioned in your previous post has to do with the fact that only one clone is done in that code because it assumes your content fills the container. You could add a loop in there to clone it multiple times but I was inspired to create a gsap.effect.ticker that should help with this: https://codepen.io/GreenSock/pen/rNwaMPo?editors=0010 gsap.registerEffect({ name: "ticker", effect(targets, config) { let master = gsap.timeline(); buildTickers(targets, config); function clone(el) { let clone = el.cloneNode(true); el.parentNode.insertBefore(clone, el); clone.style.position = "absolute"; return clone; } function nestChildren(el, className) { let div = document.createElement("div"); while (el.firstChild) { div.appendChild(el.firstChild); } el.appendChild(div); className && div.setAttribute("class", className); div.style.display = "inline-block"; div.style.boxSizing = "border-box"; return div; } function buildTickers(targets, config, isResize) { if (isResize) { // on window resizes, we should delete the old clones and reset the widths targets.clones.forEach(el => el && el.parentNode && el.parentNode.removeChild(el)); gsap.set(targets.chunks, {x: 0}); } else { targets.clones = []; targets.chunks = []; } master.clear(); let clones = targets.clones, chunks = targets.chunks; targets.forEach((el, index) => { let chunk = chunks[index] || (chunks[index] = nestChildren(el, config.className)), chunkWidth = chunk.offsetWidth + (config.padding || 0), cloneCount = Math.ceil(el.offsetWidth / chunkWidth), chunkBounds = chunk.getBoundingClientRect(), elBounds = el.getBoundingClientRect(), right = (el.dataset.direction || config.direction) === "right", tl = gsap.timeline(), speed = parseFloat(el.dataset.speed) || config.speed || 100, i, offsetX, offsetY, bounds, cloneChunk, all; el.style.overflow = "hidden"; gsap.getProperty(el, "position") !== "absolute" && (el.style.position = "relative"); // avoid scrollbars for (i = 0; i < cloneCount; i++) { cloneChunk = clones[i] = clone(chunk); if (!i) { bounds = cloneChunk.getBoundingClientRect(); offsetX = bounds.left - chunkBounds.left; offsetY = bounds.top - chunkBounds.top; } gsap.set(cloneChunk, {x: offsetX + (right ? -chunkWidth : chunkWidth) * (i + 1), y: offsetY}); } all = clones.slice(0); all.unshift(chunk); tl.fromTo(all, { x: right ? "-=" + (chunkBounds.right - elBounds.left) : "+=" + (elBounds.right - chunkBounds.left) }, { x: (right ? "+=" : "-=") + elBounds.width, ease: "none", duration: elBounds.width / speed, overwrite: "auto" }).to(all, { x: (right ? "+=" : "-=") + chunkWidth, ease: "none", duration: chunkWidth / speed, repeat: -1 }); master.add(tl, 0); }); // rerun on window resizes, otherwise there could be gaps if the user makes the window bigger. isResize || window.addEventListener("resize", () => buildTickers(targets, config, true)); } return master; } }); Once that effect is registered, all you have to do is: let tl = gsap.effects.ticker(".ticker", { speed: 100, className: "ticker-content", //direction: "right" }); So you can tweak the speed, direction, and the class name that's added to the wrapper <div> elements around the chunks. It dynamically figures out how many clones to make. It even handles resizes. You can even add data-speed or data-direction attributes to the elements instead of passing that in through the config object. I hope that helps!
  20. // you don't need 'new' let TL_mouseleave = new gsap.timeline({ id: 'TL_mouseleave' }); // no need to 'add' you can just call TL_mouseleave.to() & overwrite: false is the default TL_mouseleave.add(gsap.to('#btn', { backgroundColor: 'white', color: 'black', delay: 0, duration: 3, overwrite: false, ease: "ease.in" })); TL_mouseleave.pause(); // so it becomes... let TL_mouseleave = gsap.timeline({paused: true}); TL_mouseleave.to('#btn', { backgroundColor: 'white', color: 'black', delay: 0, duration: 3, ease: "ease.in" }); Some notes on things I changed in your pen
  21. Hey there! It's hard to help by looking at this example because I would do this completely differently - this is a really good case for creating a tween or timeline and then playing it forward/on reverse on hover. e.g. https://codepen.io/GreenSock/pen/zYzYGeN?editors=0011 In general this sounds like it's going to invite conflicts and confusion if you're animating the same properties. There wouldn't be a one case fits all approach - it would depend on which properties you're animating and what you want to happen when they conflict. overwrite:true or overwrite:'auto' will be your friends. Do you envisage the timelines being more complex than these examples? Maybe you could put together a more fleshed out example for us to see?
  22. You have conflicting tweens here, setting overwrite:true on the exit tween seems to fix the issue. Is this what you're after? https://codepen.io/GreenSock/pen/QWgWwda?editors=0010
  23. If it works on CodePen, then it's an issue with your WordPress setup. There's really not a lot we can say beyond that. Just make sure it's not messing with the scroll. It would be the same as creating a standalone trigger. ScrollTrigger.create({ start: "top top", end: "bottom bottom", snap: 1 / (3 - 1), onUpdate({ progress }) { sketch.model.rotation.y = 2 * 3.14 * progress; sketch.model.position.z = -2.6 * Math.sin(3.14 * progress); } }); Also not that the progress value isn't include in the scrub. If you want to "scrub" it, you can do something like this. let proxy = { progress: 0 }; ScrollTrigger.create({ start: "top top", end: "bottom bottom", snap: 1 / (3 - 1), onUpdate({ progress }) { gsap.to(proxy, { progress: progress, ease: "power3", overwrite: true, onUpdate() { sketch.model.rotation.y = 2 * 3.14 * proxy.progress; sketch.model.position.z = -2.6 * Math.sin(3.14 * proxy.progress); } }); } });
  24. I assume you want something like this: gsap.to(tl, { timeScale: 1, ease: "power1.inOut", duration: 2, overwrite: true }); ?
  25. Thanks nicofonseca, I have "circ.out" in my code, just an error in my message. Still doesn't seem to have any affect in the transition from the updated velocity to the default gsap.to(tl, { timesScale: 1, duration: 3, overwrite: true, ease: 'circ.out' }); Is there a way to achieve a smoother transition?
×