W3C home > Mailing lists > Public > www-style@w3.org > August 2011

Re: [css3-images] Order of color-stop fixup

From: Tab Atkins Jr. <jackalmage@gmail.com>
Date: Wed, 17 Aug 2011 18:46:12 -0700
Message-ID: <CAAWBYDCNF=XRm9EHMpOWRzZMU0odjb+B=kVoncz9NoYFSwFmnA@mail.gmail.com>
To: Shane Stephens <shans@google.com>
Cc: Brian Manthos <brianman@microsoft.com>, www-style list <www-style@w3.org>
I've discussed the issue more with Shane, and we've managed to find
some cases where the current spec really does produce slightly "bad"
behavior.  Overall, we'd still be OK with leaving the spec how it is,
but would prefer to fix it.

The tl;dr version of the two problems are (1) early fix-up makes
intermediate forms do weird things with fixed-up stops in some
situations, and (2) early fix-up makes it impossible to represent a
decent class of intermediate forms exactly.

The first problem is illustrated at
<http://www.xanthir.com/etc/gradient-fixup-order.html>.  The page
explains the problem, but I'll summarize it here:

In the example, we animate the width of the box from 200px to 1000px,
at the same time as we animate the background from linear-gradient(to
right, red 50%, white 100px, blue 100%) to linear-gradient(to right,
red 10%, white 100px, blue 100%).  (In other words, we only change the
position of the red stop.)

Because the white stop is specified as 100px in both gradients, one
would expect that at all points during the transition the white stop
is either at exactly 100px (when the red stop resolves to less than
100px) or is at the same position as the red stop (when the red stop
resolve to greater than 100px).

However, if you do position-fixup early, as the first gradient on the
page does, you get a weird effect where the white stop jumps *ahead*
of the red stop due to the way the linear interpolation works (the
effect is maximized halfway through the transition, when the red stop
is at 180px and the white is at 200px).

Doing the position-fixup at the last moment instead keeps the white at
a reasonable position the whole time - it's always Math.max(100px,
red.position).


A second problem is in the representation of intermediate values
during a transition.

Many intermediate gradients can be represented exactly.  Some
intrinsically can't (for example, a "to top left" to "to bottom right"
transition, or a "cover" to "contain" transition), and so the
intermediate steps must either be represented in some explicit form
that loses some of the details, or represented as a cross-fade()
function that abstractly represents the gradient at that point in the
transition.

There's a third class that *looks* like they can be exactly
represented, but actually can't be, due to the ordering of the fixup
rules: transitions where the matching color-stops have positions
specified in different unit families (like one in % and the other in
px).  One would naively expect that this could be written as a simple
calc().  For example, say you were transitioning between the following
two gradients:

linear-gradient(to right, red 50%, white 100px, blue 100%)
linear-gradient(to right, red 10%, white 40%, blue 100%)

It *appears* that the 50% point of the transition could be represented as:

linear-gradient(to right, red 30%, white calc(50px + 20%), blue 100%)

This doesn't actually work, though.  If the box's width is 300px, the
naive intermediate form yields:

linear-gradient(to right, red 90px, white 110px, blue 300px)

If you actually resolve the start and end gradients and then
interpolate, though, you get the following:

start: linear-gradient(to right, red 150px, white 150px, blue 300px)
end:   linear-gradient(to right, red 30px, white 120px, blue 300px)
blend: linear-gradient(to right, red 90px, white 135px, blue 300px)

This difference is due to the fact that a color-stop is never
independent; its position is always dependent on the positions of all
preceding color-stops as well.

So, the current rules mean you're required to represent the
intermediate forms with the explicit or cross-fade() forms.  Delaying
the position-fixup instead lets up represent this class of gradients
exactly.  This isn't a bug, just a nice-to-have.

(We could avoid all the issues with "when to do fixup?" if we didn't
fixup at all, and just treated misplaced stops similar to how we
handle stops before 0% on radial-gradient - each stop defines painting
up to its point, but subsequent stops can define themselves earlier
and thus visibly start at a color already partially-blended.  I think
I suggested this at some point early on, and it was rejected because
it would take extra effort to massage the gradient into a form that
the platform libraries liked.)

~TJ
Received on Thursday, 18 August 2011 01:46:59 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:43 GMT