w3c/csswg-drafts

[css-transforms-2] proposal: new "transform: bend();" function

Opened this issue · 17 comments

ramiy commented

The transform property has several functions that allow elements to be transformed in two-dimensional or three-dimensional space.

This proposal aims to add a new native CSS function that let you bend elements, using the following syntax:

.element {
  transform: bend();
}

Formal syntax:

bendX( [ <angle> | <zero> ] )
bendY( [ <angle> | <zero> ] )
bendZ( [ <angle> | <zero> ] )

This concept transformations by stretching & shrinking the element from its four corners. The 2D transform (bendX() & bendY()) will bend the element on the same plane. The 3D transform (or bendZ()) will bend the element to a cylinder - the two top corners and the two bottom corners will be bent back to create a cylinder.

transform_ bend

The illustration covers positive and negative values. It covers X, Y and Z plane.

Simple example: https://codepen.io/team/amcharts/pen/gZOLGM?editors=0010

I assume this would also imply an individual bend property.

ramiy commented

I assume this would also imply an individual bend property.

Yes.

Maybe duplicate of #4845, which has a little bit more discussion?

ramiy commented

@dbaron similar topics but not duplicate. The discussion there on Warp and Twist is mainly focused on texts. My focus on Bend here is on the entire element. Also, could find there any mention on 3D plane.

@ramiy, looks like you forgot that when specifying a corner the first on starts at the top-left. Your bendX(180deg) also includes a rotation!

It is critical to comprehend that bending can be useful to take a progress element and make it animate clockwise like how a clocks arms move. The top-left corner does not move since the bend filter (when positive) effects the other corners!

So here is a fix for the bendX(180deg):
css_bend

Additionally introducing bend (as desirable as it is) would create potential complications with height, width and related properties. I think having width set may end up down-sizing the element or visually "down-scaling" in a sense.

Using CSS bend filter would make it much easier to create an animated pie chart out of a progress element to animate how much disk space a client is using. Additionally I could position multiple progress elements over each other to better visualize relative disk usage.

The illustration has the top-left 1 point in the wrong position in almost every instance. Because of that it's difficult to visually determine what a proper bendY() would be however bendZ() makes sense conceptually (not really sure about the Z illustrations hoever).

ramiy commented

@ramiy, looks like you forgot that when specifying a corner the first on starts at the top-left. Your bendX(180deg) also includes a rotation!

It is critical to comprehend that bending can be useful to take a progress element and make it animate clockwise like how a clocks arms move. The top-left corner does not move since the bend filter (when positive) effects the other corners!

I think that the starting point should be controlled by transform-origin.

And the direction should be controlled by the bend() function value:

  • positive values = clockwise
  • negative values = counter clockwise

Additionally introducing bend (as desirable as it is) would create potential complications with height, width and related properties. I think having width set may end up down-sizing the element or visually "down-scaling" in a sense.

When you set width and height and then use transform values like translate(), scale() or skew() (or their respective single properties), does it create potential complications? I don't think so. For example, scaling an elements (transform: scale(2);) does not change the original width and height although the element double its original size. I think the same logic should be applied on bend(). Besides, browser can leverage GPU to accelerated CSS transitions [1] [2].

Using CSS bend filter would make it much easier to create an animated pie chart out of a progress element to animate how much disk space a client is using. Additionally I could position multiple progress elements over each other to better visualize relative disk usage.

