- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Wed, 17 Aug 2011 18:46:12 -0700
- 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 UTC