Tweener, 4 years later – A post mortem

Posted by Zeh Fernando on 18/June/2009 at 18:21

Another post that has been a long time in the making.

From the start. Until a few years ago, whenever I needed some kind of animation added to the Flash-based work I developed, I used an ActionScript tweening extension I created called MC Tween. This extension was created strictly according to my own tastes; other great tweening extensions already existed at the time, but I didn’t particularly like any of them, as I considered the API for most of them too abstract for what I needed them for. MC Tween used a prototype-based approach, adding new functionality to already existing Object types such as MovieClips. Apparently a few other people agreed with my syntactical approach to programmatic tweening, so it ended up enjoying quite a popularity for a while.

In 2005, however, I started growing increasingly irritated with MC Tween. While it managed to do its job well, ActionScript syntax and paradigm changes (specially the ones introduced with AS2), coupled with my better understanding of object-oriented programming,  made me realize it didn’t quite fit my development workflow very well anymore. What’s more, MC Tween development had begun to grow increasingly stale, and due to its popularity, any sort of experimental feature change was too dangerous to even be considered. It was sort of stuck.

So in june of that year, I started an experiment, creating a new, class-based, tweening extension. Initially, it was instance-based (you created instances of tweens), but it was later changed to a static class that created all your tweenings through a single method call, addTween(), and handled additional features through a few additional methods like stopTween(), getTweens() and such.

The class went through a lot of changes, like having its own name changed (initially called Twina, then Tweener, then ZTweener, then Tweener again), its package moved around (from generic.*, to zeh.easing.*, to caurina.transitions.*) and its API changed a lot, not to mention changing from the instanced approach to a static approach, and other big internal modifications. I think the lesson here – and this is honestly the point of most of this post – is that when you create something for yourself, knowing that no one is going to look or judge the output, much less use it on their own mission-critical work, you allow yourself to be a lot more wrong simply because you’re the only one who has to accept the risk; but, in the process, you find better ways of doing something.

At the time I started this project, Fuse Kit was becoming the most popular tweening extension out there. This was something I did not want to compete with, and I specially enjoyed the freedom of creating something in an iterative fashion, for myself, and changing it to fit my needs on every new project. That’s why Tweener remained under wraps (used only by myself) for quite a while, even though I was using it on every project I had to build for Gringo (at the time).

Tweener was “made public” on January of 2007, almost two years after its first version was created. Nate Chatellier had just joined the team – and created the AS3 version of Tweener – and I thought it wasn’t fair to keep it under wraps forever. And at that point, the API wasn’t likely to change a lot, so input would be appreciated.

Tweener wasn’t something so out of this world, and its API wasn’t anything too brand new either, since the kind of loose approach it took with new tween definition could already be found on several other existing tweening extensions – even AS1-based extensions. Still, it was probably the right syntax at the right time – as it also managed to be quite popular for a while, being even ported to other languages.

That apparently was 4 years ago (judging from the logs, Tweener was started on June 15, 2005). Other tweening extensions exist today – maybe specially popular are Go, TweenLite/TweenMax, GTween, and the new BetweenAS3 – and you know what? It’s that time again. Even if it has a modern AS3 version available, many of the paradigms adopted Tweener are pretty old-fashioned now. I’ve been growing increasingly discontent with it, I’ve learned more, and it’s time to move on.

I could just move on, but I want to have a look back at what worked and what didn’t. Consider this a Tweener post-mortem – my analysis of what I’ve learned these past 4 years and how has ActionScript changed for libraries like Tweener.

So. When moving from AS2 to AS3, there’s one important thing we need to consider: errors, in AS2, are pretty silent. You can try changing properties of a null object and Flash Player will happily oblige. Sure, it won’t work since the object doesn’t even exist, but it won’t give you any sort of error message.

This creates a problem because when you expect something to work tightly, you need several levels of verification to see whether the data you’re getting is what you expect it to be, so you can warn the user otherwise. With Tweener, under AS2, I tried to play it safe for the user: more precisely, I’d check whether objects existed on every update, whether the property being tweened already had a default value.

It turns out that with AS3, this is an unneeded feature. If you try accessing objects that don’t exist, or properties that are not there, the execution of your code will break in all kinds of ways. And this is good – it helps the user understand he’s making something very, very wrong, and that he must fix it.