Yes, charts is an amazing use case for frameworks like Charts.CSS. This was my initial use case (I'm the creator of Charts.CSS). But, 3D bend has much bigger potential beyond simple charts.

The illustration has the top-left 1 point in the wrong position in almost every instance. Because of that it's difficult to visually determine what a proper bendY() would be however bendZ() makes sense conceptually (not really sure about the Z illustrations hoever).

You are correct. I see the wrong position. But you get the point. You understand what transform capability I want to add to CSS transforms.

Actually, I think filter: bendY(180deg) should render like the following:

The property is not filter, it's a transform.

I'll note that this bend() function is not a transform function - it doesn't decompose to a matrix transform, as all transform functions do (because they're all affine transforms).

This is instead some variety of vertex shader, warping a box (decomposed into triangles at some resolution) into a different shape, and thus is likely part of the (abandoned) Custom Filters spec https://drafts.fxtf.org/custom/.


More importantly, however, is establishing what the use-cases for this are. Mentioned in the comments is the idea of using this to stretch a progress element around a circle, but I don't think it's clear that such use-cases actually want to use this sort of mechanism, where an element's contents would get stretched in non-linear ways.

If you want to animate a wedge growing around a circle, a combination of conic-gradient() (to get the color wedge) and clip-path (to clip out the center, if you want an arc rather than a wedge) can accomplish this pretty easily.

If you're doing anything more complex than a simple color block, like an outlined line advancing around a circle, this sort of warping is not when you want, because it affects the various dimensions in different ways - the outline would get stretched at the "sides" as it extends around to be larger than the inner/outer part of the outline. You instead probably want to animate an SVG path more directly, so you have proper control over the geometry without the undesirable visual warping.

But once we've knocked out these use-cases, I don't see any other major use-cases; that is, cases where a reasonable number of authors are trying to do something but are currently blocked, such that it's worth adding new functionality to the language. It seems very unlikely that you want text warped like this, for example - what you probably want instead is to lay out text along a path (circular or otherwise) without warping it, which you can do with SVG already.

conic-gradient() isn't a very good workaround, it has aliasing issues when making hard lines that cannot be worked around by adding a small distance between stops. Because the concrete distance between stops is different the further you get from the conic gradient's origin.

That said, I would use this feature to bend an element during an animation, specifically in the Z-direction. At the beginning and end of the animation, the element would be un-bent, so distortion wouldn't be a concern.

conic-gradient() isn't a very good workaround, it has aliasing issues when making hard lines that cannot be worked around by adding a small distance between stops. Because the concrete distance between stops is different the further you get from the conic gradient's origin.

Bending an element would have similar aliasing issues, for the same reason.

If you want to animate a wedge growing around a circle, a combination of conic-gradient() (to get the color wedge) and clip-path

Invalid. The progress element is for showing progress, not a gradient. A gradient is used for style purposes, not functionality.

this sort of warping is not when you want, because it affects the various dimensions in different ways

You mean, we have to make an effort to solve these problems? Then THAT is what this conversation should be about, NOT about creating erroneous approaches to avoid tackling the problem at hand.

Let's look at the visual effects of CSS transform: https://developer.mozilla.org/en-US/docs/Web/CSS/transform

Yes, it twists and warps elements. Using bend is 100% exactly applicable to CSS transform.

The only valid conversations that should be occurring are how to resolve the known issues, everything about using different tactics to accomplish the goal of bend is off-topic.

dbaron commented

I agree that this proposal doesn't fit with transforms, nor does it really explain the details of what is being proposed, how it works, how it interacts with the other features already in CSS, or how it could be implemented.

I think it's worth stepping back and explaining what the underlying user needs (or perhaps developer needs) are here, and then working from there to understanding how they could be addressed.

I agree that this proposal doesn't fit with transforms

Disagreeing without suggesting an alternative nullifies the purpose of replying.

nor does it really explain the details of what is being proposed, how it works

There sure looks like a bit of conversation covering exactly that.

or how it could be implemented.

Probably by people who actually work instead of making excuses.

I think it's worth stepping back and explaining what the underlying user needs (or perhaps developer needs) are here

That has already been covered, again, did you read through this thread? There are 10 posts including your own.

@dbaron I'm familiar with you back at Mozilla and don't recall you posting noise back then back there so I'll presume you're on here burned out at the end of your day, get some rest and come back fresh. 👍

If bending did fit with transform, it could be and expressed as a transformation matrix.
Apparently, it can be described as a rotate transformation for one (automatically determined) edge of the box, while the opposite edge remains unchanged and everything in between automatically morphs alongside the rotation path.

I think it would be helpful to consider whether similar effects would be desirable with

  1. one static edge and translate, skew or scale,
  2. one or three static (“pinned”) corners instead.

*transform-attachment: [<edge> | <corner>{1,3} | auto | _none_]

@jabcreations You're being rather rude here. I suggest stepping back and reconsidering your involvement in this thread.

@Crissov What you described is still not a transformation matrix. Like I said, it's a fragment shader. It's fundamentally unsuitable to be considered as part of 'transform'. (I also don't think your description matches what the OP is describing; in the OP's description two opposite edges are both curved.)

For what it's worth, the axes in the initial proposal don't seem to correspond with the x-, y- and z-axis we have in the other transform functions.

What's shown in the bendX() and bendY() example actually bends around the z-axis (pointing "into the monitor"). So, bendZ() seems to be the one that actually covers the use cases mentioned, i.e. a circular progress bar and a pie or donut chart.

Bending along the x-axis means to rotate the top and/or bottom edge of the element into the screen. And bending along the y-axis applies the same to the left and right axis.
That means, the element would appear smaller in one axis and clinched towards the edges.

Also, the examples of 180° and 360° bending in the initial proposal indicate that the edges would collapse at 0°, which they obviously don't do. So it would need to be defined how exactly the bending works.
Furthermore, it needs to be expressed how it interacts with transform-origin.

I believe, bending should work in a way so that the area an element has always stays the same. That means, to get a circle as shown the examples, you need a relatively long rectangle and not a square.


Iterating on what @tabatkins and @dbaron said, bending is not an affine transformation and cannot be expressed using a matrix transformation. So I can see why they say that it doesn't fit to how transform is currently defined.

Though I can also see the point in saying that bending is some kind of (advanced) transformation. So it does somewhat fit to the transform property. It just isn't expressible as a matrix transformation.


Regarding the use cases, I guess the main point related to the <progress> element is to keep its semantics and functionality while allowing to style it in a way that it is displayed circularly. The proposed solutions like using a conic gradient for that don't provide those semantics.


The vertext shaders referenced by @tabatkins probably would be the most flexible but also the most complex solution to this. And the abandoned idea around custom filters indicates that this approach might not be viable, at least not in that form.

Sebastian

dbaron commented

Three other comments:

  1. Understanding use cases means starting from an understanding of how the feature is going to be used. A set of pictures demonstrating an already-designed feature are not use cases. If those pictures were the use cases, I'd suggest using SVG to draw them. (Maybe that's actually how you drew them.) I don't think they are the use cases... but nonetheless the burden of showing use cases is on those who are advocating for adding a new feature. If you want to argue for the feature you probably need to more clearly convince others how that feature would be used in real web content. (If the only use case is a circular <progress>, I don't think that's anywhere close to justifying the complexity that this feature would add.)
  2. I think the effects shown in the diagrams here are relatively simple in the sense that they probably only cover a relatively small space of use cases. It would be interesting to have clearer explanations of what the underlying use cases are and what the visual effects that designers would really expect for those use cases are. This would allow understanding whether the proposal satisfies the use cases.
  3. Yet, the effects shown are actually substantially more complicated than shown. The diagrams show bending around a point that is a certain distance away from the center of one side of the box's square. It's not clear how that distance was chosen or whether it would need to be author-controlled (making the bend() function more complicated). Further, it's not clear if the use of a centered position is universally applicable.

@SebastianZ While the OP provided many examples they failed to comprehend the definition of which corner and direction do we start from. I corrected that by stating the top left moving in a to the right and then dowards motion like a clock (and not in conjunction with the progress element context). However their X/Y axis are otherwise presented in the correct 2D context. Their Z axis is presented in a Z axis context though you have to interpret is in a way since the image overall is only represented in a 2D context.

I believe, bending should work in a way so that the area an element has always stays the same.

Now that is an interesting idea. However to have a meaningful conversation in that context there has to be an axiom about which corner the bend starts from which I have continued to suggest the top-left corner. With that presumption then a 180 degrees bend has the top-left corner stay in it's position and the top-right corner moves below it as in my my bendX(180deg) example posted on February 3rd. Then do we want the top-border that is now (visually) the outer/right to adhere to the width? What about the bottom border that is now smaller/inner in my visual illustration?

I believe, bending should work in a way so that the area an element has always stays the same.

Wouldn't it be more affordable performance wise to calculate the total length of all four borders? If a height: 100px; width: 100px; has 400px of total border length then adjusting the borders to still have 400 pixels of border length seems like less work and then web designers could scale the element as/if needed.


@dbaron

It would be interesting to have clearer explanations of what the underlying use cases are and what the visual effects that designers would really expect for those use cases are.

An example that comes to mind: a 2D train animation. A vertical/2D train track in the middle of the page. As the user scrolls downwards a z-axis "bridge" would move upwards. While it was at the bottom (and if it had three dimensions) you'd see the "north"/"page's up" side. When the user scrolled to the point where the bridge was vertically center over the train track you'd see no sides and of course as you continued to scroll and the bridge appeared at the top you'd see the "south"/"page's down" side of that element.

For food related websites think about rounded burgers, pies, pizzas, donuts, etc. It could be placed as a bottom-right quadrant in appearance at the top-left portion of the page as a visual part of a logo or menu. As a

element each
  • element could appear as a "slice".

    Another example one could make a clock out of three elements that simply bend and then animate their rotation with each element having one of their borders be treated like a "hand" on a clock.

    Another example is being able to visually format text without having to resort to using shape-outside or using it in some kind of combination with it. Imagine bendZ() and then making the element twirl around via an animation showing different parts of the text.

    How about animating two elements relative to their surfaces? Imagine two pieces of paper in a three dimensional context. When at 0x0 one is completely over the other and then at 180deg the bottom one is now in front of the previously top element. At 90deg that top element would curve half over and half under the bottom element.

    There are some potentially serious implications for making websites themselves three dimensional in part or even whole.

    @tabatkins You've made it clear you fiercely believe that bend isn't a matrix transformation. We don't care that it is or isn't, we care that it gets defined, implemented and we are able to use it. So instead of fighting the idea because you don't believe it's a matrix then how about defining it with it's own parent CSS properties?

    A rough example:

    bend-axis: x|y|z;
    bend-origin: left|right, top|bottom;
    bend-degree: 180deg|100%;
    bend-context: area|border|content;

    The bend-axis should be a given.

    The bend-origin should define which corner the bending starts at. As I've suggested the top-left corner with bends moving in a top-to-bottom / left-to-right fashion by default however allowing people to change the "corner origin" would likely allow for different visuals especially in combinations of bends that we may not be considering right now.

    The bend-degree property should be fairly obvious as well. A 180deg should result in half a circle or be equivalent to 50% where as a 360deg or 100% bend-degree would result in the element becoming a circle.

    The bend-context would allow the area to be calculated in different contexts. Someone may need the element to bend while staying in "place". Someone else may need the element to "appear larger". I think there is more subjectivity in here and it's very much worth discussing different contexts of what would be considered useful. In example if the element is a paragraph without explicit height or width being defined the bend-context would default to content as obviously, paragraphs grow in size based on the amount of text (and/or descendant elements) that they contain.

    By moving the bend out of transform it removes the debate and would possibly make it easier to apply both transforms and bends to the same element with a less confusing set of syntax for web designers/developers. I'm not sure if I covered everything brought up here though the goal is to get past conflict and get this proposal past the initial conceptual stages because at the end of the day as web designers and developers we either have what we need to get the job done or we don't.

    Edit: I also want to mention another very important context to consider for bend-origin is that the same element with different bend-origins would have different effects on how it effects the flow of content on the page.