Re: transitions vs. animations

On Mon, Apr 5, 2010 at 8:20 AM, Håkon Wium Lie <howcome@opera.com> wrote:
>  .button {
>    color: blue;
>  }
>
>  .button:hover {
>    color: red;
>    effect: on-entry change(color, 1s), on-exit change(color, 1s);
>  }
>
>  .button:focus {
>    color: green;
>    effect: on-entry change(color, 1s), on-exit change(color, 1s);
>  }
>
> So, the button will start off being blue. Let's say the element first
> is hovered (and turns red), and then is focused (and turns green).
> This is given by the normal cascading rules in CSS.

That's a lot of repetition, and it still doesn't cover all cases
(.button[disabled] turning color:gray?).  All I want to have to do is
say "Hey, you, button.  Always transition your color.".  Your proposal
would require me to specify an on-entry and on-exit for *every single
state* that changes the color of the button.  Transitions handle that
use-case with a single simple property on ".button".  This is the sort
of regression we were afraid of seeing during the FtF discussion.

I don't think you can go about this task piecemeal.  If you really
want to integrate animations and transitions, you'll have to do so by
reexamining the core use-cases, and finding an abstraction that fits
them all.  I believe there are 3 primary cases we're trying to hit:

1. Upon a *property value* changing on an element, do something to
that element (possibly just animate that property, possibly animate
other properties)

2. Upon an element changing *state* (defined implicitly by a
selector), do something.

3. While an element is *in* a state, do something.

Right now, Transitions addresses part of #1 (animating the property
that changed only), Animations addresses #3 (infinite animations) and
part of #2 (you can only really control what happens when you enter a
state, not leave it), and you can sort of hack them together to fully
address #1.

I think that, fundamentally, #1 and #3 require different mechanisms.
The concept of "property" and "state" are completely separate in CSS.
I don't think we can really tie these two things together in a useful
way.

There are some places where we can change things, though.  We should
be able to fully address #1 without relying on difficult hacks
(anything requiring :not(:hover) as a basic mechanism is screwed up
^_^).  We should also be able to fully address #2.  I actually think
we may be able to do this by making Transitions and Animations
slightly *more* different.  Here's a sketch of how they would work:

/* In this example, the transition-property value (and its place in
the shorthand) also take a function defining a set of keyframes to
execute when a given property transitions. */
.slider {
  left: 0;
  transition: left 1s, play(left, bounce) 1s;
}

.slider:hover {
  left: 400px;
}

/* In this example, there is an animation run when you enter a state,
while you are in the state (delayed by 1s, so as to wait until all
transitions and play-ins are done), and when you leave the state.
Also some transitions for good measure. */
.tooltip {
  opacity: 0;
  display: none;
  transition: opacity 1s, display 1s step-start;
}

.tooltip.show {
  opacity: 100%;
  display: block;
  play-in: rainbow 1s;
  play-during: throb 5s 1s;
  play-out: rainbow reverse 1s;
}


The changes are relatively minor here:

1. Transition-property takes another value, which specifies a
(property, keyframe) pair.

2. Three new shorthand properties: play-in, play-during, and play-out.
 Play-in specifies animations to be run when the element begins to
match the given selector.  Play-out plays when the element stops
matching.  Play-during plays infinitely while the element matches.

2. "infinite" is no longer a possible animation duration.  Instead,
all keyframes given for the play-during property are assumed to run
infinitely (and play-during doesn't accept an animation-play-count),
and all keyframes given for the play-in and play-out properties are
assumed to be finite.  Play-during simply doesn't *have* a play-count
entry.

3. Any place that accepts a keyframe must also accept a keyframe
followed by the 'reverse' keyword, indicating that the keyframes
should be run backwards.

4. The operation of (and likely names of) the individual animation
properties will have to be tweaked somewhat.  Rather than a single
"animation" shorthand with "animation-*" subproperties, you'll have
three shorthands and three separate groups of subproperties
(play-in-name, play-during-delay, etc.).


What's left to define:

1. How a keyframe set takes default values when played in a
transition.  Obviously 100% should default to the end-state.  What
about times in the middle?  Basically, I'd like to be able to *either*
do a simple transition taking a property (say, left) from value A to
value B, or do a complex transition using keyframes to take the
property from value A to value B with a complex path in between.  I'm
not sure what's ideal here yet.

2. How a keyframe-based transition and a standard transition interact
on the same element.  We may want to say that they *can't* interact,
and that transitioning both "left" and "play(left, bounce)" brings the
same conflicts that transitioning "left" twice does, and so we resolve
them in the same way (last one specified wins).  This would mean that
in my first example I'd have to remove the "left 1s" bit and specify
what to do with "left" in the bounce keyframes.  Alternately, since
you can statically examine keyframes and see what properties are
manipulated by one, perhaps just say that if a keyframe manipulates a
property, it overrides any manipulation that would be done by earlier
keyframes or transitions.  This is likely better, as it handles being
able to hook multiple keyframes to the same property elegantly, and
also fits with the idea that a plain transition is an
instantaneously-produced trivial keyframe.

3. How animations of the various types (in, out, during) interact with
eachother.  Does during always beat in?  Out beat during?  What about
when you fill any of these?  I think this can be resolved fairly
easily, if perhaps arbitrarily.



A final use-case that hasn't been addressed is "one-shot" animations,
which are useful for javascript.  Say you want to throb a button every
time someone clicks on it.  This is an animation not tied to a state
or a property value change, but rather to an action or event
originating from elsewhere.  These would basically just be triggered
by javascript.  My initial thought is to just do them as a normal
animation tied to a class, and in js just quickly add and remove the
class that triggers it.  This doesn't work, though, as removing an
animation (or equivalently, making an element no longer match a
selector providing an animation) stops the animation (I think?).  Can
I just listen for the animationend event and remove the class then?
This doesn't help me if I want to restart a running animation if the
user clicks again, though.  Being able to directly trigger an
animation through JS would be somewhat useful for this sort of thing.
I suppose this can wait a bit, though.



So, I think this sort of approach, slightly further differentiating
transitions and animations, would actually help a lot more, and seems
to make a lot of the expected common use-cases very simple.

~TJ

Received on Monday, 5 April 2010 17:40:02 UTC