When ported to AS3, however, Tweener carried many of these AS2 paradigms, so it still has a lot of verifications of that kind. Try to tween the value of a property that doesn’t exist and you’ll see a custom Tweener error, not some Flash Player error.

if (rScopes[0][istr] == undefined) {
	printError("The property '" + istr + "' doesn't seem to be a normal object property of " + String(rScopes[0]) + " or a registered special property.");
}

The same thing happens on every iteration of the property update loop – it checks whether the object being updated exists, whether the property exists, etc. Which is to say, Tweener tries too hard. The side effects of this approach are decreased performance and increased file size.

Other compromise that probably has ties with this AS2 way of distrusting the API user is that, internally, Tweener tries to play as safe as possible, giving the user little room to break stuff. One of the biggest examples of this resolution is that, when you create new tweens, Tweener searches for similar tweens (tweens applied to the same object and same properties with overlapping time) so they can be deleted. While this may make sense, since it’s the expected behavior of an animation afterall, this creates two problems – first, it has never been an option, as the overwriting is always enforced. And second, it introduces a huge loss of performance when creating new tweens, specially if you already have too many tweens in place, because of all the tweens and objects it has to check for overlaps first.

I’ve long been fond of this safer approach, but with increasingly complexity of ActionScript, and increasing performance you can tap into as long as you allow yourself to walk on the edge, it becomes too much of a compromise. In the end, it’s better to make things moderately safe, but giving the user the ability to circumvent issues that may arise with default use.

That is all to say, nowadays I believe tweening overwriting should have been off by default, but offered as a feature (not surprisingly, this is the approach that classes like TweenMax take).

Comparing performance of tweening engines recently made this problem more obvious. While it still holds moderately well with actual tween updating time and pretty well with memory consumption, Tweener has a much harder time the higher the number of tweens it has to create; tween creation time gets exponentially higher, since it checks each new tween against the entire list, and with tween numbers in the thousands it’s quite often that just trying to create the new tweens will take more time than the animation itself, just hanging the player for a while.

Now, Tweener uses a single array for its tween list, and this could be resolved by clever AS3-native tricks, like using a Dictionary instead (again, not surprisingly, this is the solution TweenMax uses, and Jack Doyle was actually the first to mention the technique to me). While doing this might have served as a stopgap, the problem still remains – Tweener just tries too hard to play it safe; to guide the user by the hand, giving him no room to explore if he just wants performance and knows what he’s doing.

That’s why, with today’s state of Tweener, I think this is not a question of optimization anymore. Stopgaps could be made, like the removal of a few checks, the addition of some modern AS3 features like a Dictionary for the list, and heck, maybe even Vectors, but I honestly believe that, by design, Tweener is already past its prime, so those changes would be just that, stopgaps.

So it makes no sense to update Tweener anymore. Sure, it works (specially if you don’t need thousands of concurrent tweens), and will continue to work, but in terms of advancements, it has reached a dead-end. There are now better ways to do things – Tweener is close to that, I think, but because it doesn’t have a lot of room to maneuver, it’s best to just leave the project as it is and move on to something else.

But, here’s something.

While I won’t be changing Tweener anymore unless some new, hideous bug shows up, I’ve made a small update – a new version, 1.33.74, is available both at the Google Code project page and at the subversion server. This update keeps the default tweening overwriting, but adds a new parameter, overwrite (that, when set to true, enables the old behavior) and a new static property, autoOvewrite (that changes the default overwriting behavior). Why is that a good thing? Because it’s faster.

Tweener Comparison

On the above graphic, the blue bar means no tween, the orange bar represents Tweener 1.31.74, and the yellow bar means Tweener 1.33.74 with overwriting set to false.

This is not much, but consider this my thank-you for the support, suggestion, lessons, and overall awesomeness I’ve received over the years due to this project. My most grateful thanks not just to people who helped the project more prominently, but to everybody who helped the project in one way or another.

And since this is some sort of closure, even though this doesn’t have much to do with the post, here’s an interesting graphic I’ve been assembling over the years since Tweener has moved to the Google Code website. It compares the percentages of download of stable versions of Tweener based on the version (see the full download count). It’s slow, but we can see how AS3 is slowly dominating the statistics. It doesn’t include the version released today.

Tweener download statistics

Here’s the same graphic, but without normalization.

Tweener download statistics

