Jump to content
GreenSock

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

Cant't Kill a specific Tween ES6

Recommended Posts

Hey there, i tried to set up a Codepen, but unfortunately  i couldn't get it work

 

....i am basically using webpack and es6 promises as well as enqurie.js and scrollmagic with gsap to

trigger different tweens/scenes and kill them when the breakpoint is changed, unfortunately i get a 

animation.tweenShowcaseDesktop.kill is not a function

on a specific tween.

 

Since i am not able to make it work in codepen here is the module where i trigger the tweens and scrollmagic :

 

I know its a very long snippet and shot , but maybe someone will spot my mistake right away.

 

thanks a lot guys and keep up the awesome work.

 

animation.js

 
import ScrollMagic from 'scrollmagic';
import 'animation.gsap';
import 'debug.addIndicators';
import TweenMax from 'TweenMax';
import 'EasePack'; // eslint-disable-line
/* eslint-disable no-unused-vars, no-undef */

/**
* *****************************************************************************
* Mobile
*/

export const controllerMobile = new ScrollMagic.Controller();

/**
* Service Tiles Mobile
*/
export function triggerServiceMobile() {
const serviceTileFigure = document.getElementsByClassName(
'js-service-tile__fig',
);

for (let i = 0; i < serviceTileFigure.length; i += 1) {
const sceneServiceMobile = new ScrollMagic.Scene({
triggerElement: serviceTileFigure[i],
reverse: true,
triggerHook: 1,
offset: 0,
duration: 280,
})
.on('start', () => {
serviceTileFigure[i].classList.add('js-service-tile__fig--is-active');
})
.on('end', () => {
serviceTileFigure[i].classList.remove(
'js-service-tile__fig--is-active',
);
})
// .addIndicators()
.addTo(controllerMobile);
}
}

/**
* *****************************************************************************
* Desktop
*/

export const controllerDesktop = new ScrollMagic.Controller();

/**
* Service Desktop
*/
export const tweenServiceDesktop = TweenMax.fromTo(
'.c-service',
1,
{ autoAlpha: 0, scale: 1, y: 200 },
{ ease: Power0.easeIn, autoAlpha: 1, scale: 1, y: 0 },
);

export function triggerServiceDesktop() {
// const sections = document.getElementsByTagName('section');

const sceneServiceDesktop = new ScrollMagic.Scene({
triggerElement: '#trigger',
reverse: false,
triggerHook: 0.75,
})
// .addIndicators()
.setTween(tweenServiceDesktop)
.addTo(controllerDesktop);
}

/**
* Portfolio Desktop
*/
export const tweenShowcaseDesktop = TweenMax.staggerFromTo(
'.c-showcase-item',
0.4,
{ autoAlpha: 0, scale: 0, y: 0 },
{ ease: Power4.easeOut, autoAlpha: 1, scale: 1, y: 0 },
0.3,
);

export function triggerShowcaseDesktop() {
const showcaseTiles = document.getElementsByClassName('c-showcase-item');
const sceneShowcaseDesktop = new ScrollMagic.Scene({
triggerElement: '.c-showcase',
reverse: false,
triggerHook: 0.5,
})
// .addIndicators()
.setTween(tweenShowcaseDesktop)
.addTo(controllerDesktop);
}

 

and my entry where i import and trigger them:

// Mobile Animationen
enquire.register(breakpoint.forPhoneOnly, {
match() {
import('changeLogoColor')
.then((changeLogoColor) => {
changeLogoColor.default();
})
.catch(e => console.error(`${e.name} : ${e.message}`));
import('animation')
.then((animation) => {
animation.triggerServiceMobile();
})
.catch(e => console.error(`${e.name} : ${e.message}`));
},
unmatch() {
import('animation')
.then((animation) => {
animation.controllerMobile.destroy();
})
.catch(e => console.error(`${e.name} : ${e.message}`));
},
});

// Desktop Animationen
enquire.register(breakpoint.forTabletPortraitUp, {
match() {
import('animation')
.then((animation) => {
animation.triggerServiceDesktop();
animation.triggerShowcaseDesktop();
})
.catch(e => console.error(`${e.name} : ${e.message}`));
},
unmatch() {
import('animation')
.then((animation) => {
animation.controllerDesktop.destroy();
animation.tweenShowcaseDesktop.kill();
animation.tweenServiceDesktop.kill();
})
.catch(e => console.error(`${e.name} : ${e.message}`));
},
});
Link to post
Share on other sites

It looks like the problem is that TweenMax.staggerFromTo() returns an ARRAY of tweens, but you're treating it like it returns a tween instance. In other words, you're trying to call kill() on an array instance. 

 

