Closed shshaw closed 3 years ago
I think cash is pretty good as is, feature wise. For animation, I wouldn't use anything other than velocity for most implementations. Something like spinning an element when clicked enters the territory of css animation based on class changes.
Yeah, I use CSS animations / class toggles wherever I can but programmatic animations are still a need. Velocity isn't any better in terms of file size ( 30+kb minified ).
When I don't need a boatload of features, I try to find something lightweight & performant. A simple $().animate
would be quite useful as an extension for those situations, but I understand if it's not something you think cash should support.
Here's a basic conversion that animates CSS properties on collections in 1.8kb: http://codepen.io/shshaw/pen/WwLKVY?editors=0010
Definitely could be simplified more, especially cutting extra features that jQuery.animate doesn't have ( yoyo
, reverse
, variable duration
, repeat
, delay
, pause
, etc. ). Gets it down to around 1.3kb at first pass
Adding transform ( x
, y
, scale
, rotate
) support would be pretty easy and would allow animations to be even more performant.
Velocity.js is great for programmatic animation but i do agree with @shshaw in that loading velocity for some basic animations via CSS is nearly always total overkill.
Imagine simple stuff as show/hide or page-object transitions with 'stagger'.
With Custom Builds (issue #94), you could have a cash's core + css + animation in ~4kb, 2kb gzipped for handling all sorts of complex animations. That would be a lot of functionality in a tiny package.
Basic transform support with prefixed properties support. Compresses to about 2kb, and might be trimmable.
Needs support for reading existing transform properties, potentially building a transform matrix instead of the individual transform functions.
@shshaw So will this happen? Animation or motion in UI really is pretty much now but what technique(s) that are used is in constant flux (latest being the "FLIP" technique by Google) so basic wrappers and some extensions would be a great thing since such base extensions easily can be rewritten or forked to work with A, B or C etc.
@tommiehansen FLIP may be too heavy handed of a technique for a general animation engine. Based on what I've seen, FLIP works well when you're planning & coding every piece of it and only animating x, y, scale & opacity, but I don't know if it would work well in a general setting. I'll have to look at FlipJS to see how they handle it.
That did make me think: in keeping with Cash's 'mission' of using native browser APIs, we could use Element.animate() if available, and fall back to requestAnimationFrame. Browser support isn't the best, but once Firefox enables it by default in the next version then you're at about 50% coverage. Shouldn't be hard to adapt my implementation to do that.
@shshaw Yes, that's why i wrote about techniques being in 'constant flux' and the anim engine therefor needing to be very generic. It seems i either was not clear or you just didn't read what i wrote. :)
And yes -- using Element.animate() + fallback would be great.
@tommiehansen I just misinterpreted your mention of FLIP as a recommendation.
As for your original question: 'will this happen?': I've gotten to go ahead from @kenwheeler. I'll start a branch for this after I've cleaned up my concept a little bit. Contributions are welcome. Don't know how quickly I'll be able to roll it out, but an alpha should be available soon for testing.
Here's what's on my list for $.fn.animate
features. Most are already implemented in the latest prototype, but need some cleanup and fine tuning.
List now moved to main issue
Any other essentials I seem to be missing?
Regarding missing features:
delay
option before starting animation?Most of those I have an implementation of, but I've trimmed out. These specfic items wouldn't add much weight.
Reverse is good for playing something in reverse and is helpful. Need options to change duration and easing though since one often want to animate in with A but out with B.
elem.animate({ y: 0, opacity: 1 }, { duration: 500, easing: easeOutQuad }) elem.reverse({ duration: 250, easing: easeInQuad })
Another thing is staggering which is always useful when one animates a group of elements.
Examples http://codepen.io/tommiehansen/full/QNRYJR/ http://codepen.io/tommiehansen/full/ZWNRoY/
Cool demos, @tommiehansen. I think having iterations
and direction
properties makes sense, and shouldn't add much weight.
Btw -- seeing as the Web Animations API is on the rise an idea would be to use that syntax because that would make it much more easy to transition over in the near future. Chrome and Firefox already have support for it and the MS Edge team has flagged it from 'under consideration' to 'medium priority'.
The syntax is pretty straight forward and it shouldn't be too hard for anyone that have ever used something like jQuery to use WAAP.
I'm making a cross between the Web Animations API and the jQuery.animate API to allow for complete
and start
callbacks. I am also not supporting the full keyframes interface like the Web Animations API, but simply animating from the current state to the state provided as the first parameter.
Here's the latest prototype. Still a bit rough at the moment, but it is working with objects and elements (with and without Element.animate) and supports transforms.
// Animate from the current state to these properties
var animateTo = {
x: '200px', // translateX shortcut
scale: 0.5
},
animationOptions = {
// jQuery-like animation options
start: function(){ console.log('Animation has started'); },
complete: function(){ console.log('Animation is complete!'); },
// jQuery/Web Animation API equal options
easing: 'linear',
duration: 400,
// Web Animation API style options
iterations: 2,
direction: 'alternate',
delay: 1000
};
$('#myDiv').animate( animateTo, animationOptions );
The Web Animation API's fill
was preventing further transform
animations from working, so I have to manually apply the styles after the animation is complete in order to prevent the element from jumping back to the original state.
Feel free to fork & play around with that. Let me know if you catch any oddities. I'm aware of a few issues, but the functionality is about there.
Tested it briefly. The easy stuff, A -> B seems to work as expected.
But something that's immediately noticable is that the complete function is broken since it fires once per item in a collection. So complete: function(){ myComplexFunction(); }
would fire once per item in a collection. Need to check length
of collection and fire the complete action after the last iteration has completed its animation.
If it fired once per collection it would be easy to do animate 1 > 2 > 3 like this: http://codepen.io/tommiehansen/pen/Razoex?editors=0010
...or even write a little function that iterates over an array of frames and just fires them one after the other.
For jQuery.animate: "If supplied, the start, step, progress, complete, done, fail, and always callbacks are called on a per-element basis;", which is inline with Web Animation API's individual animations and finish
event.
If that's how we proceed, then this
for the callbacks should be the element being animated, so as in your example, you could do this:
el.animate(frames[0], {
easing: opts.easing,
duration: opts.duration,
complete: function(){ $(this).animate(frames[1], opts); }
});
Perhaps two separate callbacks are needed: complete
for each element and collectionComplete
(or allComplete
?) run when the collection is finished animating, which would be especially useful if a stagger
is implemented.
I have a newer prototype that's trimmed off a full kb from the minified version (from 4.5kb to 3.3kb) while retaining the current functionality, and the code should be a little easier to parse.
It's still likely that we'll need to implement a transform matrix conversion, and I'm looking at a Bezier curve convertor that should allow us to have parity with CSS easings.
I've updated the original issue with the essential features list and some possible features. Take a look and let me know if anything we've talked about is missing or any other features you've thought of.
Latest prototype up. 4kb minified, 2kb gzipped with staggering, Bezier conversions and Transform matrix calculations (buh, what a nightmare).
The current CSS bezier conversions for ease
, ease-in-out
, etc, aren't 100% identical. Need to tweak the curve values.
You work fast, trying it out now. :)
Edit: It seems to work great, the only caveat is of course the lack of being able to easily create more then two frames since multiple complete
quickly becomes very messy. See my updated example at http://codepen.io/tommiehansen/pen/Razoex?editors=0010
You said that you'd leave out support for it though so it isn't a bug or fault per say.
Velocity.js does have it in this regard with its sequence, check out a very old example i made: http://codepen.io/tommiehansen/pen/netCu?editors=0010
Click the Demo loop (infinite)
button to play all the sequences. As an example only a single click on the button "popular photos" triggers 4 animations (0: scale down/up button, 1: slide out button, 2: slide in div, 3: fade in images with staggering).
Glad it's working as expected! Feel free to run some performance tests, especially compared to jQuery.animate and Velocity. I'm not very familiar with Velocity.
I haven't ruled out a allComplete
callback yet, but I'm trying to figure out the appropriate way to implement it. I think a callback is necessary per element, especially with stagger, but not sure what the best way to distinguish the two would be.
complete
and allComplete
complete
and finish
complete
and done
each
and complete
elementComplete
and complete
Any thoughts?
Compare against jquery.animate seems redundant. No one really uses jq animate for 'real' animations. Against velocity it would be raw js animations vs css3 animations, there are already stuff on that. The big diff is that css3 animations is native to the browsers which, in my opinion, is always better.
Something that is slighly urgent, for dev-purposes at least, is some sanity check for infinite loops. My browser constatly hangs when trying loops at Codepen.
The complete
per element is quite odd. Depends on how one see an animation. I don't really have an answer on that except that it depends on if one see a "frame" or an animation as something that occurs (which it technically do) on multiple items.
Another "problem" with just A > B animations is that these can already be done sufficiently in plain CSS and JS just like @kenwheeler said and if one needs simple callbacks one could just include something like ba.js, https://github.com/Arood/bajs
Before you write more code maybe you could/should check out how the WAAPI handles these things, this is a good guide: http://danielcwilson.com/blog/2015/07/animations-intro/
In trying to really test the latest build i began trying to recreate http://codepen.io/tommiehansen/pen/netCu?editors=0010
..here: http://codepen.io/tommiehansen/pen/yOdEZm?editors=0100
Opacity doesn't seem to work for some elements and the lib quickly becomes annoying due to the lack of any sequencing, que-system or timeline of any sorts. Even the old jquery.animate handles ques (chaining):
element
.animate(props, opts)
.animate(props, opts)
.animate(props, opts);
But chaining in that way isn't really good because that too becomes a bit too much as soon as one reaches over 4 frames, that's why libs like Velocity js got a sequence func:
var frames = [
[{ props }, { opts }],
[{ props }, { opts }],
[{ props }, { opts }],
]
$.runSequence(frames);
A diff in Velocity JS is also that it reads the previous state, something that WAAPI doesn't seem to do at the moment. This makes it easier to create sequences since one doesn't need to write all props for each and every frame within a sequence.
Also note that everything 'fun' in Velocity JS requiers the UI-extension which adds even more bulk (33kb more).
Not sure what the issue with Infinity
iterations would be, but I've added a check that may help. It's working as expected for me, so can you send an example of the freezing?
I have been following the WAAPI documentation and implementing everything as close to that as possible.
Regarding callbacks, WAAPI animations are all per element, and the finish
and cancel
event callbacks are also per element. CSS animations callbacks via animationend
events are also per element. Again, I'm not ruling out an allComplete
, just explaining why a complete
( or finish
) per element is needed.
The real advantage with $.fn.animate
and other animation engines is being able to set up and call animations on the fly. A to B animations in a programmatic way, (not known ahead of time, as a CSS animation could handle that) especially with looping and cross browser support are not exactly simple in plain JavaScript, unless there's a technique I'm unaware of, for a few reasons:
I do like ba.js's way of creating a CSS animation that's applied to the element. A method like that could potentially save a good bit of code, and would likely be a worthwhile fallback all the way to IE10. However, JS animations would override existing CSS animations and could not be combined, though that may already be the case when animating transforms in particular.
Overall, it sounds like you mainly want a way to set up more complex animations in a CSS keyframes style syntax, which may be too far out of scope for this.
Sorry, was posting that as your latest message came through, so there may be some redundancy.
Sequencing isn't far fetched in the manner which you describe ( array of arrays featuring properties and options ), as it would just be re-calling Animate
for the next set of frames at the end.
Ok, then implement an allComplete
, allDone
, or something that somehow describes that it is something that is triggered after an array of elements has finished animating.
Sequencing and/or simplified sequencing would be awesome.
Don't feel that what i write about is too complex. Sequencing is basically just a check for the last elem on the next 'frame' as you say. What you could do is check if the props is an object or an array and then just trigger sequencing if it's the latter.
It's on the list. Still figuring out the best way to implement and best naming conventions, so any suggestions are welcome.
I'm also seeing some issues with opacity. Not sure where that's coming from.
What you could do is to try to re-create my re-creation to get a better feel of the general pitfalls: http://codepen.io/tommiehansen/pen/yOdEZm?editors=0010
Just updated the prototype with an allComplete
callback.
Trying to figure out the opacity bug currently, but the overall setup now that allComplete
is in place is better for a simple sequencing setup.
The expectation would be that the next sequence event would fire after all staggered elements have finished animating, correct?
Ah! Looks like I was removing some properties from the original end
object, so opacity would only apply to one.
The prototype has been updated. 3.83kb minified, 1.98kb gzipped
Tommie, give the latest prototype a try. I think you'll like it ;-)
Compiled Size: 2.05KB gzipped (3.98KB uncompressed)
Probably need to change the callbacks to be frameComplete
and allComplete
Yes, that would be the expected behaviour. Has nothing to do with staggering though but rather has to do with collections, collections can be anything like $('h1, p, .box')
Say one would want a startpage where one slides up a bounch of elements and after that has completed show a background image or start a video in the background. That's something that is more common then my box-thing.
Yes, i also saw you added the whole lib as a pen so that it can easily be included -- great. :)
Trying the card-animation with the new stuff now.
Your changes really brings the code needed to written way down now, but i still hit a speed-bump quite early since there is no simple way to run sequence A on elem 1 and then sequence B on elem 2 without the code starting to get messy. Now it requires one to add "allComplete" to the animation frames themselves or maybe i've misunderstood how it works.
var myFrames = [
[{ y:0 }, { duration:100 }],
[{ y:100 }, { duration:100, allComplete: function(){ /*messy */ } }],
];
For ones sanity one really want to separate complete
-calls away from any sequencing:
elem1.animate( myFrames, { allComplete: function(){
elem2.animate( otherFrames );
})
Even that quickly becomes messy though, so:
var myFrames = {
[{ elem1, prop2, prop3 }, { opts1 } ],
[{ elem1, prop2, prop3 }, { opts2 }],
[{ elem2, prop2, prop3 }, { opts3 } ],
};
$.animate( myFrames, { allComplete: function() { console.log('finished sequence'); }})
But the latter would move the entire thing away from the WAAPI which might not be that wise. The WAAPI is hardly the gold standard though, there's tons of stuff that's still missing and the "grouping" and "keyframeEffects" (effects ... ?!) is odd and alien at best; http://danielcwilson.com/blog/2015/09/animations-part-4/
Yes, you can include $.fn.animate using this pen.
To clarify some behavior:
// You can use one keyframe & options object.
$('h1').animate({ x: 200 },{ duration: 1000 });
// Or you can use a keyframes array
$('h1').animate([
{ x: 200 } // keyframe 1 (not the starting state, but the first keyframe to animate to)
{ x: 100 } // keyframe 2
[{ x: 0, rotate: 360 },{ duration: 500 }] // keyframe 3, with custom options. These options will be merged via `$.extend` with the global options.
],
{ duration: 1000 } // Options for all keyframes
);
Right now, I'm working on the callbacks and this is what I have so far:
start: noop, // animation start
complete: noop, // animation complete
itemStart: noop, // an item's animation has started
itemProgress: noop, // progress on any item in the animation
itemComplete: noop, // an item's animation has completed
frameStart: noop, // a new keyframe has started
frameComplete: noop, // a keyframe has been completed
The prototype has been updated with the new callbacks.
With the new stuff a complete' in a sequence seems to act as a allFramesComplete
so this sort of works:
$('button').on('click', function(){
var $t = $(this);
$.animate($t, buttonFrames, {
complete: function(){
$.animate($('.back'), backFrames, {
complete: function(){
$.animate($('.backImg'), { opacity: 1 }, { duration: 200, stagger: 60 })
}
});
}
});
})
But it gets quite messy quite quickly. It does almost work though, the opacity for the images last in the que refuses to animate for some reason.
Edit: They do actually animate but only if i also include a transform value.
Yeah, complete
means "the entire animation has complete", reference my list above for the callback definitions.
This isn't intended to be TimelineMax or anything. Not sure how sequencing is handled in Velocity (I cannot parse their documentation, what a mess!), but sequencing multiple animations across different elements is a bit beyond the scope of this. The 'callback soup' like you have would be how you'd handle that for moderately complex animations. Anything beyond that, and you'd be better served by a full animation library.
The opacity issue may be related to a cash bug I've discovered. You can't set $(el).css({ opacity: 0 })
or any other zero / false-y value. Looks like another version push is coming :-)
It almost is great though, so props for that. :-) And that it is almost TimelineMax isn't such a bad thing. Even if only using one callback to trigger animation #2 it's still much more then most libs do or can do comfortably.
So now http://codepen.io/tommiehansen/pen/yOdEZm?editors=0010 actually is working even if the callbacks are a bit messy. Click "Push me" to trigger the longer sequence.
Great demo. Love the animations.
If you have any ideas for a simple way cross-element animations could be queued outside of callbacks, please let me know. I can't parse how Velocity does it, and I think GSAP's syntax would be too much for this.
Yes -- no GSAP syntax. :-)
I'll experiment with it more as soon as i got time (which equals the entire weekend since my girl is working). It fits the bill as well since i've been scouring the internet for a decent and non-bloated animation engine so that i don't have to write or include helper1, helper2, helper3 each and every time i want to do more then a simple statechanges aka "A > B animations" (ie the stuff that would end up in a stylesheet anyway).
That's why i began writing small libs such as animation.stagger, demo: http://codepen.io/tommiehansen/full/ZWNRoY/ full src: https://github.com/tommiehansen/base/blob/master/js/animation.stagger/animation.stagger.js
Which basically just loops through elements and adds animation-delay or transition-delay but also has the option to end staggering after n elements. It leaves all other things to CSS and is great if one just want to add staggering without resorting to :nth-child or some ugly mixin in SASS and LESS.
Any way to reset an element?
A zero duration animation that resets the original state? :-)
No, currently it acts as fill: both, so whatever the end state is (accounting for direction: alternate), that's applied.
I am looking at implementing a stop method to allow for killing infinite animations, just haven't integrated that yet. On Sat, May 21, 2016 at 2:14 AM Tommie Hansen notifications@github.com wrote:
Any way to reset an element?
— You are receiving this because you were assigned. Reply to this email directly or view it on GitHub https://github.com/kenwheeler/cash/issues/119#issuecomment-220763280
Yes -- i "solved" it that way, but it's not very intuitive though. :)
Looks like the WAAPI equivalents would be:
var player = element.animate(/* ... */);
console.log(player.playState); //"running"
player.pause(); //"paused"
player.play(); //"running"
player.cancel(); //"idle"... jump to original state
player.finish(); //"finished"...jump to end state
The Animator class is being returned by $.fn.animate, so prototype methods should be able to allow for that. Play/pause may be tricky for the requestAnimationFrame fallback, but I've done it before. On Sat, May 21, 2016 at 6:37 AM Tommie Hansen notifications@github.com wrote:
Yes -- i "solved" it that way, but it's not very intuitive though. :)
— You are receiving this because you were assigned. Reply to this email directly or view it on GitHub https://github.com/kenwheeler/cash/issues/119#issuecomment-220773070
Yeah, i read something about that too.
And actually -- if you want to go the WAAPI way i read ... somewhere that it actually uses the syntax that i nearly proposed before;
var myFrames = [
[elem1, { props }, { opts }],
[elem2, { props }, { opts }],
[elem3, { props }, { opts }],
];
It would seem they've been inspired by Velocity JS or any other that uses the same syntax for sequences.
Tried a little and this actually works, it's slighly ugly and i have no idea if it will works with multiple frames within each 'frame' ...
function seq2(sequence, curNum, len){
curNum = curNum || 0;
len = len || sequence.length;
var cur = sequence[curNum];
if(curNum+1 < len ){
cur.opts.complete = function(){ seq2(sequence, curNum+1, len); };
}
// trigger animation
cur.e.animate( cur.frame, cur.opts )
}
The func() compresses down to around 131 bytes (without gzip) but i'm guessing that you can do it much better. :-)
Edit: Added codePen for it http://codepen.io/tommiehansen/pen/LNwPvx?editors=0010
Demo of the func in action and with using lots of sequencing: http://codepen.io/tommiehansen/full/bpXNPr/
Hit the 'Play all' -button to play all.
Latest Prototype
Cash.fn.animate essentials:
Element.animate
, falling back to arequestAnimationFrame
animation engine$.animate
method for animating object properties using therequestAnimationFrame
engine.requestAnimationFrame
loop for best performancetransform
properties usingx
,y
,z
,scale
, etc. (we should be able to parse the currentelement.style.transform
to fill in the 'from' values.)start
andcomplete
callbacks (per element)progress
callbacks (may be problematic withElement.animate
, but necessary for Object callbacks)width: 100%
towidth: 20px
, for example. This is necessary for the requestAnimationFrame fallback, but WAAPI handles it automatically)Possible features:
collectionComplete
callback (allComplete
,animationend
?)fill
support? Currently applies whatever the end state of animation is (equivalent tofill: both
)player.pause(); //"paused"
,player.play(); //"running"
,player.cancel(); //"idle"... jump to original state
andplayer.finish(); //"finished"...jump to end state
,player.reverse()
)var frames = [ [{ props }, { opts }], [{ props }, { opts }] ]; $(element).animate(frames);
Original Issue
I think the future of Cash may need to center around modularity with lightweight extensions/plugins. There are only so many DOM methods we can add that are worthwhile, and performance optimizations will only go so far.
The main Cash package will always have the most useful methods to help beginners get started, but I picture developers being able to select the features/modules they need to get a lightweight package (less than 10-15kb) that gives them exactly what they need for a project without any cruft like we've talked about in #94.
Thinking along those terms, I have a basic animation engine (two files: animateCore.js and canvallax.Animate.js that's quite performant. It could easily be adapted as a cash plugin, minified to about 1kb gzipped, 2kb minified.
Currently it's more along the lines of GSAP TweenMax syntax and designed to animate properties of objects, but it could be adapted to handle CSS properties and more closely match the jQuery syntax for compatibility while introducing some more advanced functions (delay, repeat, animateFrom, animateFromTo, etc).
Like the discussion for AJAX ( #103 ), this wouldn't be an addition to the core, but a separate module for those that want a lightweight but familiar syntax to run simple animations. jQuery with the animation module is at least 50kb, and TweenLite + jQuery support is around 30kb. That's a lot to add for simple effects like spinning an element when clicked or to smoothing a transition from point A to point B.
Any thoughts on animation or the future of Cash?