As a closing note, I must add that, personally, I’m using slightly different approaches to tweening than what was introduced by Tweener. While Tweener still works and will continue to work, my own workflow has changed slightly. I never use special shortcuts for color tweening and stuff anymore, preferring instead to create separate classes and specific functionality via getter/setters, decorators and the like. Should other people be doing that too? Heck if I know, but the reason why I work in things like Tweener in the first place is because I was happy to work on something that would fit my own development flow well, and making it available to other people later if they wanted it. You can even say it’s a bit of a naive thing to do, and that’s part of the reason why it took me so long to make it public, but I prefer to use something that’s tailored to the needs of a person (as long as I agree with that person) instead of something that’s tailored to the needs of everybody. That’s not to say any of the other libraries out there do this – I wouldn’t know – but rather to explain how is my approach to that kind of library and why I prefer to move on instead of keep hammering on something I don’t think has the best approach.

Nowadays, I’m using mixed solutions for tweening: Tweener for some stuff, an experimental tweening engine I’ve built which fits my workflow very well and that I can abuse when I want top performance (because it doesn’t try anything other that I want it to) for others, and a tweening engine built by the folks from Firstborn of which I’ll be contributing to (as soon as I get to the office – from next week on probably) and that I hope I’ll be able to talk more about the future.

To be clear, there won’t be a “Tweener 2″, or anything like that.

And thanks again!



30 comments posted on “Tweener, 4 years later – A post mortem”

Great post. I actually started to feel a bit nostalgic when I realized that I went through all the “stages” you listed here (MC Tween, Fuse Kit, Tweener and now Firstborn’s inhouse solution). Thank you for all the work you put into making my work easier… Looking forward to have you in the office here…


Posted by Jens on 18/June/2009 at 18:36 ·

Thanks Zeh for Tweener And all the support you’ve given us the last few years! It gave me a jump start into Flash when I knew nothing.


Posted by Mike on 18/June/2009 at 18:49 ·

I hope you don’t mind if I continue to tout Tweener as being the best among my friends and colleagues…even if it’s abandoned. Use of a post-mortem Tweener is like hiring a zombie to do your menial chores, btw. :)


Posted by Don on 18/June/2009 at 18:59 ·

Thanks guys.

It’s just development that has ceased; code will still work, specially in regards to legacy stuff. So I wouldn’t be too worried. My main point is that there’s some good stuff out there, not that it has suddenly became bad.

And that doesn’t mean much really. To put things in perspective, MC Tween still sees around 140 downloads *a day* from my site even though it’s AS1 only and the last version was released 3 years ago. Not everybody needs to live in the cutting edge using stuff that was released yesterday.


Posted by Zeh on 18/June/2009 at 19:13 ·

I’ve used Tweener more times than I count. Saved me a lot of time and effort- remains one of my favorite tweening libraries.

Very interesting post – it’s nice to hear your thoughts on the whole animation stuff, and even how Tweener actually works – which we never investigated before. A shame that Tweener won’t be directly evolving into something more, but I can see why, now.

We love Tweener and the way it works – it’s been a massive improvement over the inbuilt tweening options in AS2 and a great way to transition across to AS3, which we were very thankful for. We still use Tweener and will continue to use it, but we have noticed the syntax being a bit ‘outdated’ – styles change, as you say. Massive kudos for developing and releasing Tweener in the first place though – a real eye opener for how easy things could be, way back when.


Posted by James on 18/June/2009 at 20:04 ·

Thank you Zeh, for all contribution to the Flash Community.
Hope that new things come public as Tweener.


Posted by Pedro Taranto on 18/June/2009 at 20:32 ·

Obrigado, Zé! Você e a Tweener me ajudaram bastante!

What a ride it has been. I remember the day that I discovered MCTween–it was like Flash become exciting all over again. The biggest (and only?) drawback I had to making the AS3.0 jump was the lack of MCTween. So glad you were willing to share Tweener with me at the time. It makes me a bit sad to read a Tweener post mortem, but I whole heartedly agree. Thanks for your amazing contributions to the community Zeh!! I hope Tweener will not be your last open source project. You have too much going on in that great brain of yours to keep it all wrapped up for yourself! =)

Thanks for everything. ~Nate Chatellier

Great post Zeh. Thanks for Tweener, I’ve used it in more projects than I can remember.


