Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-transforms] Let 'transform-origin' and 'transform' take comma-separated lists #589

Closed
SebastianZ opened this issue Oct 11, 2016 · 13 comments

Comments

@SebastianZ
Copy link
Contributor

SebastianZ commented Oct 11, 2016

Previous discussion

Back in 2012 there was a discussion about extending the transform-origin property to take a list of origins and make transform comma-separated.

I'd like to reconsider this, because I think its a common use case to have different origins for transformations and I couldn't find a final conclusion on this.

The suggestions in that thread were:

  1. Let transform-origin and transform take comma-separated lists like e.g. the background-* properties.
  2. Introduce a origin() function affecting all following transformations
  3. Extend the transform functions by parameters for the origin.

Arguments against the different solutions above were:

  • SVG allows both, comma and space separation (SVG 2 refers to CSS Transforms, so the syntax is the same)
  • backwards incompatible
  • cannot be overridden partially
  • more verbose for the average case
  • confusion-prone
  • inconsistent with how other properties work (obviously wrong, as other properties use commas for list separation)
  • comma-separating all transform functions complicates adding a list of transform functions

Proposal

Considering the above, my idea is the following:

  1. Introduce a transform-function longhand property, which takes a comma-separated list of multiple space-separated transform functions

    The syntax of transform-function would look like this:

    none | <transform-function-list>#
    

    where

    <transform-function-list> = <transform-function>+
    
  2. Change transform-origin to take a comma-separated list of transform origins

    The syntax of transform-origin would then look like this:

    <transform-origin>#
    

    where

    <transform-origin> = [ left | center | right | top | bottom | <length-percentage> ]
    |
    [ left | center | right | <length-percentage> ]
    [ top | center | bottom | <length-percentage> ] <length>?
    |
    [[ center | left | right ] && [ center | top | bottom ]] <length>?
    

    (or maybe just <position>)

    Each would default to 50% 50%.

  3. Turn transform into a shorthand for transform-function and transform-origin

    The syntax of transform would then look like this:

    [ <transform-function-list> <transform-origin>? ]#
    

This would be backwards compatible, make them consistent with other properties like background-*, avoid confusion and allow to set different origins for the transform functions.

Example

transform: rotate(20deg) skew(10deg, 5deg) 0 0, scale(80%)

This would rotate the element by 20 degrees around the upper-left corner, then skew it by 10 degrees on the X axis and 5 degrees on the Y axis, and finally scale it around the center by 80 percent.

Sebastian

@ewilligers
Copy link
Contributor

ewilligers commented Oct 1, 2019

How would transform-origin lists interact with other properties using transform-origin: translate, rotate, scale, offset-anchor, also SVG elements <transform>, <animateMotion> ?

@dirkschulze
Copy link
Contributor

To the counter-argument of comma-separation and backwards compatibility with SVG transform attribute: We have frozen the syntax of SVG transform attributes:

  • We accept that the syntax is different on the attribute than on property: commas may continue to appear between transform functions.
  • The transform attribute won't have new transform functions, will continue to have rotate() with 3 arguments while the transform property derivative won't.

In other words, we decoupled the syntax of the transform attribute from the transform property. If we change the syntax of the CSS transform property we don't need to change or consider the syntax of the transform attribute.

@AmeliaBR
Copy link
Contributor

AmeliaBR commented Oct 2, 2019

I'm assuming that the commas in the transform property wouldn't have any other meaning / effect, other than grouping the transform sequence according to how they match to transform-origin (and transform-box, too!). In other words, if there was only a single value for the modifier properties, the series of function lists in the transform property would effectively get concatenated together, as if the commas weren't there.

That means that most of the time, commas in transform wouldn't have any effect, but they suddenly would if you also add a comma in transform-origin/-box. I agree with Dirk's comment about the SVG syntax being fixed and independent, but it does add an extra layer of potential author confusion, if commas in the modifier properties don't interact with arbitrary commas in the attribute, but do interact with commas in the property value (which would often seem arbitrary, until you change transform-origin).

A more serious complication (as Eric noted): how would this interact with the individual transform properties (rotate, scale, etc)? Those also use transform-origin and transform-box, but wouldn't align neatly with the comma separated list pattern. Similarly, offset-anchor for motion path defaults to using the transform-origin value. I can't see any sensible way to integrate all these uses with this proposal.

@SebastianZ
Copy link
Contributor Author

How would transform-origin lists interact with other properties using transform-origin: translate, rotate, scale, offset-anchor, also SVG elements <transform>, <animateMotion> ?

At the time I created this issue the CSS properties didn't exist yet. 😄
Because the properties only accept single values (for now), I think the simplest solution is to keep their behavior as is, i.e. use the first transform-origin value for their transformation.

A more complex approach would be to also extend those to take lists of transformations.

Note that my initial suggestion for the changed syntax for transform had a little error. The <transform-origin> value is optional.

Sebastian

@SebastianZ
Copy link
Contributor Author

I just saw @AmeliaBR's comment after I posted my one. Thank you for pointing out transform-box and the potential confusion arising from the comma-separation!

