Closed valentinstoychev closed 9 years ago
Animations is a very wide topic and I will be glad to see scenarios that we need to support first.
Animations are paramount to be able to create functional user experiences. As to scenarios, what kind are you looking for?
I'm assuming that the stuff CSS Animations can do would be very useful. Or at the very least, being able to transition transform
and opacity
attributes which can be hardware accelerated.
For ui elements, I need translate, opacity, and color animations. Also would be great to have custom slide/fade animations for page in and out on the navigation stack.
+1 for Css3 animations (without javascript), and also something javascript based like jquery's animate which can apply to any numeric css property.
@RangerMauve is right, transform
is huge for UI. Also, code>@keyframe</codes would be a huge asset to NativeScript as well.
My suggestion is to use an API that closely resembles jQuery's animate. For example:
var panel = page.getViewById("panel");
var cancellationToken = panel.animate({
opacity: 0.25, // number between 0.0 and 0.0
backgroundColor: "#FFFF0000", // string containing a hex or a known color name
translate: {x:100, y:100}, // JSON object with two numbers -- x and y
scale: {x: 2, y:2}, // JSON object with two numbers -- x and y
rotate: 180, // number of degrees
}, 5000, 100, 3, function(cancelled) {
if (cancelled){
console.log("Animation cancelled");
}
else {
console.log("Animation complete");
}
});
Where 5000 is the duration, 100 is the delay, and 3 is the repeat count. Duration, delay, repeat count and the finished callback are all optional parameters. You can cancel a running animation by calling cancellationToken.cancel(); So the most minimal animation code could look like this:
page.getViewById("panel").animate("opacity: 0");
This will make the panel transparent for a duration that is default for the specific platform's animations.
Since the native platforms do not have built-in keyframes support, keyframes could easily be achieved by simply chaining several individual animations with the same or different durations one after another. For example, here is how one could move a label to the four corners of its parent panel:
label1.animate({translate: {x:100 y:0}}, function(){
label1.animate({translate: {x:100 y:100}}, function(){
label1.animate({translate: {x:0 y:100}}, function(){
label1.animate({translate: {x:0 y:0}}, function(){
console.log("Animation finished");
});
});
});
});
I guess that we can think of a CSS-like syntax that will read the what is written in the CSS, build the animations with the JavaScript API suggested above and then run then when the respective component is loaded in the visual tree.
The core animation API is also public and accessible so you could do more complex things with it, such as combining multiple animations for different controls and running them together or sequentially. Here is a TypeScript example for the core animation API:
var animations: Array<animation.Animation>;
animations = new Array<animation.Animation>();
animations.push({ target: button1, property: animation.Properties.translate, value: { x: -240, y: 0 }, duration: vm.duration, delay: 0, repeatCount: vm.repeatCount });
animations.push({ target: button1, property: animation.Properties.scale, value: { x: 0.5, y: 0.5 }, duration: vm.duration, delay: 0, repeatCount: vm.repeatCount });
animations.push({ target: button1, property: animation.Properties.opacity, value: 0, duration: vm.duration, delay: 0, repeatCount: vm.repeatCount });
animations.push({ target: button2, property: animation.Properties.translate, value: { x: -240, y: 0 }, duration: vm.duration, delay: vm.duration, repeatCount: vm.repeatCount });
animations.push({ target: button2, property: animation.Properties.scale, value: { x: 0.5, y: 0.5 }, duration: vm.duration, delay: vm.duration, repeatCount: vm.repeatCount });
animations.push({ target: button2, property: animation.Properties.opacity, value: 0, duration: vm.duration, delay: vm.duration, repeatCount: vm.repeatCount });
animations.push({ target: button3, property: animation.Properties.translate, value: { x: -240, y: 0 }, duration: vm.duration, delay: vm.duration * 2, repeatCount: vm.repeatCount });
animations.push({ target: button3, property: animation.Properties.scale, value: { x: 0, y: 0 }, duration: vm.duration, delay: vm.duration * 2, repeatCount: vm.repeatCount });
animations.push({ target: button3, property: animation.Properties.opacity, value: 0, duration: vm.duration, delay: vm.duration * 2, repeatCount: vm.repeatCount });
configureAnimationCurve(animations, true);
cancelToken = animation.start(animations, vm.playSequentially, (cancelled?: boolean) => {
if (cancelled) {
console.log("Buttons slide out animations cancelled");
return;
}
console.log("Buttons slide out animations completed!");
animations = new Array<animation.Animation>();
animations.push({ target: panel1, property: animation.Properties.scale, value: { x: 0, y: 0 }, duration: vm.duration, delay: 0, repeatCount: vm.repeatCount });
animations.push({ target: panel1, property: animation.Properties.rotate, value: 1080, duration: vm.duration, delay: 0, repeatCount: vm.repeatCount });
animations.push({ target: panel1, property: animation.Properties.backgroundColor, value: new colorModule.Color("red"), duration: vm.duration, delay: 0, repeatCount: vm.repeatCount });
configureAnimationCurve(animations, true);
cancelToken = animation.start(animations, vm.playSequentially,(cancelled?: boolean) => {
if (cancelled) {
console.log("Panel animation cancelled");
return;
}
console.log("Panel animation completed!");
});
});
The code-snippet above is taken from our animations testing app located here: https://github.com/NativeScript/NativeScript/blob/animations/apps/animations/main-page.ts
The jQuery-like API that I am suggesting here will simply be a kind of syntactic sugar wrapper on top of the core animation API (which is also public and accessible and anyone can use it directly if he wants to). So for a very simple animation you could simply do label.animate(...). For more complex animation sets you could directly use the core animation API located in the animation module here:
https://github.com/NativeScript/NativeScript/tree/animations/ui/animation
Any suggestions are welcome.
Here is a link to the Android application I am testing with: https://www.dropbox.com/s/xig9ln2hvw51qmk/Animations.Android.zip?dl=0
Here is a link to the iOS application I am testing with:
https://www.dropbox.com/s/s0uaagkmv4wor7o/Animations.iOS.zip?dl=0
var cancellationToken = panel.animate({ opacity: 0.25, // number between 0.0 and 0.0 backgroundColor: "#FFFF0000", // string containing a hex or a known color name translate: {x:100, y:100}, // JSON object with two numbers -- x and y scale: {x: 2, y:2}, // JSON object with two numbers -- x and y rotate: 180, // number of degrees }, 5000, 100, 3, function(cancelled) { if (cancelled){ console.log("Animation cancelled"); } else { console.log("Animation complete"); } });
Where 5000 is the duration, 100 is the delay, and 3 is the repeat count.
Why not move duration, delay, and repeatCount on the options object? It looks more readable to me:
var cancellationToken = panel.animate({
opacity: 0.25, // number between 0.0 and 0.0
backgroundColor: "#FFFF0000", // string containing a hex or a known color name
translate: {x:100, y:100}, // JSON object with two numbers -- x and y
scale: {x: 2, y:2}, // JSON object with two numbers -- x and y
rotate: 180, // number of degrees.
duration: 5000,
delay: 100,
repeat: 3
}, function(cancelled) {
if (cancelled){
console.log("Animation cancelled");
}
else {
console.log("Animation complete");
}
});
The jQuery API seems to allow both a number as a second parameter and a full-blown options map:
$( "#clickme" ).click(function() {
$( "#book" ).animate({
opacity: 0.25,
left: "+=50",
height: "toggle"
}, 5000, function() {
// Animation complete.
});
});
vs.
$( "#clickme" ).click(function() {
$( "#book" ).animate({
width: "toggle",
height: "toggle"
}, {
duration: 5000,
specialEasing: {
width: "linear",
height: "easeOutBounce"
},
complete: function() {
$( this ).after( "<div>Animation complete.</div>" );
}
});
});
First of all; Great work!
Imho animate
should really return a promise instead of taking a callback function (or if you want to: return a promise if no callback function is set). jQuery has the .promise()
method (e.g. $('div').hide().promise()
but I can't really see any benefits of doing it that way, compared to always return a promise) Also, other functions of {N} returns Promises, such as the alert.
That way we could have this syntax for "keyframing"
label1
.animate({translate: {x:100 y:0}})
.animate({translate: {x:100 y:100}})
.animate({translate: {x:0 y:100}})
...and this syntax for creating animations. Note how I've added the duration
, delay
and repeat
properties to the parameters object.
panel
.animate({
opacity: 0.25,
duration: 5000,
delay: 100,
repeat: 3
})
.catch(function (cancelled) {
console.log("Animation cancelled");
})
.then(function () {
console.log("Animation complete");
})
Small notes:
pointer-events: none;
. In HTML/CSS we've the problem that even a transparent element still catches user input such as clicks, and we therefor need to remove the pointer-events.infinite
(just as css animation-iteration-count: infinite
)opacity: 0.25, // number between 0.0 and 0.0
@emiloberg
This is simply a fluent API. We can do that without promises. We can make the animate method return some kind of hollow animation set abstraction object which also has the animate method and will simply "gather" the information that you pass with each call. It should also have a start method to run the chained animations. Something like
label1.animate(...).animate(...).animate(...).playSequentially() or label1.animate(...).animate(...).animate(...).playTogether().
I have already implemented such functionality here: https://github.com/NativeScript/NativeScript/blob/animations/ui/animation/animation.d.ts
export function start(animations: Array<Animation>, playSequentially: boolean, finishedCallback?: (cancelled?: boolean) => void): Cancelable;
}
So behind the scenes when someone calls the animate method of this not-yet-existing animation set abstraction, the method will append one animation to an internal animation array and return 'this' so the animate method can be called again. Finally, when someone calls playSequentially/playTogether we will call the above start function.
We can add the duration, delay, repeat count and curve to the options JSON. It is a matter of choice. jQuery has the duration as a separate parameter.
Will translate opacity remove tap click events? Like css' pointer-events: none;. In HTML/CSS we've the problem that even a transparent element still catches user input such as clicks, and we therefor need to remove the pointer-events. The opacity animation simply sets UIView.alpha = 0 and android.view.View.setAlpha(0); We don't do anything additional besides that. How about repeat infinitely with keyword infinite (just as css animation-iteration-count: infinite)
I will write the infinite loop count this down as feature suggestion.
Here is a link to the Android application I am testing with: https://www.dropbox.com/s/xig9ln2hvw51qmk/Animations.Android.zip?dl=0
Here is a link to the iOS application I am testing with:
https://www.dropbox.com/s/s0uaagkmv4wor7o/Animations.iOS.zip?dl=0
I also think it will be good if we return a Promise
from the start
instead of taking a function as last parameter. We can even implement one of the early drafts of the cancellation spec: https://github.com/promises-aplus/cancellation-spec
Is there a need to combine more complex animations when some of the items will be played sequentially and some in parallel? Currently the API supports only sequential composition.
@ligaz Yes, I think so. Angular 2.0 animations syntax has this support and we will create a future limitation if we don't support it. We want to support Angular 2.0 animations at some point.
I'm a big fan of the jQuery-like API, and I agree with @hdeshev and @emiloberg that you should be able to optionally place the animation config—duration and such—in the options object.
Random observation, but is there a need to configure repeat
? jQuery doesn't have it, and I can't think of a practical use case.
Anyways, it seems a lot of the discussion is about the return value of animate()
. @hamorphis could you provide a snippet of how to use the current return value of animate()
, because I don't see one in this thread so far. Ideally I'd like to see us to say as consistent with the web animations spec as much as reasonably possible, which would mean returning an object you can set an onfinish
property on or call cancel()
on.
@ligaz "Currently the API supports only sequential composition."
That is not true. The API supports both sequential and parallel execution of multiple animations. Here is the definition of the start function from the animations module:
export function start(animations: Array<Animation>, playSequentially: boolean, finishedCallback?: (cancelled?: boolean) => void): Cancelable;
}
Here is how I do it in Android:
var animatorSet = new android.animation.AnimatorSet();
if (playSequentially) {
animatorSet.playSequentially(nativeArray);
}
else {
animatorSet.playTogether(nativeArray);
}
In iOS, based on the playSequentially parameter value, I create a bunch of iOS animation functions and chain them differently depending on the parameter:
var nextAnimationCallback: Function;
var animationDelegate: AnimationDelegateImpl;
if (index === animations.length - 1) {
// This is the last animation, so tell it to call the master finishedCallback when done.
animationDelegate = AnimationDelegateImpl.new().initWithFinishedCallback(finishedCallback);
}
else {
nextAnimationCallback = createiOSAnimation(animations, index + 1, playSequentially, finishedCallback);
// If animations are to be played sequentially, tell it to start the next animation when done.
// If played together, all individual animations will call the master finishedCallback, which increments a counter every time it is called.
animationDelegate = AnimationDelegateImpl.new().initWithFinishedCallback(playSequentially ? nextAnimationCallback : finishedCallback);
}
@tjvantoll " @hamorphis could you provide a snippet of how to use the current return value of animate()"
There is no animate method yet. This is why I started this discussion. We need input from many people. We need to decide what the public API will be first, and then I will implement it. What we currently have is the so called core animation API, which is a low-level .NET-style API. Given that, we can build any kind of fluent public API on top of it. This discussion is foe that -- we are trying to decide what kind of public API to build on top of the existing one, which by the way is fully functional. My point is that I don't want to create a public API which people will dislike -- so I am waiting for input here.
For everyone involved in this discussion, I think that it would be great to take a look at what is happening behind the scenes when we reach the Android or iOS runtimes and tell then to animate something. After all, we are developing animations for Android and iOS and we have to have in mind what limitations there are. We depend on this functionality, i.e. we are not developing a random animation API for a random platform. You get my point.
Please, take a look at these:
https://github.com/NativeScript/NativeScript/blob/animations/ui/animation/animation.d.ts https://github.com/NativeScript/NativeScript/blob/animations/ui/animation/animation.android.ts https://github.com/NativeScript/NativeScript/blob/animations/ui/animation/animation.ios.ts
I found about NativeScript, I haven't tested yet because I can't seem to find a good example with a cool UI, transitions, animations, nice fonts, etc.
This topic is interesting, so my suggestion is to follow this model API: http://greensock.com/gsap
@heldrida About greensock:
There is no reason to copy greensock API. Better - add gsap support and develop few additional plugins to it, to ensure compatibility.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Come up with a solution for providing a way to implement smooth animations in NativeScript app.
If you are interested in this feature please add comments below with specific requirements and vote for this feature in our ideas portal.