Posted by Lawrie on 19/June/2009 at 5:14 ·

No man, thank YOU.

Feels like it was yesterday when I was teaching Actionscript to my workmates and they were laughing at the name of “MC Tween”. You know.. the hip hop stuff… MC Hammer and so on…


Posted by Mr.doob on 19/June/2009 at 10:27 ·

Zeh, can’t thank you enough, i have used your tweening engines starting with MC, for everything I have done in the last 6 years!


Posted by Noneis on 19/June/2009 at 14:25 ·

And now for the question that, I believe, cost many sleepless nights to many of us, what’s “caurina”?


Posted by makc on 19/June/2009 at 17:43 ·

@makc: Oh that’s a good one, everybody asks me that.

Ok, initially the tweening extension used my old package name – zeh.easing.* – and when Nate Chatellier joined the team, and it was about to go public, I didn’t want it to have my name on the package, it’d sound like a solo man thing. I wanted to pick something a bit more abstract so I picked part of my own company’s name.

My own company was called “Caurinauebi”. It only existed on paper, because it was needed for local legal purposes (so I could work for other companies as a freelancer), and it was never advertised. The name was too long so I just picked “caurina”. Since people still had to write imports all the time back in the day, at least when using it inside the Flash IDE, I really wanted something simple and short.

The name “Caurinauebi” is a sort of a personal joke. If you try reading the name in brazilian portuguese, it sounds as “Caught in a web”, which is a music from Dream Theater and, I thought, a good name for a company that did web stuff.

In retrospect the name was a mistake. It’s not abstract enough, it’s just strange. Some people call Tweener “the caurina”. Some people think it’s a girl’s name. Stuff like that.

Nowadays I’ve gave up on trying not to respect the normal package naming conventions and I just use “com.zehfernando.*” for everything (ie, classes at my public svn server). A simpler package name is actually one of the main reasons why I picked this new domain (zehfernando.com) instead of keep using the old one (zeh.com.br).

So here’s an addendum to the post-mortem article: always use a package name that makes sense or you’ll regret it later.


Posted by Zeh on 19/June/2009 at 18:04 ·

Hi Zeh.

I’d like to thank you very much for Tweener. At Gringo, we’ve been using it extensively for years. Tweener is just a workhorse.
Of course, it does have it’s short comings, but tweener has been **the** reliable code infrastructure for us. Never ever we hit a bug with it. This is, to me, the golden standard of code: something you can rely on. All things equal, I’ll take rock solid code to performance any day.

Also, the way you’ve handled Tweener publicly has always been an example. Always replying with great details on the mailing list, and never getting tangled on flamewars, even on blog posts that were really critical of Tweener (some were pretty inaccurate, by the way).

As a side note, a small anedocte that I remember fondly. Once I had a project that when unloading swfs, I needed to “reset” Tweener, remove all tweens. So I asked Zeh over IM if a removeAll would be useful. He replyed “Yes, I guess… wait a minute”. Two minutes latter he came back in IM and asked me to svn up Tweener. There it was, the removeAll feature. Five minutes from the suggestion to working code in trunk. That was sweet.

By the way, the most under appreciated feature in Tweener is it’s tagline: “because there’s infinity between 0 and 1″ .

Thanks for all the hard work, and good luck on your new tweening endeavors.
Cheers
Arthur Debert

Zeh, it’s pretty interesting how something like a tweening engine can have such a big impact on lives and an industry, let alone have people such passionate about the one they use, don’t you think?

Thank you again for all your work – people really do appreciate it.