I'd probably just use a TimelineLite instance's staggerFromTo() instead of TweenMax.staggerFromTo() so that those tweens are wrapped in a timeline that can be killed easily, or you could just loop through the tweens in the TweenMax.staggerFromTo() array and kill() each one. 

 

Does that help?

  • Like 2
Link to post
Share on other sites

Thanks a lot Jack, 

using TimlineLite or Max throwed  

TimelineLite __default.a.staggerFromTo is not a function

 

but this worked like a charm 

animation.tweenShowcaseDesktop.forEach((id) => {
id.kill();
});

 

thanks for such a quick reply, much appreciated!

Link to post
Share on other sites

I wonder if you accidentally tried to call staggerFromTo() on TimelineLite as a static method instead of an instance method: 

//BAD: 
TimelineLite.staggerFromTo(...);

//GOOD: 
var tl = new TimelineLite();
tl.staggerFromTo(...);

 

Anyway, I'm glad you got it working ;)

  • Like 1
Link to post
Share on other sites
1 hour ago, GreenSock said:

I wonder if you accidentally tried to call staggerFromTo() on TimelineLite as a static method instead of an instance method: 


//BAD: 
TimelineLite.staggerFromTo(...);

//GOOD: 
var tl = new TimelineLite();
tl.staggerFromTo(...);

 

Anyway, I'm glad you got it working ;)

Of course i tried it the bad way ...thanks for the heads up :-)

Link to post
Share on other sites

I kinda stumbled on a similar issue, in my animation.js i have the following tweens :

 

 
export const tweenChangeLogo = {
enterOuter: () => {
TweenMax.fromTo('.c-logo__outer', 1, { fill: '#4dabfc' }, { fill: '#fff' });
},
enterInner: () => {
TweenMax.fromTo('.c-logo__inner', 1, { fill: '#fff' }, { fill: '#4dabfc' });
},
leaveOuter: () => {
TweenMax.fromTo('.c-logo__outer', 1, { fill: '#fff' }, { fill: '#4dabfc' });
},
leaveInner: () => {
TweenMax.fromTo('.c-logo__inner', 1, { fill: '#4dabfc' }, { fill: '#fff' });
},
};

const changeLogoTrigger = document.querySelectorAll('.js-change-logo');

export function changeLogo() {
changeLogoTrigger.forEach((trigger) => {
const sceneChangeLogo = new ScrollMagic.Scene({
triggerElement: trigger,
reverse: true,
triggerHook: 0.065,
duration: trigger.clientHeight,
})
.on('enter', () => {
tweenChangeLogo.enterOuter();
tweenChangeLogo.enterInner();
})
.on('leave', () => {
tweenChangeLogo.leaveOuter();
tweenChangeLogo.leaveInner();
})
.addTo(controllerMobile);
});
}

 

since i stored the tweens in an object (to be able to export them and kill them in my index.js if needed) 

i tried to kill them via :

 

(animation.tweenChangeLogo.enterOuter()).kill();  

 

but its not working... is it even possible or the right way to store the tweeens in an object?

 

thanks a lot once again

 

Link to post
Share on other sites

The problem (if I understand correctly) is that you're creating a new tween each and every time which is totally fine (desirable in many cases, in fact), but if your goal is to kill one that's currently running, this ain't gonna help: enterOuter().kill() because that's just creating a whole new instance and immediately killing it. 

 

You can either store the tween instance somewhere to kill it, or you can use TweenLite.killTweensOf(".c-logo__inner"), for example, if you want to kill all the tweens of that particular target. 

 

Does that help? 

Link to post
Share on other sites

Thanks a lot Jack, its not necessary to create a new tween for each animation, i wasn't actually sure if its possible to use just the same and alter the animation? I also want to kill all the tweens it was just an example of what i tried...so would it be better to refactor the code then and just use one tween?

Link to post
Share on other sites

Yeah, I think you are on the right track with using functions to create animations for you.

Like Jack said earlier, TweenMax.staggerTo() creates an array of tweens so you can just call kill() on an array or the function that created the tweens.

You need to somehow store a reference to those animations so a big part of the equation is making sure the functions that create animations also return them.

 

A friend asked a similar question a few years ago and I made this video (sort of off the cuff) and demo to help explain the concept of creating unique entrance and exit animations that can be generated at any time and sequenced. 

 

 

 

See the Pen 71c57db5ff84ab208b2776ebe381ff1b by GreenSock (@GreenSock) on CodePen

 

  • Like 2
Link to post
Share on other sites

thanks a lot for the input and the videos carl and jack. 

it helped and i got a bit further but i still struggle to access specific methods from outside, 

but i think thats more an issue of my knowledge.

 

i will try to dive deeper and get back when i made some progress. 

 

thanks :-)

Link to post
Share on other sites

I got it working in an object literal, this way i am able to simply initialize the mobile animations and destroy them at specific breakpoints or similar.

 

the only thing that i encountered is that the tweens won't reset and work again if i killed them.

 

On a timeline i used  'timeLineLite.clear();'

 

but since its not a timeline and i have to kill/clear all i did use 

TweenMax.killTweensOf ....
i couldnt find anything like clearTweensOf.... or could i somehow wrap the different tweens in a timeline as well?
 
Thanks for all the help and patience 
 
export const mobile = {

  controller: new ScrollMagic.Controller(),

  changeLogo: {
    init: () => {
      const tweens = {
        enterOuter: () => { TweenMax.fromTo('.c-logo__outer', 1, { fill: '#4dabfc' }, { fill: '#fff' }); },
        enterInner: () => { TweenMax.fromTo('.c-logo__inner', 1, { fill: '#fff' }, { fill: '#4dabfc' }); },
        leaveOuter: () => { TweenMax.fromTo('.c-logo__outer', 1, { fill: '#fff' }, { fill: '#4dabfc' }); },
        leaveInner: () => { TweenMax.fromTo('.c-logo__inner', 1, { fill: '#4dabfc' }, { fill: '#fff' }); },
      };
      const trigger = document.querySelectorAll('.js-change-logo');

      trigger.forEach((id) => {
        const scene = new ScrollMagic.Scene({
          triggerElement: id,
          reverse: true,
          triggerHook: 0.065,
          duration: id.clientHeight,
        })
        .on('enter', () => {
          tweens.enterOuter();
          tweens.enterInner();
        })
        .on('leave', () => {
          tweens.leaveOuter();
          tweens.leaveInner();
        })
        .addTo(mobile.controller);
      });
    },
    destroyTweens: () => {
      TweenMax.killTweensOf('.c-logo__outer');
      TweenMax.killTweensOf('.c-logo__inner');
      TweenMax.set('.c-logo__outer', { clearProps: 'all' });
      TweenMax.set('.c-logo__inner', { clearProps: 'all' });
    },
  },
};

 

 

Link to post
Share on other sites

You're still re-creating each tween every time an enter/leave event occurs which is fine I guess, but I'm just confused about where you're expecting things to "reset and work again". I don't see where you're calling destroyTweens() either. I'm not very familiar with ScrollMagic, so you may need to seek help from its author but I can tell you that if your goal is to have a tween "reset" in the sense of "return its values to their starting positions", you can just call tween.pause(0) or tween.seek(0) or tween.progress(0) or tween.time(0). Lots of options. Use pause(0) if you want it to jump to 0 and pause. 

 

Clearing a timeline just means emptying it of all of its child tweens/timelines. 

 

Does any of that help? :)

Link to post
Share on other sites

Thanks for the patience :-) i guess its a bit hard to understand what i am trying to achieve.

I basically have a logo that needs to change its color on mobile devices if the user scrolls over a section that has the same 

color as the logo. I managed to get it working, the only problem was, that the logo won't change it's color back to normal if

e.g. the user changes the viewport from mobile to desktop.

 

The second animation is just a staggerFromTo that only happens on desktop devices, since its a timeline it also worked as u described.

The animation resets once u change the viewport to mobile and restarts if its back to desktop again....thats all fine.

 

So its basically only the mobile animation, and i was already unsure if it's possible to use a timeline instead of Tweenmax for every change of the logo.

The tweens just change the inner and outer logo fills once it touches a section that has the same background.

Scrollmagic isn't doing much, its only triggering the tweens.

 

I am not sure if it helps but i will post the index.js where i call the functions and the animation.js where they sit.

 

I am already super thankful for all the help :-)  So i i felt already bad to bother you guys all the time ...you rock :-)

 

index.js: (enquire fires as soon as a breakpoint matches or unmatches)

 

/**
 * Animations
 */

// Mobile Animationen
enquire.register(breakpoint.forPhoneOnly, {
  match() {
    import('animation')
      .then((animation) => {
        animation.mobile.changeLogo.init();
      })
      .catch(e => console.error(`${e.name} : ${e.message}`));
  },
  unmatch() {
    import('animation')
      .then((animation) => {
        // destroy mobile scrollMagic controller and reset scenes
        animation.mobile.controller.destroy(true);
        // clear and reset animations on tweens
        animation.mobile.changeLogo.destroyTweens();
      })
      .catch(e => console.error(`${e.name} : ${e.message}`));
  },
});

// Desktop Animationen
enquire.register(breakpoint.forTabletPortraitUp, {
  match() {
    import('animation')
      .then((animation) => {
        animation.desktop.showcase.init();
      })
      .catch(e => console.error(`${e.name} : ${e.message}`));
  },
  unmatch() {
    import('animation')
      .then((animation) => {
        // destroy desktop scrollMagic controller and reset scenes
        animation.desktop.controller.destroy(true);
        // clear and reset animations on tweens
        animation.desktop.showcase.destroyTweens();
      })
      .catch(e => console.error(`${e.name} : ${e.message}`));
  },
});

 

animation.js

import ScrollMagic from 'scrollmagic';
import 'animation.gsap';
import TweenMax from 'TweenMax';
import TimelineLite from 'TimelineLite';
import 'EasePack';
/* eslint-disable no-unused-vars, no-undef, no-console */

/**
 * Mobile animations
 */

export const mobile = {

  controller: new ScrollMagic.Controller(),

  changeLogo: {
    init: () => {
      const tweens = {
        enterOuter: () => { TweenMax.fromTo('.c-logo__outer', 1, { fill: '#4dabfc' }, { fill: '#fff' }); },
        enterInner: () => { TweenMax.fromTo('.c-logo__inner', 1, { fill: '#fff' }, { fill: '#4dabfc' }); },
        leaveOuter: () => { TweenMax.fromTo('.c-logo__outer', 1, { fill: '#fff' }, { fill: '#4dabfc' }); },
        leaveInner: () => { TweenMax.fromTo('.c-logo__inner', 1, { fill: '#4dabfc' }, { fill: '#fff' }); },
      };
      const trigger = document.querySelectorAll('.js-change-logo');

      trigger.forEach((id) => {
        const scene = new ScrollMagic.Scene({
          triggerElement: id,
          reverse: true,
          triggerHook: 0.065,
          duration: id.clientHeight,
        })
        .on('enter', () => {
          tweens.enterOuter();
          tweens.enterInner();
        })
        .on('leave', () => {
          tweens.leaveOuter();
          tweens.leaveInner();
        })
        .addTo(mobile.controller);
      });
    },
    destroyTweens: () => {
      TweenMax.killTweensOf('.c-logo__outer');
      TweenMax.killTweensOf('.c-logo__inner');
      TweenMax.set('.c-logo__outer', { clearProps: 'all' });
      TweenMax.set('.c-logo__inner', { clearProps: 'all' });
    },
  },
};

export const desktop = {
  controller: new ScrollMagic.Controller(),
  timeLineLite: new TimelineLite(),

  showcase: {
    init: () => {
      desktop.timeLineLite.staggerFromTo('.c-showcase-item', 0.3,
        { autoAlpha: 0, scale: 0, y: 0 },
        { ease: Power4.easeOut, autoAlpha: 1, scale: 1, y: 0 }, 0.2);
      const scene = new ScrollMagic.Scene({
        triggerElement: '.c-showcase',
        reverse: false,
        triggerHook: 0.5,
      })
        .setTween(desktop.timeLineLite)
        .addTo(desktop.controller);
    },
    destroyTweens: () => {
      desktop.timeLineLite.clear();
      desktop.timeLineLite.set('.c-showcase-item', { clearProps: 'all' });
    },
  },
};

 

 

Link to post
Share on other sites

You can definitely nest tweens in a timeline - that's no problem at all. I just don't quite understand why you're seeing any benefit in that. A timeline is just a wrapper around tweens. So having one tween in a timeline versus having a single tween (outside a timeline) doesn't really matter. The power of timelines is in sequencing and grouping tweens so that you can control a playhead across many tweens as a whole. 

 

I'm still fuzzy on what exactly the problem is - I think it'd help a **LOT** if you created a reduced test case in codepen with only the absolutely essential code to reproduce the problem. Can you do that? 

Link to post
Share on other sites

Thanks Jack, i guess it would have been way better to just do it from the start :-)

 

if you resize the window..it should fire the animation if the logo touches the colored area,

if you resize it and the logo is in the white area, the tweens should have reseted to their initial color.

 

Anyway, i just figured while making the codepen that it is indeed scrollmagic related...the tweens do reset fine, its just the scrollmagic controller that keeps firing the tweens :-) 

 

 

See the Pen owyBYz?editors=0111 by HendrikEng (@HendrikEng) on CodePen

 

Link to post
Share on other sites

Thanks for putting the codepen together. Yeah, it's super useful for troubleshooting (even just for yourself).

 

We don't really support ScrollMagic (we didn't author that tool), but perhaps someone else around here knows it well enough to chime in with a suggestion. Please do let us know if there's anything GSAP-related that you need help with. 

Link to post
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.

×