Jump to content

Ben Carey

  • Posts

  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Ben Carey's Achievements



  1. For anyone coming across this issue in the future, I have come up with the following solution: This is inserted into my footer component: watch: { $route() { // Initialize the scroll animation bus.$once('page-ready', this.initializeScrollAnimation); } } Where page-ready is a custom event that I fire when all assets have been preloaded and the main route has been mounted. This forces the footer to initialise the scroll animation after the router-view components have initialised their scroll animations.
  2. I have come across an issue with ScrollTrigger and Vue JS, and whilst I know what is causing it, I cannot think of a solution to the problem... Consider the following two components: Component 1 (in <router-view>) <template> <h1 id="heading">Hello World</h1> </template> <script> import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; gsap.registerPlugin(ScrollTrigger); export default { mounted() { const scrollTrigger = { trigger: '.some-section', start: 'top bottom', end: 'bottom bottom', scrub: true, pin: true }; gsap.timeline({ scrollTrigger }) .from('#heading', { y: 500, opacity: 0 }); } } </script> Component 2 (outside of <router-view>) <template> <footer id="main-footer"> <p>Some footer</p> </footer> </template> <script> import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; gsap.registerPlugin(ScrollTrigger); export default { mounted() { const scrollTrigger = { trigger: '#main-footer', start: 'top bottom', end: 'bottom bottom', scrub: true }; gsap.timeline({ scrollTrigger }) .from('#main-footer p', { y: 500, opacity: 0 }); } } </script> The problem above is that Component 2 gets rendered before Component 1. This causes the triggers to be calculated incorrectly... The usual solution to this would be to utilise refreshPriority, alongside sort and refresh - however, these do not work as each component creates a new instance of ScrollTrigger and therefore, the refreshPriority has no effect. I have tried setting GSAP as a global on the Vue prototype, and importing ScrollTrigger in my app.js file, but this did not solve the issue. For example (did not work): import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; gsap.registerPlugin(ScrollTrigger); Vue.prototype.$gsap = gsap; Can anyone suggest a way in which I can either, prevent the second component from calling the ScrollTrigger set up until the other components have (not using events as this is dirty), or, is there a way in which I can register one ScrollTrigger instance globally and then all tweens will be added to the same instance?
  3. Perfect!!!! Thank you so much!!! This has sorted it 😀
  4. Is there a way to obtain the current target via the onupdate callback within the fromTo tween in GSAP 3? In GSAP version 2, I was able to do this: this.timeline.staggerFromTo(this._shuffle(this.spheres), 4, { positionY: -1 }, { positionY: 1, repeat: -1, yoyo: true, ease: Power1.easeInOut, onUpdate: function onUpdate(a) { a.target.mesh.position.y = a.target.meshInitPosY + (a.target.positionY + 1) * 200; a.target.mesh.rotation.y = a.target.meshInitRotY + (a.target.positionY + 1) * 3; }, onUpdateParams: ["{self}"] }, '0.01'); However, in GSAP 3, all I get is an array of all of the targets and I need to get the actual target... this.timeline.fromTo(this._shuffle(this.spheres), { positionY: -1 }, { duration: 4, positionY: 1, repeat: -1, yoyo: true, ease: Power1.easeInOut, stagger: 0.01, onUpdate() { console.log(this, this._targets); // this returns all of the spheres //this.target.mesh.position.y = this.target.meshInitPosY + (this.target.positionY + 1) * 200; //this.target.mesh.rotation.y = this.target.meshInitRotY + (this.target.positionY + 1) * 3; } }); Am I missing something very obvious?
  5. Ah, brilliant!!! Thank you. You are probably wondering why I am not using fromTo as well, this is only because I thought that might be causing the issue when I first started debugging. I will changing to fromTo when it comes to publishing. In that case, I am 99% sure this issue is caused by retina displays. This is further supported by the fact that it performs better when I plug my Mac into my external monitor (not retina). Can you think of anything I can do to improve this?
  6. @Shaun Gorneau - I can confirm that the issue still persists when I use xPercent and yPercent. See here: https://codepen.io/sinemacula/pen/OJPrjXj I can also confirm that the animation is much less glitchy when I test it on an attached monitor which indicates the issue is related to retina displays.
  7. Hi Shaun, Thank you for your prompt response. I assume you are using a retina display as well? I plugged my Mac into my monitor and whilst it isn't perfect, it seems to be better... Funnily enough, using numeric values with xPercent and yPercent was the one thing that I thought of doing but I didn't actually try. I will try this now and see if it makes a difference.
  8. I have now created this in GSAP v3 and can confirm the issue still persists. See here: https://codepen.io/sinemacula/pen/dyPwRqb
  9. I have read various posts on here, some old, and some more recent, about animations being glitchy in Firefox. I have done my absolute best to obtain as much information as I can prior to posting this, in addition to creating the attached Codepen. For some reason, I cannot get my animation to play smoothly in Firefox. To keep things simple, this is all the information I have (and have learned/tried): Issue only occurs in Firefox on Mac OS (smooth as silk in Firefox on Windows) Not my graphics card as a) it is a top spec 2019 Macbook Pro, and b) my designer has the same issue on his Mac I have tried the rotation fix I have tried force3D I have tried using low res images The more effects that I add, the worse it gets i.e. autoAlpha, Rotation, filter etc... Additional information: I am using GSAP 2.1.3 as haven't had the time to upgrade (after I post this, I will attempt to create the same animation with the latest version in a separate codepen) It does seem as if there is a minor memory leak of some sort in either GSAP or Firefox because my fan goes mad when I have the codepen tab open and on screen I have wondered whether it is to do with the retina display as I had a similar issue with my fan in the past with this plugin that was caused by the retina display Can anyone suggest what may be causing this?
  10. In case anyone comes across this post in future; I wasn't able to identify the issue with timeScale, however the method that Zach originally suggested is the way in which to go about handling this. Namely, recreating the timeline on each resize (use a debounce function): _handleResize() { // Debounce the scroll animation initialization if (this.resizeTimeout) { clearTimeout(this.resizeTimeout); } this.resizeTimeout = setTimeout(this.initializeScrollAnimation, 100); } And then re initialize the timeline: initializeScrollAnimation() { // Empty the timeline if it already exists if (this.scrollTimeline) { this.scrollTimeline.clear(); } const trigger = 100; const duration = Math.max(this.$refs.image.clientHeight * 0.6, 800); this.scrollTimeline = new TimelineMax({ paused: true }) .fromTo(this.$refs.left, duration, { x: '-12.5%', y: '.6%', rotation: -45.01, autoAlpha: 0 }, { x: '-2.5%', y: '10.6%', rotation: -45, autoAlpha: 1, ease: Power1.easeInOut, force3D: true }, trigger) .fromTo(this.$refs.center, duration, { x: '240.1%', y: '73.8%', rotation: -45.01, autoAlpha: 0 }, { x: '220.1%', y: '63.8%', rotation: -45, autoAlpha: 1, ease: Power1.easeInOut, force3D: true }, trigger) .fromTo(this.$refs.right, duration, { x: '88.5%', y: '4.7%', rotation: -45.01, autoAlpha: 0 }, { x: '68.5%', y: '-6.7%', rotation: -45, autoAlpha: 1, ease: Power1.easeInOut, force3D: true }, trigger) .fromTo(this.$refs.center, duration / 2, { scale: .85, filter: 'drop-shadow(rgba(0, 0, 0, .3) -60px 75px 33px)' }, { x: '218%', y: '62%', scale: 1, filter: 'drop-shadow(rgba(0, 0, 0, .6) -60px 75px 33px)', ease: Power1.easeInOut, force3D: true }); this._updateScrollTimeline(); }
  11. Hi Zach, Thank you very much for your prompt response . The way I am using timeScale is by calculating the percentage change in my responsive image height, and then inverting that percentage change and applying it to the timeScale. In other words, think of it like this: User starts on large screen Responsive image height is 1500px Timeline duration is 1500 (the height of the image that will be animated) User resizes the screen (smaller) New image height is 1000px Calculate the new timeScale by doing originalHeight / newHeight i.e. 1500 / 1000 = 1.5 This should increase the speed of the animation on smaller screens and decrease it on larger screens. For some reason, it does nothing... I have checked all of the numbers being inserted into timeScale and they are correct. Just to double check, I hardcoded the following and still couldn't get it to make any difference to the speed of the animation: this.scrollTimeline.timeScale(1000) It seems that timeScale has no effect on my Timeline and I don't understand why. Could there be scenarios where timeScale is ignored? I saw there was a new version the other day and I was tempted to upgrade but this is a large project and there are various places I am using Tween and Timeline etc so the upgrade will take me time that I am quite limited on at the moment. That said, if you think upgrading may fix my issue then I will go ahead and do it... You are right, technically large numbers will be passed into this.scrollTimeline.time(window.pageYOffset);, however, I assumed that Timeline would just ignore the number outside of the animation bounds, thus, it wouldn't cause any issues. Am I wrong here? Creating a demo is really difficult due to the size of this component, and the fact that it is Vue. I was hoping that the issue would be really obvious and therefore I could avoid it but if there is nothing that stands out to you then I will have to find a way to create a simple demo. Just to add to the above, I can confirm that this.scrollTimeline.timeScale() reflects the set value when I get it. In other words, it is succesfully setting the timeScale, it just isn't doing anything with it. It is basically as if the Timeline is ignoring the timeScale, no matter what the set value is.
  12. I have set up a timeline that is controlled by the user's scroll position. See the following (it's in Vue.js but that shouldn't really affect anything here): this.scrollImageHeight = this.$refs.image.clientHeight; let trigger = 100; let duration = this.scrollImageHeight * 0.8; this.scrollTimeline = new TimelineMax({ paused: true }) .to(this.$refs.left, duration, { x: '-2.5%', y: '10.6%', force3D: true }, trigger) .to(this.$refs.center, duration, { x: '132%', y: '58.7%', force3D: true }, trigger) .to(this.$refs.right, duration, { x: '68.5%', y: '-6.7%', force3D: true }, trigger) .to(this.$refs.center, 100, { scale: 1 }); Then, in order to make the timeline responsive, I intend to update the timeScale whenever the user resizes their screen (yes, I am debouncing the resize callback :-)). const timeScale = this.scrollImageHeight / this.$refs.image.clientHeight; this.scrollTimeline.timeScale(timeScale); For some reason, setting the timeScale has absolutely no effect on the timeline. The timeline keeps the original duration set when the timeline initialized. I have also tried changing the duration of the timeline but this also had no effect. I have confirmed that the number passed into timeScale is correct. I have tried hardcoding other numbers to see if that has any effect but nothing seems to work. I am using gsap version 2.1.3. Can anyone suggest what I am doing wrong here? Note: I haven't supplied a codepen as this is just a snippet taken from my large Vue component, and I am hoping that I am just missing something really obvious and you will be able to point it out. If that is not the case then I will do my best to create a codepen. In addition, in case this is relevant, this is my scroll callback: this.scrollTimeline.time(window.pageYOffset);