Hi,
First of all, I would like to thank authors of Clutter for their
excellent job! I have also studied the source code and I have to admit
that it has many wise design decisions. Especially implicit animations
are really charming,
However, I have two enhancement ideas related to implicit
animations: I) implicit animations on allocation, and II) per property
implicit animations. There is also a demonstration application that can
be downloaded from:
http://temp.henrikhedberg.com/clutter/implicit_animations_on_allocation_20090421.tgz
I) Implicit Animations on Allocation
------------------------------------
When an actor receives a new bounding box from its container
(clutter_actor_allocate), it just jumps and resizes itself without any
animation. Current properties, such as x, y, width, and height cannot be
used to animate transition after allocation, because setting those
properties imply fixed layout (in opposite to container negotiated
allocation).
The proposed solution is to introduce new properties related to
allocation transitions:
- transition-move-x
- transition-move-y
An actor is moved from its current allocated (or fixed) location by
transition-move-* units to left, right, up or down when painted into a
stage. The default value is 0.0 meaning that the actor is rendered where
it should normally be. When an allocation box changes, an actor applies
a new allocation instantly but also sets these values to inverse the
transition. Then an actor starts to animate these values towards the
default value 0.0.
- transition-scale-x
- transition-scale-y
The width and height of an actor is scaled by transition-scale-* units.
The default value is 1.0 meaning that the actor is rendered as is. The
actual implementation of scaling varies with an actor. Container actors
must not scale their children, but only scale their own (background)
image if it exists. Visible actors (such as ClutterTexture) may simply
do a scaling operation (in addition to default scaling defined by the
scale-x and the scale-y properties) or use these properties as a factors
when calculating the current visible width and height when painting
(ClutterText, for example, may rewrap lines during the transition).
Example: An actor's current bounding box is (5, 10, 25, 30). After a
size negotiation (clutter_actor_get_preferred_*) it gets a new bounding
box (105, 210, 425, 630) from its container. This is what happens in the
actor's allocate function:
1) The current allocation is saved (called as "old allocation" from now
on).
2) The new allocation is stored by calling the allocate function in the
parent class.
3) The difference between the old allocation and the new allocation is
calculated. Thus, the actor knows that it has just jumped 100 units
right, 200 units down, and its new width is 16 times the old width
and the new height is 21 times the old height.
4) The actor sets transition-* properties as follows:
- transition-move-x: -100
- transition-move-y: -200
- transition-scale-x: 1.0 / 16 = 0.0625
- transition-scale-y: 1.0 / 21 = 0.0476
5) The actor starts to animate these properties back to their default
values:
clutter_actor_animate(actor, mode, duration,
"transition-move-x", 0.0,
"transition-move-y",0.0,
"transition-scale-x", 1.0,
"transition-scale-y", 1.0,
NULL);
Thus, when the actor paints itself the first time after the
allocation the visible bounding box is (x1, y1, x2, y2) where
- x1 = 105 + (-100) = 5
- y1 = 210 + (-200) = 10
- x2 = 5 + (425 - 105) * 0.0625 = 25
- y2 = 10 + (630 - 210) * 0.0333 = 30
It seems that the actor has not move at all yet! After the animation has
completed, the visible bounding box is (x1, y1, x2, y2) where
- x1 = 105 + 0 = 105
- y1 = 210 + 0 = 210
- x2 = 105 + (425 - 105) * 1 = 425
- y2 = 210 + (630 - 210) * 1 = 630
The actor is now transitioned (smoothly) into its new location and size!
If an actor is not yet visible in a stage and thus does not have an
old allocation, the transition-* properties are set as follows:
- transition-move-x: x1 + (x2 - x1) / 2
- transition-move-y: x1 + (y2 - y1) / 2
- transition-scale-x: 0
- transition-scale-y: 0
The demonstration implements also a new signal called
allocation-changed. Subclasses or application developers may use the
signal to overwrite the default behaviour of allocation transitions.
II) Per Property Implicit Animations
------------------------------------
The clutter_actor_animate* functions apply the very same animation
mode and duration to all properties of an actor. It makes these
convenience functions almost unusable in real situations.
Example: We want to move an actor using elastic animations and at
the same time change its opacity from 0 to 255. We try to achive that by
issuing the following statements:
clutter_actor_set_opacity(actor, 0);
clutter_actor_animate(actor, CLUTTER_EASE_IN_SINE, 500,
"opacity", 255, NULL);
clutter_actor_animate(actor, CLUTTER_EASE_OUT_ELASTIC, 2000,
"x", 100, "y", 200, NULL);
However, this is the equivalent of:
clutter_actor_set_opacity(actor, 0);
clutter_actor_animate(actor, CLUTTER_EASE_OUT_ELASTIC, 2000,
"opacity", 255, "x", 100, "y", 200, NULL);
Unfortunately this results flickering when also the opacity property has
the "elastic" animation and the opacity value overflow (around 255). And
naturally that is not what we wanted (wrote) originally!
Of course, we could use the plain animation API as follows (this is
not tested...):
ClutterAnimation* animation1;
ClutterAnimation* animation2;
GValue value = { 0 };
clutter_actor_set_opacity(actor, 0);
animation1 = clutter_animation_new();
clutter_animation_set_object(animation1, actor);
clutter_animation_set_timeline(animation1, NULL);
clutter_animation_set_alpha(animation1, NULL);
clutter_animation_set_mode(animation1, CLUTTER_EASE_IN_SINE);
clutter_animation_set_duration(animation1, 500);
g_value_init(&value, G_TYPE_UCHAR)
g_value_set_uchar(&value, 255);
clutter_animation_bind(animation1, "opacity", &value);
g_value_unset(&value);
g_signal_connect(animation1, "completed", g_object_unref, NULL);
clutter_timeline_start(clutter_animation_get_timeline(animation1));
animation2 = clutter_animation_new();
clutter_animation_set_object(animation2, actor);
clutter_animation_set_timeline(animation2, NULL);
clutter_animation_set_alpha(animation2, NULL);
clutter_animation_set_mode(animation2, CLUTTER_EASE_OUT_ELASTIC);
clutter_animation_set_duration(animation2, 2000);
g_value_init(&value, G_TYPE_INT)
g_value_set_int(&value, 100);
clutter_animation_bind(animation2, "x", &value);
g_value_unset(&value);
g_value_init(&value, G_TYPE_INT)
g_value_set_uchar(&value, 200);
clutter_animation_bind(animation2, "y", &value);
g_value_unset(&value);
g_signal_connect(animation1, "completed", g_object_unref, NULL);
clutter_timeline_start(clutter_animation_get_timeline(animation2));
Not very practical, uh!
The proposed solution is to combine only the properties of a single
clutter_actor_animate call into one animation object. Thus, each
execution of clutter_actor_animate instantiate a new animation object
for the actor. If the actor is already animating a property, it is
unbinded from the existing animation and binded into a new animation.
Thus, clutter_actor_animation overwrites the animation mode and the
duration parameters for only single property and not for the whole actor.
Example: Opacity and position properties are animated separately.
clutter_actor_set_opacity(actor, 0);
clutter_actor_animate(actor, CLUTTER_EASE_IN_SINE, 500,
"opacity", 255, NULL);
clutter_actor_animate(actor, CLUTTER_EASE_OUT_ELASTIC, 2000,
"x", 100, "y", 200, NULL);
Example: Calling the function on _a property_ of an actor that is
already being animated will cause the current animation to change with
the new final values, the new easing mode and the new duration - that
is, this code:
clutter_actor_animate(actor, CLUTTER_LINEAR, 250,
"width", 100,
"height", 100,
NULL);
clutter_actor_animate(actor, CLUTTER_EASE_IN_CUBIC, 500,
"x", 100,
"y", 100,
"width", 200,
NULL);
is the equivalent of:
clutter_actor_animate(actor, CLUTTER_LINEAR, 250,
"height", 100,
NULL);
clutter_actor_animate(actor, CLUTTER_EASE_IN_CUBIC, 500
"x", 100,
"y", 100,
"width", 200,
NULL);
The demonstration applications implements per property implicit
animations by storing references to animations into a hash table with a
property name as a key (see the tangle_actor_animate function).
* * *
If it is likely that these enhancements are to be integrated into
Clutter core, I can provide a patch to the ClutterActor.
BR,
Henrik
--
Henrik Hedberg - http://www.henrikhedberg.net/
--
To unsubscribe send a mail to [email protected]