So now what about the future. You mention some new engines. So what are your takes on say, TweenMax(http://blog.greensock.com/tweenmaxas3/) or even something like the Desuade Motion Package (http://desuade.com/dmp)?

I know Moses attempted to unify things, but that didn’t seem to happen :P

Cheers!


Posted by Andrew on 23/June/2009 at 14:16 ·

@Andrew: thanks. But in the same vein, I think we all have Robert Penner to thank the most.

I can’t give a very deep take about other engines since I don’t use them, but my opinion is – specially with TweenMax – that they’re doing everything right, syntax wise. One of the reasons why I had no problem saying Tweener is sort of stalled from now on is because there’s great alternatives out there. Specially with the way TweenMax/TweenLite treat sequences, maybe – I really dig the syntax and if I had to implement something like this today, it’d probably be too similar to that. So personally I like it. I’m speaking for myself only though.

I haven’t looked at Desuade, sorry.

Go is a nice project in essence. But I guess in the end we all still suffer of the “not-invented-here” syndrome. There’s always a lot of redundancy in libraries for any language. I’m not sure this is a bad thing.


Posted by Zeh on 23/June/2009 at 16:22 ·

Thanks Zeh for Tweener and thanks Nate as well. It was a great key that helped bridge the AS2 and AS3 worlds and got many people and creative teams creating consistent tweening code. Jack Doyle of TweenMax and TweenLite has really taken this to heart and is laser focused on this. The plethora of tweening kits allowed people to get into more as3 kits like 3d, physics etc. I think it was the start of a really great phase in Flash development as AS3 became a real start to a programming platform.

I think many of the decisions that maybe cause it to have some date is the hooks to AS2 style development and the transition. It was perfect for the time and is still used quite frequently by many flash devs I know including myself.

Thanks for your hard work and support of a really great tool. Hopefully Tweener gave back to you as much as it gave to the flash community. Onward!


Posted by ryan on 26/June/2009 at 5:51 ·

Zeh, I’ve got to echo a hardy “thanks” for Tweener and the way you have consistently held a humble, generous posture with the community. I still remember years back when you contacted me after noticing TweenLite. You were completely encouraging. A class act to be sure.

For you Tweener users out there, I think you’ll find migrating to TweenLite/Max to be relatively painless because the syntax is very similar. http://www.TweenMax.com

And for the record, Zeh has an open invitation to help shape the future of the GreenSock platform with any advice from his years of experience. It would be an honor.

You’re an inspiration, Zeh, and I hope you’ll keep sharing. Best wishes in the next season of your career.


Posted by Jack on 28/June/2009 at 3:46 ·

Thanks for the library! I have myself moved to do haXe only for flash stuff, and even there I use a caurina hx port of Tweener sometimes, it saves some time :)

I wonder if you have any other good and modern tweening libraries in haxe to suggest, have you seen or used anything comparable/reliable?

First of all, thank you for Tweener! I too feel a bit nostalgic reading this, since Tweener has been inextricably linked to my own experience and growth as a Flash developer.

When Flash 5 was the new thing, I remember when I began learning Actionscript and began constructing my own onEnterFrame loops and prototype functions to begin animating MovieClips, which at the time was so exciting to me. I later discovered Robert Penner’s easing functions and Flash’s “hidden” tween class, and was happy that way until I started using Tweener.

Tweener is the first library I began using, and until recently was the only library I consistently used on all my projects. Tweener’s “set it and forget it” simplicity has always been a favorite, and the results show. Many projects that use other tween solutions break when you click out of turn. Tweener’s ability to overwrite previously active tweens has been a cornerstone to it’s bullet-proof usefulness in my every day work.

When Grant Skinner released GTween for the first time, I considered changing to another library. The limitations of instance-based tweens and the loss of Tweener’s overwriting was a problem I could not overlook. Despite the difference in performance Tweener still performed as well as I needed in all my projects, and reliability is more important to me than benchmark ratings.

Five months ago I released GTweener, and funny enough, this week I released an update that includes the overwrite feature you just added to Tweener. Yesterday I was visiting Tweener’s documentation and I was surprised when I saw the overwrite property. “Tweener had this all along!” I thought. How comical that we actually developed the same feature within weeks of each other.

On Tweener’s Google Code page, you describe Tweener as “the spiritual successor to MC Tween.” In all reality, GTweener really is my version of a spiritual successor to Tweener.

I have kept performance at nearly the same level as GTween unmanaged, but I have included almost all of Tweener’s standard properties and features as well as new features I have found use for. I would truly appreciate if you would ever find the time to take a look at GTweener and tell me how you like it, and if there are any features that you feel are missing or might even like to see added. I truly respect your work and once again thank you for the invaluable contribution you have made to the Flash community.

http://code.google.com/p/gtweener/

Hello Joshua,

Thanks for the kind words. I’ll have a look at the library when possible.

If you allow me an early comment though, I think the name of the extension is too close to GTween and this might cause confusion. I even had a problem understanding the first sentence that mentioned it until I spotted the difference. Maybe I’m not the best person ever to say something like this since even “Tweener” is a very generic name (in retrospect, I should have kept one of the earlier, more unique alternatives) but regardless, I think it’d work better both for GTween and for your extension (even if even GTween should be supplanted by the new project Grant Skinner is building with Jack Doyle in the future).