Regarding the comma separation issue, yes, in CSS it's meant to match the different values within the modifier properties with each other. The logic would be similar to the one for layering multiple background images, i.e. the number of transforms is determined by the number of comma-separated functions in the transform-function property. If a related property doesn’t have enough comma-separated values to match the number of transforms, the list of values is repeated until there are enough values. So the following two examples would be the same:

transform-function: scale(2), rotate(30deg), translateX(100px);
transform-origin: center center, 100px 0;
transform-function: scale(2), rotate(30deg), translateX(100px);
transform-origin: center center, 100px 0, center center;

It's very unfortunate that in SVG there is generally no difference between space and comma separation and I agree that this may cause some confusion for authors.

What relates to transform-box, I'm not sure there is a use case for having different reference boxes for different transformations. I assume that authors would only like to specify one reference box for all transformations.

Sebastian

@tabatkins
Copy link
Member

I'm assuming that the commas in the transform property wouldn't have any other meaning / effect, other than grouping the transform sequence according to how they match to transform-origin (and transform-box, too!).

Ideally this would affect how animation works, specifically by bounding the sequences that get glommed together into a matrix() when there's a function mismatch.

At the time I created this issue the CSS properties didn't exist yet. smile
Because the properties only accept single values (for now), I think the simplest solution is to keep their behavior as is, i.e. use the first transform-origin value for their transformation.

Agreed, having them as part of the first transform-group makes the most sense to me.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed Let 'transform-origin' and 'transform' take comma-separated lists, and agreed to the following:

  • RESOLVED: Reject this proposal
The full IRC log of that discussion <dael> Topic: Let 'transform-origin' and 'transform' take comma-separated lists
<dael> github: https://github.com//issues/589
<dael> smfr: Old proposal to allow transform property tot ake a comma sep list where each has its own transform-origin. Additionally a proposal for shorthand to allow origins
<dael> smfr: Seems a bit too late to add this, big change to transform. Not in favor, but welcome other input
<dbaron> Seems like a bunch of complexity (e.g., with animations), so I'm fine with rejecting the proposal.
<dael> TabAtkins: Reasonable. To address use case might be interesting to expore transform-origin function that takes the list. But that would be convenience feature.
<dael> TabAtkins: I was in favor back in the day, but I agree it's minor and not worht the churn
<dael> AmeliaBR: Agree with TabAtkins . If we do this it has to apply to transform-box as well as transform-origin
<dael> astearns: Is what the proposal requests possible but in a different way?
<dael> TabAtkins: Yeah, transalte-origin is a translate function before and after your transform list. You can simulate it on your own. You have to know how to do that and perhaps an example would be good. Nothing to prevent you from representing on your own
<dael> astearns: Proposal is to reject this proposal
<dael> astearns: Concerns?
<dael> RESOLVED: Reject this proposal

@smfr
Copy link
Contributor

smfr commented Oct 23, 2019

Closing per WG resolution.

@SebastianZ
Copy link
Contributor Author

In the meeting notes @tabatkins said that "translate-origin is a translate function before and after your transform list". I guess he still referred to transform-origin, though I still don't get what he meant.

He also mentioned that there should be an example added explaining that.

It would be good to see how you do a scaling around the center and then a rotation around the bottom right corner of the scaled element, for example.

Sebastian

@ewilligers
Copy link
Contributor

In the meeting notes @tabatkins said that "translate-origin is a translate function before and after your transform list". I guess he still referred to transform-origin, though I still don't get what he meant.

Yes, that should be transform-origin. For example,

  transform-origin: 10px 20px;
  transform: rotate(30deg) scale(4);

is equivalent to

  transform-origin: 0px 0px;
  transform: translate(10px, 20px) rotate(30deg) scale(4) translate(-10px, -20px);

See steps 2 and 8 in https://drafts.csswg.org/css-transforms-2/#ctm

@AmeliaBR
Copy link
Contributor

And if you want to scale from the center-left but rotate around the middle, it would look something like:

  transform-origin: 0 0; /* reset to have no effect on the math */
  transform: translate(0, 50%) scale(4) translate(0, -50%)
              /* scale around center-left */
             translate(50%, 50%) rotate(45deg) translate(-50%, -50%)
              /* rotate around center */;

Demo: https://codepen.io/AmeliaBR/pen/NWWgaPV?editors=1100

@tabatkins
Copy link
Member

Yup, 'translate-origin' is nothing more than a shorthand for a translate() before your transform list, and a reverse translate() at the end.

(And so, on the call I floated the idea of a translate-origin(<length>{2}, <translate-list>) function that would do the back-and-forth for you.)

@SebastianZ
Copy link
Contributor Author

Excuse the long delay for my reply and thank you very much for your explanations! This was not clear to me at all before.

Because I'm surely not the only one, I've now added a simple example explaining that to the MDN page for transform-origin.

Regarding my example, I still believe

transform-origin: center, bottom right;
transform: scale(2), rotate(45deg);

is way more readable than

transform-origin: 0 0;
transform:
	translate(50% 50%) scale(2) translate(-50% -50%)
	translate(100% 100%) rotate(45deg) translate(-100% -100%);

Though I can understand that the use case is probably not strong enough to adjust existing implementations to the more complex list syntax.

Sebastian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants