Open toFrankie opened 1 year ago
实锤了,Safari 就是新时代的 IE 浏览器。原因是有些东西在 Safari 渲染表现与预期(标准)不一致,而且 Safari for Mac 跟 Safari for iOS 的表现还不一定是相同的。
今天遇到了这样一个问题。举个例子,假设外层一个 max-width: 430px 的 section 元素,里面是一个 svg 元素,里面包含动画还有嵌套了一些元素。预期表现是:点击红色区域,绿色背景透明度匀速从 0 切换至 1。
<section style="max-width: 430px; margin: auto; overflow: hidden; font-size: 0"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="pointer-events: none; width: 100%; background-color: red"> <foreignObject x="0" y="0" width="100%" height="100%"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; background-color: green"> <animate attributeName="opacity" begin="click" from="0" to="1" calcMode="linear" dur="1s" fill="freeze" restart="never" /> <rect x="0" y="0" width="100%" height="100%" fill="transparent" style="pointer-events: visible"> <set attributeName="visibility" begin="click" to="hidden" fill="freeze" restart="never" /> </rect> </svg> </foreignObject> </svg> </section>
body { margin: 20px; }
根据所设置的 viewBox="0 0 350 350"、preserveAspectRatio="xMidYMin meet" 以及 width: 100%,按道理的话,红色的 <svg> 及其内嵌套 <foreignObject> 和 <svg>,应该都是同等大小的正方形,而且取决于父元素 <section> 的宽度。
viewBox="0 0 350 350"
preserveAspectRatio="xMidYMin meet"
width: 100%
<svg>
<foreignObject>
<section>
是的,这个在 Chrome 表现没问题,但在 Safari for Mac 上就出现问题了,离奇的是 Safari for iOS 也是正常的。
如下图,此时 <body> 的宽度是大于 430px,因此 <section> 的宽度为 430px,自然 <svg> 的宽度就是 430px。
<body>
430px
但是,当我们点击蓝色框之外,红色区域(截图由于选中元素,该区域表现为橘色)以内的位置,你知道 Safari 定位到的元素是什么吗?
嗯......它定位到 <section> 元素了。意思就是说,内部的 元素区域并未覆盖到点击区,但我宽高明明设置的都是 100%,就很离谱。
100%
但是,我在右侧 Elements 选项卡选中 <rect> 元素时,它表现的区域明明就是占满的啊,也就是 430 * 430。
<rect>
430 * 430
Safari 你在玩我?
经多次测试,它可点击区域只有 350 * 350,也就是 viewBox 那个空间。
350 * 350
viewBox
由于是 Safari 的 bug,目前只能用一些治标不治本的方法,用魔法打败魔法。
给 <rect> 设置 transform: scale(2); transform-origin: left top;,其父级的 <svg> 设置 overflow: visible。由于 <foreignObject> 元素默认为 overflow: hidden,因此不用担心点击 430 * 430 之外的位置会触发事件。
transform: scale(2); transform-origin: left top;
overflow: visible
overflow: hidden
利用 <svg> 做了一个循环切换的交互,同样地,它在 Chrome 一切安好,而在 Safari 下则惊喜满满。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="width: 100%"> <foreignObject x="0" y="0" width="100%" height="100%"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="width: 100%"> <foreignObject x="0" y="0" width="100%" height="100%"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 1; width: 100%; background-size: cover; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475354583.png); background-color: red"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.22222222; 0.33333333; 1" values="1; 1; 0; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </foreignObject> <foreignObject x="0" y="0" width="100%" height="100%"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; width: 100%; background-repeat: no-repeat; background-size: cover; background-position: top center; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475369330.png); background-color: green"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.22222222; 0.33333333; 0.55555556; 0.66666667; 0.666666670001; 1" values="0; 0; 1; 1; 0; 0; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </foreignObject> <foreignObject x="0" y="0" width="100%" height="100%"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; width: 100%; background-repeat: no-repeat; background-size: cover; background-position: top center; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475408407.png); background-color: blue"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.55555556; 0.66666667; 0.88888889; 0.99999999; 1" values="0; 0; 1; 1; 0; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </foreignObject> <foreignObject x="0" y="0" width="100%" height="100%"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; width: 100%; background-repeat: no-repeat; background-size: cover; background-position: top center; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475354583.png); background-color: red"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.88888889; 0.99999999; 1" values="0; 0; 1; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </foreignObject> </svg> </foreignObject> </svg>
Safari 表现出「忽大忽小」的问题。如下图,灰色背景大小为 430 430,而红色背景处则是 350 350。
由于录制 GIF 太麻烦了,你可以使用 Safari 打开链接体验一下:https://codepen.io/tofrankie/full/abRWpaE。
由于 <foreignObject> 的坑,那就不要嵌套多层,所以可以这样处理,结构上也更清晰。
<section> <section style="height: 0"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 1; width: 100%; background-size: cover; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475354583.png); background-color: red"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.22222222; 0.33333333; 1" values="1; 1; 0; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </section> <section style="height: 0"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; width: 100%; background-repeat: no-repeat; background-size: cover; background-position: top center; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475369330.png); background-color: green"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.22222222; 0.33333333; 0.55555556; 0.66666667; 0.666666670001; 1" values="0; 0; 1; 1; 0; 0; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </section> <section style="height: 0"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; width: 100%; background-repeat: no-repeat; background-size: cover; background-position: top center; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475408407.png); background-color: blue"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.55555556; 0.66666667; 0.88888889; 0.99999999; 1" values="0; 0; 1; 1; 0; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </section> <section style="height: 0"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="opacity: 0; width: 100%; background-repeat: no-repeat; background-size: cover; background-position: top center; background-image: url(https://cdn.jsdelivr.net/gh/toFrankie/blog/images/1682475354583.png); background-color: red"> <animate attributeName="opacity" begin="0s" keyTimes="0; 0.88888889; 0.99999999; 1" values="0; 0; 1; 0" calcMode="linear" dur="9s" repeatCount="indefinite" /> </svg> </section> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" preserveAspectRatio="xMidYMin meet" style="width: 100%"></svg> </section>
这是 Webkit 的 Bug,相关链接:
该问题早在 2009 年就提出了,至今仍然没有任何进展,隔壁 Chromium 的 Blink 已在 2020 年 9 月修复。其中一个可复现的示例:https://codesandbox.io/s/chrome-foreignobject-defect-wf91j。在 Safari 打开使用 ⌘ + + 或 ⌘ + - 去缩放页面就能看到。
⌘
+
-
我用 Chrome 62 亲测了一下,确实也有问题,而且区域更小了。
简言之,根本原因就是 Safari/WebKit 无法正确渲染 <foreignObject> 中的 HTML 元素。
实锤了,Safari 就是新时代的 IE 浏览器。
赞一个,但我还是没解决safari 兼容问题 渲染位置偏移,放大缩小后偏位更夸张,拖动元素偶尔出现残影
实锤了,Safari 就是新时代的 IE 浏览器。原因是有些东西在 Safari 渲染表现与预期(标准)不一致,而且 Safari for Mac 跟 Safari for iOS 的表现还不一定是相同的。
背景
今天遇到了这样一个问题。举个例子,假设外层一个 max-width: 430px 的 section 元素,里面是一个 svg 元素,里面包含动画还有嵌套了一些元素。预期表现是:点击红色区域,绿色背景透明度匀速从 0 切换至 1。
根据所设置的
viewBox="0 0 350 350"
、preserveAspectRatio="xMidYMin meet"
以及width: 100%
,按道理的话,红色的<svg>
及其内嵌套<foreignObject>
和<svg>
,应该都是同等大小的正方形,而且取决于父元素<section>
的宽度。是的,这个在 Chrome 表现没问题,但在 Safari for Mac 上就出现问题了,离奇的是 Safari for iOS 也是正常的。
案例一
如下图,此时
<body>
的宽度是大于 430px,因此<section>
的宽度为 430px,自然<svg>
的宽度就是430px
。但是,当我们点击蓝色框之外,红色区域(截图由于选中元素,该区域表现为橘色)以内的位置,你知道 Safari 定位到的元素是什么吗?
嗯......它定位到 元素区域并未覆盖到点击区,但我宽高明明设置的都是
<section>
元素了。意思就是说,内部的100%
,就很离谱。但是,我在右侧 Elements 选项卡选中
<rect>
元素时,它表现的区域明明就是占满的啊,也就是430 * 430
。Safari 你在玩我?
经多次测试,它可点击区域只有
350 * 350
,也就是viewBox
那个空间。解决办法
由于是 Safari 的 bug,目前只能用一些治标不治本的方法,用魔法打败魔法。
给
<rect>
设置transform: scale(2); transform-origin: left top;
,其父级的<svg>
设置overflow: visible
。由于<foreignObject>
元素默认为overflow: hidden
,因此不用担心点击430 * 430
之外的位置会触发事件。案例二
利用
<svg>
做了一个循环切换的交互,同样地,它在 Chrome 一切安好,而在 Safari 下则惊喜满满。Safari 表现出「忽大忽小」的问题。如下图,灰色背景大小为 430 430,而红色背景处则是 350 350。
由于录制 GIF 太麻烦了,你可以使用 Safari 打开链接体验一下:https://codepen.io/tofrankie/full/abRWpaE。
解决方法
由于
<foreignObject>
的坑,那就不要嵌套多层,所以可以这样处理,结构上也更清晰。原因
这是 Webkit 的 Bug,相关链接:
该问题早在 2009 年就提出了,至今仍然没有任何进展,隔壁 Chromium 的 Blink 已在 2020 年 9 月修复。其中一个可复现的示例:https://codesandbox.io/s/chrome-foreignobject-defect-wf91j。在 Safari 打开使用
⌘
++
或⌘
+-
去缩放页面就能看到。我用 Chrome 62 亲测了一下,确实也有问题,而且区域更小了。
简言之,根本原因就是 Safari/WebKit 无法正确渲染
<foreignObject>
中的 HTML 元素。实锤了,Safari 就是新时代的 IE 浏览器。
References