Jump to content
Search Community

Angular and scrollTrigger code optimisation

Corentin Darras test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hello,

 

First of all, i'm totally new to GSAP and really in love of it! Congrats team for this work.

 

I work on an Angular v10. I would like to know if this following code follow the good practices for GSAP or if i can optimizes it better. 

Sometimes when i refresh my page, all animations are playing on the scrolltrigger simultaneously like they have lost their real positons. (i can add a video to show the issue) 

 

drawLine() {
  gsap.defaults({ease: 'none'});
  gsap.set('.ball', {xPercent: -50, yPercent: -50})
  var tl = gsap.timeline({
    defaults: {
      duration: 0.5,
      autoAlpha: 1,
      scale: 2,
      transformOrigin: 'center',
      ease: 'elastic(2.5, 1)'
    }
  })

  //Tl 1
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-header',
      endTrigger: "#c-description",
      scrub: true,
      start: 'top top',
      end: '+=400px',
      onEnter: () => this.setActivePath('dot1'),
      onEnterBack: () => this.setActivePath('dot1')
    }
  })
    .to('.ball01', {duration: 0.01, autoAlpha: 1})
    .from('.theLine', {drawSVG: '0'}, 0)
    .to('.ball01', {motionPath: {path: '.theLine', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 2
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-description',
      endTrigger: "#c-helping",
      scrub: true,
      start: 'top 25%',
      end: '+=420px',
      toggleClass: {targets: ".c-dot-nav", className: "reverse"},
      onEnter: () => this.setActivePath('dot2'),
      onEnterBack: () => this.setActivePath('dot2')
    }
  })
    .to('.ball02', {duration: 0.01, autoAlpha: 1})
    .from('.theLine2', {drawSVG: '0'}, 0)
    .to('.ball02', {motionPath: {path: '.theLine2', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 3
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-helping',
      endTrigger: "#c-starting-1",
      scrub: true,
      start: 'top 25%',
      end: '+=720px',
      toggleClass: {targets: ".c-dot-nav", className: "reverse-w"},
      onEnter: () => this.setActivePath('dot3'),
      onEnterBack: () => this.setActivePath('dot3')
    }
  })
    .to('.ball03', {duration: 0.01, autoAlpha: 1})
    .from('.theLine3', {drawSVG: '0'}, 0)
    .to('.ball03', {motionPath: {path: '.theLine3', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 4
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-starting-1',
      endTrigger: "#c-starting-2",
      scrub: true,
      start: 'top 40%',
      end: '+=260px',
      onEnter: () => this.setActivePath('dot4'),
      onEnterBack: () => this.setActivePath('dot4')
    }
  })
    .to('.ball04', {duration: 0.01, autoAlpha: 1})
    .from('.theLine4', {drawSVG: '0'}, 0)
    .to('.ball04', {motionPath: {path: '.theLine4', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 5
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-starting-2',
      endTrigger: "#c-flip",
      scrub: true,
      start: 'top 40%',
      end: '+=476px'
    }
  })
    .to('.ball05', {duration: 0.01, autoAlpha: 1})
    .from('.theLine5', {drawSVG: '0'}, 0)
    .to('.ball05', {motionPath: {path: '.theLine5', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 6
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-flip',
      endTrigger: "#c-tips",
      scrub: true,
      start: 'top 40%',
      end: '+=385px',
      onEnter: () => this.setActivePath('dot5'),
      onEnterBack: () => this.setActivePath('dot5')
    }
  })
    .to('.ball06', {duration: 0.01, autoAlpha: 1})
    .from('.theline6', {drawSVG: '0'}, 0)
    .to('.ball06', {motionPath: {path: '.theline6', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 7
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-tips',
      endTrigger: "#c-form",
      scrub: true,
      start: 'top 40%',
      end: '+=832px',
      onEnter: () => this.setActivePath('dot6'),
      onEnterBack: () => this.setActivePath('dot6')
    }
  })
    .to('.ball07', {duration: 0.01, autoAlpha: 1})
    .from('.theline7', {drawSVG: '0'}, 0)
    .to('.ball07', {motionPath: {path: '.theline7', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 8
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-form',
      endTrigger: "#c-talk-soon",
      scrub: true,
      start: 'top 40%',
      end: '+=690px',
      toggleClass: {targets: ".c-dot-nav", className: "reverse"},
      onEnter: () => this.setActivePath('dot7'),
      onEnterBack: () => this.setActivePath('dot7')
    }
  })
    .to('.ball08', {duration: 0.01, autoAlpha: 1})
    .from('.theline8', {drawSVG: '0'}, 0)
    .to('.ball08', {motionPath: {path: '.theline8', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);

  //Tl 9
  var action = gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: '#c-talk-soon',
      endTrigger: "#c-footer-1",
      scrub: true,
      start: 'top 40%',
      end: '+=500px',
      toggleClass: {targets: ".c-dot-nav", className: "reverse-w"},
      onEnter: () => {this.setActivePath('dot8'); this.nurse2.play();},
      onEnterBack: () => this.setActivePath('dot8')
    }
  })
    .to('.ball09', {duration: 0.01, autoAlpha: 1})
    .from('.theline9', {drawSVG: '0'}, 0)
    .to('.ball09', {motionPath: {path: '.theline9', alignOrigin: [0.5, 0.5]}}, 0)
    .add(tl, 1);
}
Link to comment
Share on other sites

