Jump to content
GreenSock

Eunomiac

Possible to Define a Class Whose Objects Can Be Passed as Targets for GSAP Methods, Instead of an Element/Selector/jQuery?

Go to solution Solved by Eunomiac,

Recommended Posts

I'm working on a module for a virtual tabletop app (Foundry VTT), which provides an HTML sandbox for rendering game items.  I'm representing these rendered objects as instances of classes I've defined.  I'm currently implementing GSAP functionality simply by defining an "elem" field on these classes to hold a reference to the HTMLElement object, which I pass to gsap as one would expect: gsap.to(myClassInstance.elem, {...})

 

However, my module is growing more complex (surprise) and I've found myself wishing that, instead of only passing the HTMLElement, I could pass the whole object as a target: gsap.to(myClassInstance, {...}).  Is it possible, by defining the right set of methods, getters, setters and fields in my custom class (as aliases or overrides), that I can "trick" gsap into thinking those instances are valid targets, i.e. by making sure that I've provided aliases for any fields or methods gsap expects to be there? (Unfortunately, I have to extend my classes from a Foundry base class, so I can't, say, extend the HTMLElement class --- which I assume would work.)

 

If this is possible, how might I get a list of the methods and fields gsap expects to exist, and that I'd thus need aliases for --- at least regarding the basic to / from / fromTo Tweens, as well as Timelines? (I have .d.ts TypeScript files for the GSAP types, so perhaps that's where I should look for those necessary fields/methods?)

 

In case I'm stuck in the mire of an X/Y problem, let me back up slightly and describe my intentions:  I want to be able to send multiple instances of my classes (as an array of objects) as targets to a single gsap Tween, so that I can stagger their animations (among other things), without having to animate each one separately.  And, for reasons related to Foundry's architecture and the way I've defined my classes, it isn't always feasible for me to simply convert my objects to elements (e.g. gsap.to(myClassInstances.map((obj) => obj.elem), {...})) --- not to mention I have some unique methods/fields on my objects that I'd love to have access to within gsap's callback functions, without having to change the callbackScope.

 

(Hope it's okay that I've left out a demo; I didn't think one would be necessary or helpful with this particular question!)

Link to comment
Share on other sites

If I understand correctly, you should be able to do all that you describe. 

GSAP is built to animate any numeric property of any object JavaScript can touch.

Those properties can also be "function-based"

It's probably one of the most under-utilized features of the platform.

 

I updated two demos from the archives

 

Custom Object with getX and setX methods

See the Pen mdXmLXg?editors=1010 by snorkltv (@snorkltv) on CodePen

 

For this to work the functions must have matching names like setMyProp / getMyProp or setOpacity / getOpacity.

 

Custom Object with getter/setter function

See the Pen JjpNvLv by snorkltv (@snorkltv) on CodePen

 

Both use an onUpdate on the tween for the purpose of just proving the values are being changed. Your objects can have their own code to handle whatever rendering or refreshing of the canvas that needs to take place when values change.

 

Original thread for reference below. There is an additional demo in there from @GreenSock

 

 

  • Like 3
Link to comment
Share on other sites

  • Solution

Well, that was certainly a lot easier than I expected! 

 

I was about to ask some follow-up questions, then decided to see if I could answer them on my own, using the pens you posted as my guide. Here's the CodePen I ended up with.  I think it does a nice job of extending the pens you posted to class syntax, as well as showing that this also works with multiple objects animated in a single Tween, even with a stagger.  Perhaps someone like me might find it helpful down the road?

 

I did have one question as I was working on it:  Are there any performance or best-practices issues with my use of gsap.set within the setters that will be targeted by the tween (e.g. set posX(x) { gsap.set(this.elem$, {x}) }?  I just want to confirm there aren't any recursion issues or other potential consequences of "nesting" gsap calls in such a way?

 

See the Pen gOvRWNg?editors=0011 by eunomiac (@eunomiac) on CodePen

Link to comment
Share on other sites

Thanks for putting in the time to experiment and provide that demo. Great job!

 

A set() is a tween object with no duration so there is a little bit of overhead involved. A new object is created with its own methods and properties.

In your situation you are going to be using many set() calls 60 times per second. There could be some performance implications with lots of objects animating at once.

 

I don't have time to implement this for you now, but take a look at quickSetter() 

 

Also, @GreenSock may be able to weigh in with some better advice

Link to comment
Share on other sites

9 minutes ago, Carl said:

I don't have time to implement this for you now, but take a look at quickSetter() 

Hot damn, that's definitely getting added to my toolbox, thanks! I don't think I'll be animating 50 items at once like I did in my demo, but another challenge I'm just about to tackle involves scaling the timeScale of a Timeline based on the proximity of the mouse cursor, so I'll be experimenting with whether quickSetter will help with that!

 

Just to clarify, though:  The issues you raised are true regardless of whether you're animating objects via class instances, or more conventionally via selectors/HTMLElements, yes?  By which I mean, you aren't describing issues that I'm introducing by animating things via class methods, correct?

  • Like 1
Link to comment
Share on other sites

2 hours ago, Eunomiac said:

The issues you raised are true regardless of whether you're animating objects via class instances, or more conventionally via selectors/HTMLElements, yes?  By which I mean, you aren't describing issues that I'm introducing by animating things via class methods, correct?

Right. The class thing doesn't really matter. 

 

Carl's advice regarding quickSetter() is solid. It's just an optimized way of doing a set() many times on the same target/property. 

  • 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.
×