Closed bradwerth closed 2 months ago
It was useful in the implementation to quickly identify empty float areas. Do you have a use case for animating a shape where points are momentarily made colinear, and/or use cases for the 1- and 2-point polygon definitions above?
@astearns As far as identifying empty floats goes, it makes it easier, since you'd only need to check the shape's geometry if the shape-margin
is 0.
I created a test case: https://codepen.io/AmeliaBR/pen/rJRyyw?editors=1100
Chrome and Safari both follow the current spec wording, and treat the straight-line polygon as an empty shape, so that the text runs completely over the first SVG. (Firefox and Edge do not support shape-outside
at all; the second SVG is there to test basic support.)
I agree with @bradwerth that it would be preferable to apply the margin to a zero-area shape, in the same manner as stroking that shape with a stroke twice the margin's width.
In fact, for shape-outside
, it would even make sense to still use a straight-line shape without the margin, because you're only filling in floated text on one side of the shape, anyway. (In my demo, that would mean text starting from the middle of the diagonal line.)
So the use case from @AmeliaBR is to be able to specify a shape that matches the inked area of an SVG path? That works for a simple line (non-dash, etc.) case, but not generally. For this case it might be better to get the inked area as an image and wrap around that.
There are also degenerate circles, ellipses and inset rects (which might need a larger value of shape-margin than merely >0 to amount to non-empty)
I'm open to considering the change if there's a good reason to take it. We did consider this case in the early stages of the spec, and went back and forth a few times over it (as I recall).
One case we considered is when a shape has a zero-width strut (for whatever reason). Say you have a triangle with a zero-width spur coming out from one of the vertices. If you animate shape-margin from zero to some positive value, then it's a bit surprising to have content jump away from what was previously an invisible part of the shape. It might be better to consider zero-width lines as contributing to the shape-outside wrapping effect to avoid that discontinuity.
So the use case from @AmeliaBR is to be able to specify a shape that matches the inked area of an SVG path?
I wasn't really thinking of that as a driving use case, more as a way to conceptualize & visualize it.
I agree that the main use case is to have a consistent transition from an almost-linear shape to an exactly linear shape, or for a shape being animated down/up from point (if you really wanted to animate it down to nothing, you could always animate down the margin to 0, to create a smooth transition).
It might be better to consider zero-width lines as contributing to the shape-outside wrapping effect to avoid that discontinuity.
Yes, I think so. The more I play around with it, the more it makes sense to use any edge as the boundary, regardless of fill area.
I added two more samples to my test case, both using a cross shape. In the first, the cross is exact, so that there is zero fill area and the float layout ignores the polygon. In the second, a few points have been shifted by a tenth of a pixel and the layout is completely different (although the difference in the SVG visualization of the same shape is imperceptible to my eyes). Both the vertical and the horizontal lines of the cross affect the layout.
Yeah, the presence of shape-margin definitely puts me on @AmeliaBR and @bradwerth's side. This is identical to the problem SVG has (had?) where a gradient won't render if the shape it's assigned to is zero-width/height, even if it's being used to paint the stroke which is larger and non-zero area.
I see two options, each with minor drawbacks (and I consider the current state of the spec to have a minor drawback as well, shown by this issue)
1) A shape-area is defined by the area enclosed by the combination of shape and shape-margin. If, after accounting for shape-margin there is no area enclosed, then the shape (or portion of the shape) does not contribute to the float area. This can result in discontinuities when shape-margin goes from zero to a positive value
Interesting counter-example. I have used "zero-width struts" in clip-path shapes, but I would never have expected them to be transparent in a shape-outside situation. You could always construct that shape so that the connecting strut is on the far edge, but then it wouldn't be convertible for the opposite float direction. Either way, I think the better solution for that use case is to support path()
notation, which doesn't need any hacks to create multiple sub-paths.
- A shape-area is defined by all of the edges, whether they enclose an area or not.
This seems most consistent and intuitive, considering that floats are in some sense checking for the edges of the shape, not checking for a nearby area associated with the shape. Adopting this into the spec would resolve things for circle and ellipse (with a 0 radius they would collapse to a point or a line). It would simplify the implementation for polygon though require some clarification about polygons with 1 or 2 vertices (do they define a point / line). The challenging one will be inset. If box is inset more than 100%, it either should be treated as a point or line (where?) or as an inside-out polygon. Neither option is particularly intuitive, but this all seems more intuitive than a shape disappearing when it has no positive area.
We're shipping support for shape-outside in Firefox 62. On this issue of shape areas versus edges, we are not compliant with the spec as written. We decided to ship our implementation enforcing these principles:
I think it's time to send this to the WG for a resolution.
Can any Chromium/WebKit devs comment on the points in @bradwerth's final comment, would it be difficult to change implementations to match?
Insets deflated beyond a point singularity are treated as rectangles in the same shape as the inverted rectangle.
I'm not too keen on this point. Could result in weird rounding errors where it is difficult to shrink the rectangle to exactly zero. I would rather limit the inset rectangle to a minimum of zero width/zero height, using rules similar to how border-radius values that add to greater than 100% are adjusted.
I'm not too keen on this point. Could result in weird rounding errors where it is difficult to shrink the rectangle to exactly zero. I would rather limit the inset rectangle to a minimum of zero width/zero height, using rules similar to how border-radius values that add to greater than 100% are adjusted.
I would support that as long as the spec defines the location of the point. Where should it be in a case like inset(100% 100% 50% 50%)? The two choices I would find most intuitive are either at the center of the original rect, or at the center of the inverted rect.
I would support that as long as the spec defines the location of the point.
My suggestion to use border-radius
rules was specifically the calculation for scaling down all the measurements proportionally (I've updated the link above to go to the precise section):
For inset rectangles, the calculation would be simpler,* because you're not worried about maintaining the aspect ratio of a corner curve:
That means that if the insets on opposite sides are equal, the collapsed point/line would be in the exact middle of the reference box. If one inset is 9 times the opposite inset, the point would be 1/10th of the distance across.
Where should it be in a case like
inset(100% 100% 50% 50%)
?
It would be at the same point as inset(66.67% 66.67% 33.33% 33.33%)
, with all the insets being scaled down equally to fit in the 100% width/height available.
* Unless you wanted to also scale down the border-radius curves proportionally, in which case things could get complicated. But if I'm reading the function definition correctly, the border radiuses apply after the insetting happens (e.g., a border-radius of 10% would be 10% of the inset shape, not the original box), so it's probably best to keep that as a separate step, with its own adjustments.
The Working Group just discussed Degenerate polygons with positive shape-margin
, and agreed to the following:
RESOLVED: Accept the changes as stated in the issue
I'm starting to look at the changes needed for this. One point in the minutes about was about negative shape-margin
, which isn't actually an issue. The property only takes positive values (and that's consistently implemented)
I've made an initial commit for these changes. I expect I'll need to do more
https://github.com/w3c/csswg-drafts/commit/d7d823919725b08df51560a47cc6382dff0adedc
I'm not sure about this - floated elements already are unaffected by the reduced float area. What else needs to be done?
Zero-area shapes will not affect floated elements that are block adjacent -- there is no minimum of 1 pixel area applied to the shape.
And so far I'm only handling degenerate inset
s using @AmeliaBR's suggestion above. I'm not entirely sure what to do with polygons and paths with winding rules that create edges enclosing no area or 'negative' area. Perhaps winding rules should only be relevant for clip-path
and we should use all of the edges in shape-outside
?
@bradwerth could you clarify what you mean by the block adjacent note above?
Closing this as I did not get any further responses. If there is anything more that needs to be done here please open a new issue
https://www.w3.org/TR/css-shapes-1/#funcdef-polygon
If a degenerate polygon has a positive shape-margin, it is practically useful for it to NOT have an empty float area. One example is an animated shape-outside: polygon with a shape-margin where the points in the polygon are momentarily made colinear. If the float area becomes empty in this case, the float area would collapse, only to abruptly reappear when the animation moved the points into an area-enclosing shape.
It would be useful for the spec to define the float area in the cases where the polygon is degenerate, but has a positive shape-margin. There are three cases: