@@ -190,6 +190,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface
190
190
final ui.Color shadowColor;
191
191
final ui.Clip clipBehavior;
192
192
html.Element ? _clipElement;
193
+ html.Element ? _svgElement;
193
194
194
195
@override
195
196
void recomputeTransformAndClip () {
@@ -214,23 +215,18 @@ class PersistedPhysicalShape extends PersistedContainerSurface
214
215
rootElement! .style.backgroundColor = colorToCssString (color);
215
216
}
216
217
217
- void _applyShadow () {
218
- applyCssShadow (rootElement, pathBounds, elevation, shadowColor);
219
- }
220
-
221
218
@override
222
219
html.Element createElement () {
223
220
return super .createElement ()..setAttribute ('clip-type' , 'physical-shape' );
224
221
}
225
222
226
223
@override
227
224
void apply () {
228
- _applyColor ();
229
- _applyShadow ();
230
225
_applyShape ();
231
226
}
232
227
233
228
void _applyShape () {
229
+ _applyColor ();
234
230
// Handle special case of round rect physical shape mapping to
235
231
// rounded div.
236
232
final ui.RRect ? roundRect = path.toRoundedRect ();
@@ -251,6 +247,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface
251
247
if (clipBehavior != ui.Clip .none) {
252
248
style.overflow = 'hidden' ;
253
249
}
250
+ applyCssShadow (rootElement, pathBounds, elevation, shadowColor);
254
251
return ;
255
252
} else {
256
253
final ui.Rect ? rect = path.toRect ();
@@ -268,6 +265,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface
268
265
if (clipBehavior != ui.Clip .none) {
269
266
style.overflow = 'hidden' ;
270
267
}
268
+ applyCssShadow (rootElement, pathBounds, elevation, shadowColor);
271
269
return ;
272
270
} else {
273
271
final ui.Rect ? ovalRect = path.toCircle ();
@@ -291,26 +289,64 @@ class PersistedPhysicalShape extends PersistedContainerSurface
291
289
if (clipBehavior != ui.Clip .none) {
292
290
style.overflow = 'hidden' ;
293
291
}
292
+ applyCssShadow (rootElement, pathBounds, elevation, shadowColor);
294
293
return ;
295
294
}
296
295
}
297
296
}
298
297
299
- final String svgClipPath = _pathToSvgClipPath (path,
300
- offsetX: - pathBounds.left,
301
- offsetY: - pathBounds.top,
302
- scaleX: 1.0 / pathBounds.width,
303
- scaleY: 1.0 / pathBounds.height);
304
- // If apply is called multiple times (without update) , remove prior
305
- // svg clip element.
298
+ /// If code reaches this point, we have a path we want to clip against and
299
+ /// potentially have a shadow due to material surface elevation.
300
+ ///
301
+ /// When there is no shadow we can simply clip a div with a background
302
+ /// color using a svg clip path.
303
+ ///
304
+ /// Otherwise we need to paint svg element for the path and clip
305
+ /// contents against same path for shadow to work since box-shadow doesn't
306
+ /// take clip-path into account.
307
+ ///
308
+ /// Webkit has a bug when applying clip-path on an element that has
309
+ /// position: absolute and transform
310
+ /// (https://bugs.webkit.org/show_bug.cgi?id=141731).
311
+ /// To place clipping rectangle correctly
312
+ /// we size the inner container to cover full pathBounds instead of sizing
313
+ /// to clipping rect bounds (which is the case for elevation == 0.0 where
314
+ /// we shift outer/inner clip area instead to position clip-path).
315
+ final String svgClipPath = elevation == 0.0
316
+ ? _pathToSvgClipPath (path,
317
+ offsetX: - pathBounds.left,
318
+ offsetY: - pathBounds.top,
319
+ scaleX: 1.0 / pathBounds.width,
320
+ scaleY: 1.0 / pathBounds.height)
321
+ : _pathToSvgClipPath (path,
322
+ offsetX: 0.0 ,
323
+ offsetY: 0.0 ,
324
+ scaleX: 1.0 / pathBounds.right,
325
+ scaleY: 1.0 / pathBounds.bottom);
326
+ /// If apply is called multiple times (without update), remove prior
327
+ /// svg clip and render elements.
306
328
_clipElement? .remove ();
329
+ _svgElement? .remove ();
307
330
_clipElement =
308
331
html.Element .html (svgClipPath, treeSanitizer: _NullTreeSanitizer ());
309
332
domRenderer.append (rootElement! , _clipElement! );
310
- DomRenderer .setElementStyle (
311
- rootElement! , 'clip-path' , 'url(#svgClip$_clipIdCounter )' );
312
- DomRenderer .setElementStyle (
313
- rootElement! , '-webkit-clip-path' , 'url(#svgClip$_clipIdCounter )' );
333
+ if (elevation == 0.0 ) {
334
+ DomRenderer .setClipPath (rootElement! , 'url(#svgClip$_clipIdCounter )' );
335
+ final html.CssStyleDeclaration rootElementStyle = rootElement! .style;
336
+ rootElementStyle
337
+ ..overflow = ''
338
+ ..left = '${pathBounds .left }px'
339
+ ..top = '${pathBounds .top }px'
340
+ ..width = '${pathBounds .width }px'
341
+ ..height = '${pathBounds .height }px'
342
+ ..borderRadius = '' ;
343
+ childContainer! .style
344
+ ..left = '-${pathBounds .left }px'
345
+ ..top = '-${pathBounds .top }px' ;
346
+ return ;
347
+ }
348
+
349
+ DomRenderer .setClipPath (childContainer! , 'url(#svgClip$_clipIdCounter )' );
314
350
final html.CssStyleDeclaration rootElementStyle = rootElement! .style;
315
351
rootElementStyle
316
352
..overflow = ''
@@ -321,28 +357,45 @@ class PersistedPhysicalShape extends PersistedContainerSurface
321
357
..borderRadius = '' ;
322
358
childContainer! .style
323
359
..left = '-${pathBounds .left }px'
324
- ..top = '-${pathBounds .top }px' ;
360
+ ..top = '-${pathBounds .top }px'
361
+ ..width = '${pathBounds .right }px'
362
+ ..height = '${pathBounds .bottom }px' ;
363
+
364
+ final ui.Rect pathBounds2 = path.getBounds ();
365
+ _svgElement = _pathToSvgElement (
366
+ path, SurfacePaintData ()..color = color, '${pathBounds2 .right }' , '${pathBounds2 .bottom }' );
367
+ /// Render element behind the clipped content.
368
+ rootElement! .insertBefore (_svgElement! , childContainer);
369
+
370
+ final SurfaceShadowData shadow = computeShadow (pathBounds, elevation)! ;
371
+ final ui.Color boxShadowColor = toShadowColor (shadowColor);
372
+ _svgElement! .style
373
+ ..filter =
374
+ 'drop-shadow(${shadow .offset .dx }px ${shadow .offset .dy }px '
375
+ '${shadow .blurWidth }px '
376
+ 'rgba(${boxShadowColor .red }, ${boxShadowColor .green }, '
377
+ '${boxShadowColor .blue }, ${boxShadowColor .alpha / 255 }))'
378
+ ..transform = 'translate(-${pathBounds2 .left }px, -${pathBounds2 .top }px)' ;
379
+
380
+ rootElement! .style.backgroundColor = '' ;
325
381
}
326
382
327
383
@override
328
384
void update (PersistedPhysicalShape oldSurface) {
329
385
super .update (oldSurface);
330
- if (oldSurface.color != color) {
331
- _applyColor ();
332
- }
333
- if (oldSurface.elevation != elevation ||
334
- oldSurface.shadowColor != shadowColor) {
335
- _applyShadow ();
336
- }
337
- if (oldSurface.path != path) {
386
+ if (oldSurface.path != path || oldSurface.elevation != elevation ||
387
+ oldSurface.shadowColor != shadowColor || oldSurface.color != color) {
338
388
oldSurface._clipElement? .remove ();
339
389
oldSurface._clipElement = null ;
390
+ oldSurface._svgElement? .remove ();
391
+ oldSurface._svgElement = null ;
340
392
_clipElement? .remove ();
341
393
_clipElement = null ;
394
+ _svgElement? .remove ();
395
+ _svgElement = null ;
342
396
// Reset style on prior element since we may have switched between
343
397
// rect/rrect and arbitrary path.
344
- DomRenderer .setElementStyle (rootElement! , 'clip-path' , '' );
345
- DomRenderer .setElementStyle (rootElement! , '-webkit-clip-path' , '' );
398
+ DomRenderer .setClipPath (rootElement! , '' );
346
399
_applyShape ();
347
400
} else {
348
401
// Reuse clipElement from prior surface.
@@ -351,6 +404,10 @@ class PersistedPhysicalShape extends PersistedContainerSurface
351
404
domRenderer.append (rootElement! , _clipElement! );
352
405
}
353
406
oldSurface._clipElement = null ;
407
+ _svgElement = oldSurface._svgElement;
408
+ if (_svgElement != null ) {
409
+ rootElement! .insertBefore (_svgElement! , childContainer);
410
+ }
354
411
}
355
412
}
356
413
}
@@ -416,10 +473,7 @@ String createSvgClipDef(html.HtmlElement element, ui.Path clipPath) {
416
473
final ui.Rect pathBounds = clipPath.getBounds ();
417
474
final String svgClipPath = _pathToSvgClipPath (clipPath,
418
475
scaleX: 1.0 / pathBounds.right, scaleY: 1.0 / pathBounds.bottom);
419
- DomRenderer .setElementStyle (
420
- element, 'clip-path' , 'url(#svgClip$_clipIdCounter )' );
421
- DomRenderer .setElementStyle (
422
- element, '-webkit-clip-path' , 'url(#svgClip$_clipIdCounter )' );
476
+ DomRenderer .setClipPath (element, 'url(#svgClip$_clipIdCounter )' );
423
477
// We need to set width and height for the clipElement to cover the
424
478
// bounds of the path since browsers such as Safari and Edge
425
479
// seem to incorrectly intersect the element bounding rect with
0 commit comments