- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Mon, 30 Aug 2010 12:42:37 -0700
- To: Simon Fraser <smfr@me.com>
- Cc: www-style list <www-style@w3.org>
On Sun, Aug 29, 2010 at 3:14 PM, Simon Fraser <smfr@me.com> wrote: > On Aug 29, 2010, at 9:56 am, Tab Atkins Jr. wrote: > >> tl;dr: I've added instructions for serializing gradients. I want to >> add an ending-point argument to linear-gradient(), and change the >> color transitions to happen in pre-multiplied rgba space. >> >> Simon and I recently had a twitter conversation about issues keeping >> him from wanting to implement the draft gradient syntax in Webkit. >> >> One of the issues was that gradients didn't have a defined >> serialization. I've now defined this in the draft. >> >> Another issue was that linear-gradient() had too much "magic". There >> is explicitly different behavior for all four combinations of >> specified and omitted values in the first argument. He'd prefer that >> the syntax have more regular behavior, such that a missing value is >> just filled in with a default value. He sent an email to this effect >> back during the original syntax discussions: >> http://lists.w3.org/Archives/Public/www-style/2009Nov/0050.html >> >> After thinking it through, I believe his complaint is valid, though I >> strongly disagree with the fix he proposes in that email. Instead, >> I'd like to add an additional argument to the linear-gradient() syntax >> - another <bg-position> given as the second argument to the function, >> for specifying an ending-point for the gradient. > > Here's the current draft, for those following: > > <http://dev.w3.org/csswg/css3-images/#linear-gradients> > > It currently has: > > linear-gradient([<bg-position> || <angle>,]? <color-stop>, <color-stop>[, <color-stop>]*); > > and you're suggesting (I think; my grammar is weak): > > linear-gradient([<bg-position> <bg-position>? || <angle>,]? <color-stop>, <color-stop>[, <color-stop>]*); Actually: linear-gradient( [ [<bg-position> || <angle>], [ <bg-position>, ]? ]? <color-stop>, <color-stop> [, <color-stop>]*); Just to avoid ambiguities - you can't have two <bg-position>s next to each other without some sort of separator. So, for example, you'd type "linear-gradient(top left, bottom right, white, silver);". >> Let me explain why I think this addresses his complaint adequately. >> >> Right now, there are four cases that can be expressed by the first >> argument to linear-gradient() - the <bg-position> can be present or >> absent, and the <angle> can be present or absent. These are all >> handled somewhat specially, in what appears to be an ad hoc manner. >> Really, though, the cases are very consistent and in-line with the >> "unspecified values just take a default" ethos that Simon wants; >> however, this isn't apparent with only four cases to look at. Adding >> an ending-point argument makes this much more apparent, and also >> addresses several use-cases. >> >> In his email, Simon notes that it appears that linear-gradient() is >> actually expressing two or three different syntaxes. He's right - >> there are two substantially different behaviors here, keyed by the >> presence or absence of the angle. >> >> If the angle is absent, the gradient-line simply goes from one point >> to another point. If both points are specified, you're done. If the >> ending-point is missing, it defaults to rotating the starting-point >> around the center of the box. If the starting-point is missing, it >> defaults to "top center". >> >> If the angle is present, the gradient-line starts at the >> starting-point, extends outward at the angle specified, and ends when >> a line drawn perpendicular to the gradient-line would intersect with >> the ending-point. If both points are specified, you're done. > > You'd have to describe what happens when the angle is perpendicular to the line > between the endpoints. That's already addressed - the actual ending-point of the gradient-line then overlaps with the starting-point, which means it's a degenerate gradient and is displayed as a solid-color image the color of the last color-stop. (This situation can come up with a few other existing combinations of values.) >> If the >> ending-point is missing, it defaults to the corner of the box in the >> direction of the angle. If the starting-point is missing, it default >> to the corner of the box in the opposite direction of the angle. > > That seems inconsistent (aka "magic") to me, and a surprising difference > from the no-angle case. In the no-angle case, the end point is obtained by mirroring > around the center of the box. In this case, a "extend to the edge of the box" > method is used. Hmm, I can see the logic of reusing the "mirror around the box" logic here for determining a missing end-point. That seems fine to me. I'll make the change. We still *need* an angle-based default for a missing starting-point, though. As far as I can tell, there is no single default that works well for angle-based gradients, let alone one that also works well for no-angle gradients. > I think there are several reasons to try to avoid the special behaviors > that the current spec has. > > With most compound or shorthand CSS properties, you can omit values > that have default values without change in behavior. This is not > the case with gradients. For example: > > linear-gradient(top left, ...) > > is not equivalent to: > > linear-gradient(top left, 0, ...) > > Similarly > > linear-gradient(200deg, ...) > > is not equivalent to > > linear-gradient(top 200deg, ...) > > This violates the principle of least surprise for authors. I don't think that either of those are particularly surprising, personally, but I can imagine opinions differing. I don't believe it's possible to define a default angle in such a way as to preserve the current behavior of a two-points gradient, nor is it possible to define a default start point that will preserve the usefulness of the current behavior of an angle gradient. > A second reason to try to avoid these branches in the gradient algorithm is because > it makes interpolating between them for animation harder. Without a canonical > form, you can't map all inputs to a common representation for interpolation. Correct, you can't. Angle-based gradients and no-angle gradients calculate their positions in fundamentally different ways which cannot be reconciled without losing information. Angle-based gradients have an additional constraint that no-angle gradients don't have. Even if we did define a shared default for the first <bg-position>, there is *no way* to define a default <angle> for both cases. The mere presence of an <angle>, no matter the value, adds an additional constraint to how the gradient positions itself that can't be generally replicated in the two-point gradient. >> In either case, missing points have consistent, well-defined default >> values. The defaults are just completely different between the two >> cases. I believe this is necessary to make the two cases actually be >> useful. Simon's proposal for always defaulting the starting-point to >> the top-left is insufficient for either case. If the angle is absent, >> it makes the default behavior a diagonal gradient, when the most >> common gradients on the web and elsewhere are vertical and horizontal. >> If the angle is present, it makes the only useful angle values be >> between 0deg and -90deg - 0deg to 90deg and 180deg to 270deg have >> unintuitive results, while 90deg to 180deg appears to have no effect >> at all in most cases (it draws the gradient completely outside of the >> box unless there are color-stops with positions lower than 0%). > > I was trying to think along the lines of considering the two > <bg-positions> not as endpoints of the gradient specifically, but > as the corners of a virtual box that bounds the gradient (following > the "extend to the edges" logic that exists in the spec). Unfortunately > this doesn't solve the problem of having sensible defaults. The gradient > > linear-gradient(top 45deg, ...) That never did extend from the top-left to the bottom-right. It starts in the top-center, actually. > would no longer extend from the top-left to bottom-right corners. > > linear-gradient(top left 45deg, ...) > > would, however. > >> I believe that the current level of "smarts" used in differentiating >> the defaults of the two cases is the minimum necessary to make the >> syntax useful. The benefits of simplifying it further would be >> strongly outweighed by the downsides of the bad defaults, in my >> opinion. > > Fundamentally, there are two ways to describe a linear gradient; either as > two end points, or a start point (or center point), angle, and length. > > That argues for two forms: > > linear-gradient(<bg-position> <bg-position>, ...); > > and > > linear-gradient(<bg-position> <angle> <length>, ...); > > This latter form would be more useful if <bg-position> specifies the center-point, > because then any angle works. A length of 100% could be specified to that which > has the gradient extend to fill the box. The defaults are a little tricky; you'd > want the <bg-position> to default to center, and <angle> to 90deg for a vertical gradient. Wouldn't this still suffer from the same interpolation problems? Is there a hidden benefit to doing it this way that I'm missing? Regarding the actual proposal: So, if <bg-position> was the center point for the gradient, I suppose the starting-point and ending-point for the gradient-line are based on the old "where a line perpedicular to the gradient-line would intersect" corners based on the <angle>? Where are the 0% and 100% points located for color-stops? Making the <bg-position> specify the center-point seems to be sacrificing both readability and writeability for the purpose of gaining a definite default value, which I think is backwards. If it's specifying the starting-point, then going with a <length> or a second <bg-position> seems about equivalent. I think I argued in the old threads that if you want an angle-gradient of a particular length you can just specify those lengths in the color-stops. ~TJ
Received on Monday, 30 August 2010 19:43:34 UTC