Welcome aboard, @Corentin Darras! And thanks for being a Club GreenSock member. 🙌

 

I noticed a few minor things:

  1. transformOrigin should have two values (one for the x-axis, one for the y-axis), like "center center" (you had simply "center")
  2. You defined an endTrigger and then you set the end to "+=400" which simply means "400px past wherever the start is", so there's no point in even defining an endTrigger in that scenario. You only need to define an endTrigger if you're actually trying to make the ending point relative to that element somehow (and it's different than the trigger element). Honestly, it's pretty rare that an endTrigger is needed. 
  3. You created a "tl" timeline and then added it to every "action" timeline. First of all, that timeline doesn't actually have anything in it, so that's entirely pointless, but also an animation/timeline can only exist in one place just like a DOM element can only have one parentNode. So if you add() the "tl" timeline to one timeline, it'll be removed from the other. Don't add() it to all your action timelines. 
  4. Overall, your code looks very repetitive which means you could probably make a helper function and parameterize things to significantly reduce the amount of code you've got. 

I hope that helps!

 

3 hours ago, Corentin Darras said:

Sometimes when i refresh my page, all animations are playing on the scrolltrigger simultaneously like they have lost their real positons. (i can add a video to show the issue) 

I'm not familiar with Angular but my total guess is that Angular is taking a little while to actually RENDER your elements to the DOM, but perhaps you're creating all your ScrollTriggers before that happens, thus when ScrollTrigger tries to measure things, it's all 0 because the elements aren't in the DOM yet and can't really be measured. Again, I'm taking shots in the dark here. You may need to tap into a lifecycle event to make sure that things actually exist before you tell ScrollTrigger to measure things. You could just call ScrollTrigger.refresh() when you're positive everything is present and measurable. 

 

Happy tweening/scrolling!

Link to comment
Share on other sites

Hi @GreenSock ! Thanks for your quick reply , i understand well certains points.

 

My animation is a line who's drawing on scroll. Each line following the height of the container. 

 

I have a question regarding the end of scrollTrigger. Actually i input a size in px who's equal to the container height, but with responsive or language change this height evolve. I'm trying to bind the value on a variable (this.descriptionHeight) who's observe the height of the container. If height change i refresh the scrollTrigger. It's possible ton bind the end of animation to the next div ? Or i need to calculate myself the distance ? My reflexion look good for you ? 

 

This is my update code following your indications => 

 

buildTl(trigger: string, start: string, end: number, tog: string, idEl: string, dotSpec?: string) {
  var dotName;
  var dotClass;
  var defaultMarker = false;
  var endSize = '+=' + end + 'px';

  if (dotSpec) {
    dotName = 'dot' + dotSpec;
  } else {
    dotName = 'dot' + idEl;
  }

  if(tog.length > 1) {
    dotClass = tog;
  } else {
    dotClass = 'init'
  }

  return gsap.timeline({
    defaults: {duration: 1},
    scrollTrigger: {
      trigger: trigger,
      scrub: true,
      start: start,
      markers: defaultMarker,
      end: endSize,
      toggleClass: {targets: ".c-dot-nav", className: dotClass},
      onEnter: () => this.setActivePath(dotName),
      onEnterBack: () => this.setActivePath(dotName)
    }
  })
    .to('.ball0' + idEl, {duration: 0.01, autoAlpha: 1})
    .from('.drawLine' + idEl, {drawSVG: '0'}, 0)
    .to('.ball0' + idEl, {motionPath: {path: '.drawLine' + idEl, alignOrigin: [0.5, 0.5]}}, 0)
}

drawLine() {
  gsap.defaults({ease: 'none'});
  gsap.set('.ball', {xPercent: -50, yPercent: -50})

  //Tl 1
  this.buildTl('#c-header', 'top top', 400, '', '1')
  //Tl 2
  this.buildTl('#c-description', 'top 25%', this.descriptionHeight, 'reverse', '2')
  //Tl 3
  this.buildTl('#c-helping', 'top 25%', 720, 'reverse-w', '3')
  //Tl 4
  this.buildTl('#c-starting-1', 'top 40%', 260, '', '4')
  //Tl 5
  this.buildTl('#c-starting-2', 'top 40%', 476, '', '5', '4')
  //Tl 6
  this.buildTl('#c-flip', 'top 40%', 385, 'ra', '6', '5' )
  //Tl 7
  this.buildTl('#c-tips', 'top 40%', 832, '', '7')
  //Tl 8
  this.buildTl('#c-form', 'top 40%', 690, 'reverse', '8')
  //Tl 9
  this.buildTl('#c-talk-soon', 'top 40%', 500, 'reverse-w', '9')
}
Link to comment
Share on other sites

  • Solution
6 hours ago, Corentin Darras said:

Actually i input a size in px who's equal to the container height, but with responsive or language change this height evolve. I'm trying to bind the value on a variable (this.descriptionHeight) who's observe the height of the container. If height change i refresh the scrollTrigger. It's possible ton bind the end of animation to the next div ? Or i need to calculate myself the distance ?

You shouldn't need to do that. You could just use something like: 

end: "+=100%"

Which is 100% of the height of the trigger element. And then when you refresh() the ScrollTrigger, it'll calculate that for you. 

 

If you really want to do it with a variable, you could use a function-based value:

end: () => "+=" + this.descriptionHeight

I hope that helps. 

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...