Jump to content
Search Community

My "generic object onUpdate" approach

katerlouis test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Back in the days, wise master Blake showed me a great trick, when I was fighting with a gradient tween (or was it clip-path?)

 

He just made an object containing a value to be tweened + a function that was called onUpdate of said tween. This function then worked with the value that is currently being tweened. Brilliant! This was when the Matrix flickered right before my eye– for a tiny moment; long enough to hammer the last nail into CSS and jQuery animations coffin.

 

It looked somewhat like this:

 

var generic = {
  elem: ".box",
  x: 0,
  update: function() {
    var percent = this.x * 100; 
    var value = "polygon(0% 0%, 100% 0%, 100% "+ percent +"%, 0% " + percent + "%)"
    TweenMax.set(this.elem, { webkitClipPath: value })
  }
}

// used like this
TweenMax.to(generic, 1, { x: 1, onUpdate: generic.update.bind(generic) })

 

Great way. Lots of control; easy to read. Accessible.

 

But for me, very personally, in almost all cases this felt like an "overkill"– 
I never needed that object again, or even worse, needed multiple "instances" of object.

I always had to scroll up to fiddle with details, play around to find what I need.

All this binding, writing the object name so often. And then I want to change the name, because .. reasons– I forget– and. Ah!

 

I like to stay inside the timeline, as long as possible.

Most of you seem to disagree with me on this one, when I bring similar issues to you, where I don't want to leave the timeline.

 

Lastly the Matrix reveals itself way more often to me and starts to lose it's blur.

And now it hit me. Creating an object on the fly to tween is no biggie, but getting access to the tweened value for the onUpdate function?

As I become more and more comfortable with logging and sniffing what the tweens have to offer, I found that `this.target` inside "onUpdate" gives me exactly what I needed.

 

 

// somewhere in a timeline or as a standalone Tween
.to({ x: 0, elem: ".box" }, 1, { x: 1, onUpdate: function() { 
		// this.target is the key to the properties!
		var percent = this.target.x * 100; 
		var value = "polygon(0% 0%, 100% 0%, 100% "+ percent +"%, 0% " + percent + "%)";
		TweenMax.set(this.target.elem, { webkitClipPath: value })
	}
})

