Bug 164949 - -webkit-overflow-scrolling: touch completely breaks 3D perspective functionality
Summary: -webkit-overflow-scrolling: touch completely breaks 3D perspective functionality
Status: RESOLVED DUPLICATE of bug 156435
Alias: None
Product: WebKit
Classification: Unclassified
Component: CSS (show other bugs)
Version: Safari 10
Hardware: iPhone / iPad All
: P2 Major
Assignee: Nobody
URL: https://drafts.csswg.org/css-transfor...
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-11-18 13:27 PST by AliG
Modified: 2020-06-26 07:36 PDT (History)
6 users (show)

See Also:


Attachments
testcase (547 bytes, text/html)
2018-05-22 01:02 PDT, Frédéric Wang (:fredw)
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description AliG 2016-11-18 13:27:19 PST
CSS perspective and translateZ provide an efficient way of creating scroll parallax effect however in iOS Safari, adding -webkit-overflow-scrolling: touch; completely breaks all the 3D and perspective effects.

Please see http://codepen.io/aghassemi/pen/QGdKbZ for an example. The only difference between the two divs is the value of webkit-overflow-scrolling but notice how perspective is ignored on the second div.
Comment 1 Radar WebKit Bug Importer 2016-12-01 12:42:09 PST
<rdar://problem/29464710>
Comment 2 Jayden Seric 2018-01-09 03:03:47 PST
Sad to see there has been no activity for over a year for such a visually disruptive bug without a decent workaround.

This is the best I could come up with for displaying a scrollable list of cards, stacked on an angle. It looks weird though because there is not a single perspective origin:

.container {
  perspective: 1000px;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}

.child {
  transform: rotateY(45deg);
}

@supports (-webkit-overflow-scrolling: touch) {
  .container {
    perspective: none;
  }
  .child {
    transform: perspective(1000px) rotateY(45deg);
  }
}
Comment 3 Frédéric Wang (:fredw) 2018-05-22 01:02:41 PDT
Created attachment 340960 [details]
testcase
Comment 4 Frédéric Wang (:fredw) 2018-05-22 05:43:45 PDT
Some preliminary debugging: When webkit-overflow-scrolling is absent RenderLayerCompositor::computeCompositingRequirements will execute

    if (!willBeComposited && canBeComposited(layer)
        && requiresCompositingForIndirectReason(layer.renderer(), childState.subtreeIsCompositing, anyDescendantHas3DTransform, indirectCompositingReason)) {
        layer.setIndirectCompositingReason(indirectCompositingReason);
        childState.compositingAncestor = &layer;
        overlapMap.pushCompositingContainer();
        addToOverlapMapRecursive(overlapMap, layer);
        willBeComposited = true;
    }