Posted by Zeh on 4/July/2009 at 20:40 ·

Thanks very much for Tweener/mctween, it’s been (and still is) a great asset to Flash community.

Thanks. I’m very new to Flash (one month!) and to any kind of programming and scripting and stumbled onto Tweener. In looking at my animations to date ALL of them are based on Tweener. Thanks for the well thought-out code, the great syntax rules, the excellent documentation, and the forgiving way in which Tweener tolerates my feeble attempts to make it do what I want.

I’m going to continue using it until I find some of these limitations you claim are there. I haven’t found any!!

I have a feeling we’ll be seeing something else great from you very soon, with your talent and ability to communicate to newbies like me.


Posted by Matt Zimmer on 10/July/2009 at 13:51 ·

thinks alot. from japan


Posted by maru on 5/August/2009 at 6:48 ·

Hi again,

Thanks for your feedback! I agree that names are important, and after considering and researching tween libraries more, I decided to start a new library called Actuate.

Actuate builds upon what I was doing with GTweener and applies the same concept to any tween library you want to use. This lets you change libraries interchangeably to take advantage of their respective strengths.

I found a library called BetweenAS3 that works faster than any other library I’ve used before, but it only supports Flash 10 and missed certain features I needed. Using a custom actuator, I’m able to add the features I need to use BetweenAS3 the way I want to. I can change to a TweenMax actuator if the project is Flash 9, or a GTween actuator to avoid TweenMax’s license restrictions for certain projects.

Once again I appreciate all of your work, the value you’ve offered us all in Tweener, and showing me that tweening should be fun and easy.

If you ever want to take a look, the project is hosted here:

http://code.google.com/p/actuate

Thanks and good luck to you!

Zeh, I just wanted to deeply thank you for such a robust and useful tool that has served me so well for several years.

MCTween and Tweener has allowed me to really enjoy my work as an ActionScript programmer. Once again, Thanks!


Posted by Felix on 17/August/2009 at 16:15 ·

Obrigado Zé!
Fico meio triste por ver o Tweener morrer… durante muitos anos foi o que usei na maioria dos meus projectos.
Nada como mudar para evoluir. :)

Thanks so much for sharing Zeh, it’s been really great to work with you and Tweener. Good luck in your future endeavors!


Posted by Dave on 4/November/2009 at 19:26 ·

Zeh, Tweener has made me a better person.

It turned me from a designer to a developer and puts me to sleep at night knowing I’ll be able to animate anything with ease. I now describe ANY animation as a tween using Tweener. Timeline wha? Tweener taught me math, sequencing, interpolation, and that things can be easy to use. No Tweener 2? Will they ever make an E.T. 2? Hell no, because they got it right the first time – just like you.

Zeh in my mind, you are a celebrity and Tweener is a legend. Not only is Tweener part of my vernacular but it’s also a word I use to describe “All that is good in the world”.

You’ve inspired me to create my own animation engine, not to replace Tweener, but to compliment it. To bridge the gap between other programming languages, and to keep my coding consistent while protecting my sanity. If you find yourself sucked into the world of WPF and Silverlight http://artefactanimator.codeplex.com/ is there for you. It’ll help you like you have helped me over the years. Surprisingly I didn’t call it XXXTweener cause it would only be considered a cheap knock-off. While it’ll never be better than the original, maybe one day it can make you proud.

Thank-you for such a great contribution to the community and an ever inspiring attitude.

Respect,
Jesse


6 pingbacks posted on “Tweener, 4 years later – A post mortem”

[...] recently came across this post on the blog of Zah Fernando, the creator of Tweener, lamenting his own engine’s death. It [...]

[...] recently came across this blog post by Zah Fernando, the creator of Tweener, lamenting his own engine’s death. It [...]

[...] 13, 2010 Uncategorized Leave a Comment Tags: animation, flash, wonderfl Tweener may be dead, but people are still posting to its mailing list. This time someone posted an idea so awesome that [...]

[...] GreenSock Tweener停止开发 Tweener, 4 years later – A post mortem AS3.0 的 TweenLite-輕量級tween引擎! __spr_config = { pid: '4ecb0de9396cef74b50000b2', [...]