// or 
.fromTo({}, 1, { x: 0, elem: ".box" }, { x: 1, onUpdate: function() { ...

// or
// tween the actual element in order to access function based values / index etc., or staggering?
// and add your own custom properties
.fromTo(".box", 1, { customX: 0 }, { customX: function(index, elem) {
	if (index % 2 == 0) return 1;
  	else return -1;
}, onUpdate: ...
  
// or
// just add multiple colors for a gradient change
.fromTo(".box", 1, 
	{ customColor1: "green", customColor2: "green" }, 
	{ customColor1: "yellow", customColor2: "blue", onUpdate: ...

 

 

 

I wanted to share this with you guys, just in case out there are people with the same fear and anxiety to leave the timeline 8)

 

 

Louis out.

*macdrop*– waitwhat?!

–****!

See the Pen yoPoKO by katerlouis (@katerlouis) on CodePen

  • Like 1
Link to comment
Share on other sites

Got a question–

My idea of tweening the actual object and add my own custom properties in order to maintain "staggerability" and function-based values doesn't seem to work.

 

Why is that?

 

// even without function based value it is not working
.fromTo(".box", 1, { customColor: "green" }, { customColor: "red", onUpdate: function() {
    TweenMax.set(this.target, { backgroudnColor: this.target.customColor }
  }
})

 

 

And one more question:

What is "this" refering to as a value inside the properties' object?

It should be the object containing the properties itself, right?

// I don't want to hardcode the "from" color
// but it needs to be set, because it is a custom value
.fromTo({}, 1, { elem: ".box", customColor: $(this.elem).css("background-color") }, { customColor: "green" }, onUpdate: function() {
    TweenMax.set(this.target, { backgroudnColor: this.target.customColor }
  }
})

 

Link to comment
Share on other sites

It's good to experiment!

 

R9qBSxo.jpg

 

 

I have to ask, what do you use for editing code? I think most of your reasons for trying to stay in the timeline wouldn't exist if you used a better editor. One that understands your code and can do refactoring, like VS Code or WebStorm

 

VS Code is free, so go download that and try some of these things out.

  • Hover over something. It will tell you what it is. Adding TypeScript definitions will make it even smarter.
  • Rename something using F2. It will change the name of everything that references that symbol. This is not the same thing as find and replace, as that will replace everything that matches.
  • Press F12 to jump to where something is defined.

Forgot what generic is, or want to change it?

 

Uh3bDeB.jpg

 

 

Press Option+F12 to peek at its definition. This will open up a mini-editor, allowing you to see and edit it. No more scrolling :-o

 

4YJ0Csz.jpg

 

 

I'll answer your questions in another post.

 

Link to comment
Share on other sites

I use Sublime Text 3 on Mac and am very happy with it. The mini-editor looks cool. I am sure there a similar solution available for ST3, – the big community has so many packages / plugins to offer! I tried quite some editors until I got stuck with ST3; love remains until today.

But even with a feature like this my opinion remains. Where are the benefits with a separately stored object, you only need once.

 

While doing all this I fell in love with the idea to tween the actual object, like you would do regularly, but add your own custom properties. But somehow this isn't working :'(

 

– even if it worked, would function based values work for this custom property? I often find myself applying logic to the values when there's more than one element tweened in one tween (which is the case 75%+ of the time?)

 

– and staggering should also be possible, right? W000h000.

Link to comment
Share on other sites

About the parentheses comment in your code, they can be omitted if you're not passing anything in to the constructor.

// valid, but looks odd to me
var tl = new TimelineMax; 

 

And using bind on a object like I showed you could be done differently. If you need multiple instances of something, make a factory function, a function that will return an instance for you. 

 

About the staggerTween. Try logging out the target. You're using jQuery, so the target is going to be a jQuery object. So using the target in your update function is the same as doing this. You're updating every element inside that jQuery object at the same time.

TweenMax.set([element1, element2, element3, element4], { ... })

 

Where did the customColor go? GSAP knows you're targeting an element, so it's going to tween the customColor as a CSS property. It's not a valid CSS property, so you won't see inline, but it will be on the element's style object.

 

For a non-stagger tween, this would work. It will put the customColor on the actual element.

TweenMax.fromTo(".box", 1, {
  autoCSS: false,
  colorProps: { customColor: "green" }
}, {
  autoCSS: false,
  colorProps: { customColor: "red" },
  onUpdate: function() {
    TweenMax.set(this.target, { backgroundColor: this.target[0].customColor });
  }
})

 

Overkill? 

 

Since you like experimenting, try playing around with using getters / setters. Did you see this post I made?

 

And check out these articles...

http://lea.verou.me/2015/04/idea-extending-native-dom-prototypes-without-collisions/

https://www.sellarafaeli.com/blog/native_javascript_data_binding

 

What going on here?

Object.defineProperty(HTMLElement.prototype, "reneClipPath", {
  get: function() { 
    return this._reneClipPath; 
  }, 
  set: function(value) {
    this._reneClipPath = value;
    this.style.clipPath = value;
  },
  configurable: true
});

 

:-o

 

 

 

 

 

 

  • Like 4
Link to comment
Share on other sites

You know how to shut me up–

A lot to swallow and digest– I guess you'll see a post here with lots of question marks in near future. 8)

 

Thank you!

 

 

[EDIT]

Question 1a):

I was wondering why the clipPath pen had no clipPaths. I am on Safari; in Chrome it works–

What is the best way to make it work in all browsers? I used "webkitClipPath" in my examples and it works in both Safari and Chrome– Is GSAP capable of prefixing? Maybe when the property is provided in it's CSS form as a string "clip-path"? (thats what I assume autoCSS does–)

 

Question 1b):

Do I get right, that you tween the "polygon"-string as a whole? How comes GSAP can do that?

 

Question 1c):

When GSAP can do that, tweening a clipPath wouldn't require the onUpdate approach and/or customProperty approach at all, right?

 

Link to comment
Share on other sites

I added webkit to that demo. GSAP will add a prefix, but you were already using a prefix. Although you had webkitClipPath. I think it should be WebkitClipPath, but it seems to work with both WebkitClipPath and webkitClipPath. Not sure what's going on there.

 

Check out the docs to understand what autoCSS does. 

 

The tweening as a string works by finding the numbers that are different, so it would split it up kind of like this. The numbers have to match up somehow. You couldn't have like extra points in that polygon and expect it to tween correctly. That would need a plugin similar to how morphSVG works.

// strings
"polygon(0% 0%, 0% 100%, 0% 100%, 0% 0%)"
"polygon(0% 0%, 0% 100%, 100% 100%, 100% 0%)"

// difference
["polygon(0% 0%, 0% 100%,", 0, "% 100%,", 0, "% 0%)"]
["polygon(0% 0%, 0% 100%,", 100, "% 100%,", 100, "% 0%)"]

 

And I don't know why using a string  like that doesn't work with clip path. That's something @GreenSock would have to answer.

Link to comment
Share on other sites

Good news, @GreenSock took a peek at the clip-path issue and made some tweeks so the complex strings should work now. No need for generic object stuff. Give this a look:

 

See the Pen JyOVqp by GreenSock (@GreenSock) on CodePen

It loads a beta version of TweenMax with CSSPlugin: https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/TweenMax-latest-beta.js

 

As Blake noted earlier its important that the values you are tweening from and to have the same number of numbers.

  • Like 3
Link to comment
Share on other sites

So all of this was for nothing? :D:D Well – I learned a ton 8)

When can we expect this change to be in a stable release?

 

Again: clipPath only doesn't work in Safari 9;

Blake said GSAP will add a prefix, so why won't it work?

I am afraid of doing it wrong by using "webkitClipPath" – and what if I wanted to use both, "webkitClipPath" and "clipPath", and hell, even "WebkitClipPath"? How would you do that?

 

I'd appreciate some more insight on the prefix thing from the Elders.

 

 

PS: If you could now also hotfix the Chrome bug I'm experiencing *shakingbrandy* 8)

 

Link to comment
Share on other sites

GSAP adds prefixes for supported CSS properties, yes, but clipPath isn't one of those. To fully support it, we'd need to add a ton of extra code that isn't very feasible at this point. It's not even supported at all in several browsers. And it can technically be a URL, a rectangle, circle, polygon, etc. - trying to accurately parse and interpolate between them all is a very tall order. Given the poor browser support and all the kb necessary to do that, I'm not inclined to add official support. You can, of course, use an onUpdate and GSAP's built-in string parsing to do whatever you want. 

Link to comment
Share on other sites

My question aimed more for the property-name itself.

"clipPath" doesn't seem to work in Safari (9) – "webkitClipPath" does; I am confused now on what to do. 

Would it be wise to just tween both properties? If yes, what's the best way to do this? Is there some sort of

(clipPath && webkitClipPath): "polygon( ... )" magic I don't know about?

How would you tween both?

Link to comment
Share on other sites

Not just safari 9, doesn't work in ie 11 or below. Which is above 13% of the market. I keep seeing people use ClipPath, I would love to use it. However, 2 out of 10 people will see it as broken.  Maybe am wrong, let me know. 

 

Link to comment
Share on other sites

I think the simplest thing to do here is just use a very simple plugin that senses which property name to use and then just works accordingly (and completely ignores things on browsers that offer no support). I whipped one together for you: 

var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window;
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() {
    "use strict";
    var cs = getComputedStyle(document.documentElement),
        prop = cs.webkitClipPath ? "webkitClipPath" : cs.clipPath ? "clipPath" : null;
    _gsScope._gsDefine.plugin({
        propName: "clipPath",
        API: 2,
        version: "1.0.0",
        overwriteProps: ["clipPath"],
        init: function(target, value, tween, index) {
            if (prop) {
                this._addTween(target.style, prop, getComputedStyle(target)[prop], value, prop);
            }
            return true;
        }
    });
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); }

 

Does that help? 

 

You just load that once up front, and it takes over all your "clipPath" tweens. You don't need to mess with adding webkitClipPath conditionally or anything. 

  • Like 2
Link to comment
Share on other sites

19 hours ago, rgfx said:

Not just safari 9, doesn't work in ie 11 or below. Which is above 13% of the market. I keep seeing people use ClipPath, I would love to use it. However, 2 out of 10 people will see it as broken.  Maybe am wrong, let me know. 

 

That's why I only use it as a reveal mechanism for the mobile menu. 

If clipPath is not supported, the menu background flashes in and the items tween in from the left.

A buggy clipPath is way more troubling that an instant background–

 

I hope you don't expect me to understand this architecture. The syntax alone looks like dark magic to me! 

I will play around with this by time– right now I need to concentrate on the flicker-bug.

Thank you :)

 

(Some wanna-be funny comments to illustrate my confusion and sheer jealousy of them'mad-skillz)

var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; // Jeez
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { // Who pushes who?
    "use strict";
    var cs = getComputedStyle(document.documentElement),
        prop = cs.webkitClipPath ? "webkitClipPath" : cs.clipPath ? "clipPath" : null; // Hey! I got that!!
    _gsScope._gsDefine.plugin({ // w00t?
        propName: "clipPath",
        API: 2,
        version: "1.0.0",
        overwriteProps: ["clipPath"],
        init: function(target, value, tween, index) {
            if (prop) {
                this._addTween(target.style, prop, getComputedStyle(target)[prop], value, prop); // I give up–
            }
            return true;
        }
    });
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); }

 

 

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