where requiresCompositingForIndirectReason returns true because of the perspective property on the overflow node and the 3D transform on its descendants. If the overflow node has overflow-scrolling:touch, willBeComposited is however set to true at the top of the function so this conditional is never executed. I'm not yet sure how/whether this is causing the bug, though.
Comment 5 Ali Juma 2018-05-22 06:53:33 PDT
(In reply to Frédéric Wang (:fredw) from comment #4)
> Some preliminary debugging: When webkit-overflow-scrolling is absent
> RenderLayerCompositor::computeCompositingRequirements will execute
> 
>     if (!willBeComposited && canBeComposited(layer)
>         && requiresCompositingForIndirectReason(layer.renderer(),
> childState.subtreeIsCompositing, anyDescendantHas3DTransform,
> indirectCompositingReason)) {
>         layer.setIndirectCompositingReason(indirectCompositingReason);
>         childState.compositingAncestor = &layer;
>         overlapMap.pushCompositingContainer();
>         addToOverlapMapRecursive(overlapMap, layer);
>         willBeComposited = true;
>     }
> 
> where requiresCompositingForIndirectReason returns true because of the
> perspective property on the overflow node and the 3D transform on its
> descendants. If the overflow node has overflow-scrolling:touch,
> willBeComposited is however set to true at the top of the function so this
> conditional is never executed. I'm not yet sure how/whether this is causing
> the bug, though.

I'd suggest looking at the corresponding RenderLayerBacking to see which GraphicsLayers are created, and which of these GraphicsLayers have preserves3D() true. Probably in the overflow-scrolling:touch case, there's a GraphicsLayer which ends up flattening the transform (because of preserves3D() being false), so the Z component ends up having no visual effect.
Comment 6 Frédéric Wang (:fredw) 2018-05-22 06:56:26 PDT
(In reply to Ali Juma from comment #5)
> I'd suggest looking at the corresponding RenderLayerBacking to see which
> GraphicsLayers are created, and which of these GraphicsLayers have
> preserves3D() true. Probably in the overflow-scrolling:touch case, there's a
> GraphicsLayer which ends up flattening the transform (because of
> preserves3D() being false), so the Z component ends up having no visual
> effect.

Thanks for the hint! Indeed I started to debug RenderLayerBacking this morning and the perspective / z-translate transform seemed to be correctly taken into account. I'll check preserves3D later...
Comment 7 Simon Fraser (smfr) 2018-05-22 08:30:51 PDT
I think this is because, in the WebKit 3D transforms implementation, overflow necessarily triggers flattening, turning off preserve-3D.
Comment 8 Simon Fraser (smfr) 2018-05-23 11:48:14 PDT
    if (style.preserves3D() && (style.overflowX() != OVISIBLE
        || style.overflowY() != OVISIBLE
...
        style.setTransformStyle3D(TransformStyle3DFlat);
Comment 9 Frédéric Wang (:fredw) 2018-05-23 12:12:03 PDT
Thanks, this code was added a long time ago in bug 83337.

"The CSS3 Transforms spec says that some properties should cause flattening of things with transform-style: preserve-3d."

Is it still valid? I can't find it in https://www.w3.org/TR/css-transforms/
Comment 10 Simon Fraser (smfr) 2018-05-23 12:32:01 PDT
https://drafts.csswg.org/css-transforms-2/
Comment 11 Frédéric Wang (:fredw) 2018-05-30 07:21:21 PDT
(In reply to Simon Fraser (smfr) from comment #8)
>     if (style.preserves3D() && (style.overflowX() != OVISIBLE
>         || style.overflowY() != OVISIBLE
> ...
>         style.setTransformStyle3D(TransformStyle3DFlat);

That code is only executed when the element has "transform-style: preserve-3d;" (which is not the case in the repro cases) so probably the flattening happens somewhere else.
Comment 12 Jayden Seric 2019-10-29 18:05:03 PDT
(In reply to Jayden Seric from comment #2)
> Sad to see there has been no activity for over a year for such a visually
> disruptive bug without a decent workaround.
> 
> This is the best I could come up with for displaying a scrollable list of
> cards, stacked on an angle. It looks weird though because there is not a
> single perspective origin:
> 
> .container {
>   perspective: 1000px;
>   overflow: auto;
>   -webkit-overflow-scrolling: touch;
> }
> 
> .child {
>   transform: rotateY(45deg);
> }
> 
> @supports (-webkit-overflow-scrolling: touch) {
>   .container {
>     perspective: none;
>   }
>   .child {
>     transform: perspective(1000px) rotateY(45deg);
>   }
> }

-webkit-overflow-scrolling: touch was deprecated in Safari 13 for iPad (but not iPhone):

https://developer.apple.com/documentation/safari_release_notes/safari_13_release_notes#3314667

This means @supports (-webkit-overflow-scrolling: touch) can't be used anymore for a workaround. The perspective for all the scrollable 3d cards on my live websites are now broken on iPad! Not sure the best way to deal with this, some dirty user agent sniffing?
Comment 13 Simon Fraser (smfr) 2019-10-29 18:25:27 PDT

*** This bug has been marked as a duplicate of bug 156435 ***
Comment 14 Frédéric Wang (:fredw) 2020-06-26 07:36:42 PDT
(In reply to Simon Fraser (smfr) from comment #13)
> 
> *** This bug has been marked as a duplicate of bug 156435 ***

Thanks Simon. The testcase seems to work in iOS 14 beta