Re: [css-color][css-transitions] Interpolation between currentcolor keyword and actual color value

On Monday 2016-04-18 14:43 -0700, Tab Atkins Jr. wrote:
> On Sun, Apr 17, 2016 at 6:05 PM, Xidorn Quan <w3c@upsuper.org> wrote:
> > When we are implementing -webkit-text-fill-color, we found an issue with
> > transition due to we always computing "currentcolor" to an actual color
> > value for interpolation rather than keeping it an computed value. [1]
> >
> > This raises a question about how "currentcolor" should be interpolated
> > with an actual color value, given it is a computed value? The spec
> > doesn't say anything about this.
> >
> > A simple example code is:
> > @keyframes t {
> >   0% {
> >     color: #f00;
> >     -webkit-text-fill-color: currentcolor;
> >   }
> >   100% {
> >     color: #0f0;
> >     -webkit-text-fill-color: #00f;
> >   }
> > }
> >
> > This code works in Firefox Nightly and all other major browser engines.
> > And there are three different behaviors:
> > 1. WebKit interpolates between #f00 (color at 0%) and #00f
> > (text-fill-color at 100%), and Gecko currently has the same behavior,
> > though somehow more buggy.
> > 2. Blink treats currentcolor and #00f as uninterpolatable, so in the
> > front 50%, it changes between #f00 and #808000, and then it becomes a
> > solid #00f.
> > 3. Edge treats currentcolor of text-fill-color at 0% as white somehow,
> > and interpolates that with #00f.
> >
> > Edge's behavior doesn't make sense at all, and Blink's behavior looks
> > conforming given the spec doesn't say anything currently, but it seems
> > WebKit's behavior is the most sensible one in the three.
> >
> > Furthermore, if currentcolor is a computed value, the most ideally
> > effect is probably making currentcolor follows the value of color at
> > every moment, so at 50% of the transition, the used value of
> > text-fill-color should be ((#f00 + #0f0) / 2 + #00f) / 2, rather than
> > WebKit's behavior which is (#f00 + #00f) / 2.
> >
> > However, that could add much complexity to implementation. If there is
> > no actual usecase, it is probably not worth that effort. Also if we
> > choose this hard way, it could be a breaking change when switch
> > currentcolor to a computed value for some cases.
> >
> > So I propose we can probably say something like, when one value is
> > currentcolor and the other value is an actual color, treat currentcolor
> > as its current used value at the given moment in advance.
> >
> > What do you think?
> >
> >
> > [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1260543
> 
> Blink's behavior is currently the spec-compliant one, unfortunately.
> currentcolor doesn't simplify at computed-value time, and so you can't
> represent the intermediate color.
> 
> To fix this well, we need to add some way to represent the
> intermediate value - this means adding a color-blending function.  I
> have a bad version of that in Colors 4 currently, but need to fix it
> up to work better.  Once we have that, you can have the intermediate
> color be rgb-blend(currentcolor, #00f 50%) or whatever (stealing
> syntax from cross-fade()), and transitions will be possible.  This
> will produce the behavior you say is "ideal" but "complex".  However,
> it's probably not that complex if it's just the result of evaluating
> the preceding expression, is it?
> 
> I'm not aware of any plans to "switch currentcolor to a computed value
> for some cases" - can you elaborate?

We've agreed to switch currentcolor to being a computed value.  We
errata'd css-color-3 to do that:
https://www.w3.org/Style/2011/REC-css3-color-20110607-errata.html#s.4.5
and made it such in css-color-4:
https://drafts.csswg.org/css-color-4/#currentcolor-color

In our implementation (and I would think in others too) this needs
to be implemented per-property.  So we have some properties handling
currentcolor the new way (as a computed value) and others falling
back to the old way (which doesn't need to be done per-property and
is thus within the generic color handling).  

We've had to do things the new way for properties where currentcolor
is the initial value; text-emphasis-color and
-webkit-text-fill-color depend on the change.  (Though for some
legacy properties we don't handle currentcolor this way, but instead
have a separate -moz-use-text-color that predates currentcolor.)

But for most of these properties we have code to treat the computed
value as being a color for animation purposes so that it does get
animated.  To fix the above compatibility bug Xidorn just removed
that code for text-emphasis-color and -webkit-text-fill-color so
they don't animate between currentcolor and a non-currentcolor
value, because of compatibility problems with
-webkit-text-fill-color, in
https://bugzilla.mozilla.org/show_bug.cgi?id=1260543 .

I'd been hesitant to make the change for other properties because it
would break animations that authors expect to work.  But now that I
realize those animations don't work for -webkit-text-fill-color, I
suspect they might all not work in Blink / WebKit, and there's no
compatibility risk, although I think this needs to be tested
per-property (which I haven't done).

But I'm also hesitant to change other properties, or to leave things
in this situation for a long time, because I'm worried about the Web
becoming dependent on animations *not* working (as happened with
-webkit-text-fill-color).

I'm also hesitant to leave things in this situation for a long time
because I'm worried that the Web might become dependent on having
some properties behave in one way and some properties behave in the
other way, either for animation or non-animation reasons.  Right now
Gecko does do animations from a currentcolor to non-currentcolor
value for things like border-color.  Essentially, depending on the
property, we now have four different behaviors for currentcolor:

1. resolved to a color at computed-value time (fill, stroke,
   gradient color stops, shadow colors, background-color,
   flood-color, lighting-color, stop-color)

2. resolved to a color at computed-value time when specified
   explicitly, but at used-value time when it's the initial value
   (via -moz-use-text-color, which predates currentcolor), and
   animated as though it was resolved to a color at computed-value
   time either way (border-color, outline-color,
   column-rule-color)

3. resolved to a color at used-value time, but animated as though
   it was resolved to a color at computed-value time
   (text-decoration-color)

4. resolved to a color at used-value time, and not animated between
   currentcolor and color values (text-emphasis-color,
   -webkit-text-fill-color)

The spec currently requires (4), but we're hoping in the long term
to make it something more like but slightly better than (3), where
the animation treats currentcolor as a computed value so it doesn't
interpolate changes from currentcolor to currentcolor when color
changes (which is the problem that caused bug 1260543, because that
created a -webkit-text-fill-color value that inherited to a
descendant that specified color, and then overrode that color) but
that it does animate between currentcolor and real color values.

My main worries about compatibility risk are for the properties
currently in group (2) and for fill and stroke in group (1), since
if we change them to group (4) we might never be able to change them
back, because people will depend on them not animating.

I haven't tested what other implementations do here; there are a
*lot* of cases to test.  I'm curious if other implementors can
explain what state their implementations are in here (perhaps a
better state for future risk).  But my general concern here is that
the group is making potentially-breaking changes, and then adding
dependencies on those changes, before implementations have had a
chance to test that they're actually safe changes to make.

-David

-- 
𝄞   L. David Baron                         http://dbaron.org/   𝄂
𝄢   Mozilla                          https://www.mozilla.org/   𝄂
             Before I built a wall I'd ask to know
             What I was walling in or walling out,
             And to whom I was like to give offense.
               - Robert Frost, Mending Wall (1914)

Received on Friday, 22 April 2016 17:48:43 UTC