[css-transforms] Adding specialized properties for simple transforms

[This is a restart of the previous thread, as requested by the WG,
because the old one was long enough to make it confusing to tell what
precisely was being proposed.]

Earlier I saw a Twitter thread started by Lea
<https://twitter.com/LeaVerou/status/487350702386479105> about how she
commonly accidentally types the name of the transform she wants as the
property (like "rotate: 45deg;") and then has to go back and correct
it afterwards.  Several other devs chimed in that they do this as
well, and I know that I've done it a few times (especially when using
SVG - I use "transform='translate(...)'" so often that I commonly try
to name the attribute "translate" first).

Common mistakes like this often translate to an unexpectedly clumsy
design, so I got to thinking about fixing this.  After some discussion
on the WG, a basic design emerged that turned out to have other
important benefits as well.

I propose we add the following three properties to the Transforms spec:

* translate: <length>{1,3}
    - specifies a translation in the X, Y, and Z axises, respectively.
Missing values default to 0.
* rotate: <angle> <number>{3}? <'transform-origin'>?
    - specifies a rotation along a given axis from a given origin. An
omitted axis defaults to 0,0,1; an omitted origin defaults to
'transform-origin's initial value.
* scale: <number>{1,3} <'transform-origin'>?
    - specifies a scale in the X, Y, and Z axises, respectively, from
a given origin. Missing values default to 1; an omitted origin default
to 'transform-origin's initial value.

The properties are implemented as if they were turned into their
equivalent transform functions, then prepended to the 'transform'
property in TRS order (with the origins applied as pre/post translates
around each one).  Thus, the 'perspective' property affects these
properties, and the 'transform' property itself simply applies after
these properties.

Adding these properties has a number of benefits:

* These three transforms have the unique property that, when applied
in the proper order, they look "independent".  That is, a translate
always moves the element left/right and top/down the specified amount,
rather than moving them along some rotated axis or by some multiplier
of the distance you specified.  A scale always scales the element
along its width/height, rather than doing less predictable squishing
along some rotated axis.  Other transform functions, like skew(), do
not have this property, and so are not being proposed as properties.

* Authors no longer have to remember the ordering of these transform
functions when they just want simple, "independent" effects.  (And the
correct ordering is frankly hard to understand - I think most people,
if they're not careful, would describe them in TSR order, but that
links the effects of scale and rotate in a weird way due to the math.
Shane did this in the prior thread, for example.)

* The three properties can be adjusted and animated independently,
making a number of effects easier or even *possible* for the first
time.  For example, scaling an element on hover and rotating it on
click currently requires you to write out a full 'transform' value
with both scale() and rotate() functions on all four possible
combinations of hover and click; with these properties, you could
simply write a 'scale' property for :hover and a 'rotate' property for
:active and everything works out.  For another example, doing any
transform to an element while a different type of transform is being
animated on the element is currently impossible; it's trivial with
these new properties.

* A common authoring mistake (writing the desired transform as the
name of the property) becomes correct behavior, at least for a common
subset of properties.


Some further discussion topics:

* Since browsers treat 2d and 3d transforms differently, I assume we
need to specify that, say, a 2-value 'translate' is equivalent to
translate(), but 3-value is equivalent to translate3d(), even though
we could technically always turn it into translate3d(), and similar
with the other two properties.

* It might be worthwhile to make scale and rotate into shorthands for
their value, origin, and axis (for rotate).  It seems reasonable to
want these to cascade independently, so you can, for example, set a
rotate on some axis and then adjust the rotation without having to
repeat the axis each time.

* TRS ordering is clearly correct when considering just these three
properties, but is it correct when combining with 'transform'? That
is, is TRSTr a logical ordering for authors?  Since rotates and scales
may have an effect on the additional transforms in the 'transform'
property, it might be better to have the actual order be TTrRS, which
I think ensures that *all four* properties can be thought of as
"independent".

For example, currently "el { rotate: 45deg; translate: 100px; }" does
the "independent" thing - the element is moved 100px to the left and
is rotated 45deg.  But "el { rotate: 45deg; transform:
translate(100px, 0px); }" doesn't - the element is instead moved
approximately 70px left and down, and rotated 45deg.  With my
suggested ordering, the two would have the same effect.  I don't
*think* there are any unexpected effects here, but I'd have to think
about what's expected if you do a "transform: scale(2, .5);
rotate(45deg);".  It might just be that in some cases you get a weird
effect like this, but does the reordering minimize the weirdness?

~TJ

Received on Thursday, 17 July 2014 16:48:30 UTC