Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safari/WebKit 无法正确渲染 <foreignObject> 中的 HTML 元素 #312

Open
toFrankie opened this issue Apr 25, 2023 · 1 comment
Open
Labels
2023 2023 年撰写 CSS 与 CSS、CSS preprocessor 相关的文章

Comments

@toFrankie
Copy link
Owner

toFrankie commented Apr 25, 2023

配图源自 Freepik

实锤了,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> 的宽度。

是的,这个在 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 下则惊喜满满。

<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@main/images/2023/4/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@main/images/2023/4/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@main/images/2023/4/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@main/images/2023/4/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@main/images/2023/4/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@main/images/2023/4/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@main/images/2023/4/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@main/images/2023/4/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 亲测了一下,确实也有问题,而且区域更小了。

Chrome 62

简言之,根本原因就是 Safari/WebKit 无法正确渲染 <foreignObject> 中的 HTML 元素

实锤了,Safari 就是新时代的 IE 浏览器。

References

@toFrankie toFrankie added CSS 与 CSS、CSS preprocessor 相关的文章 2023 2023 年撰写 labels Apr 25, 2023
@toFrankie toFrankie changed the title 关于 foreignObject 在 Safari 中未按预期进行缩放的问题 关于 foreignObject 在 Safari 浏览器存在的 bug Apr 25, 2023
@toFrankie toFrankie changed the title 关于 foreignObject 在 Safari 浏览器存在的 bug 关于 <foreignObject> 在 Safari 上的异常表现 Apr 26, 2023
@toFrankie toFrankie changed the title 关于 <foreignObject> 在 Safari 上的异常表现 Safari/WebKit 无法正确渲染 <foreignObject> 中的 HTML 元素 Apr 26, 2023
@Damon99999
Copy link

Damon99999 commented May 23, 2023

赞一个,但我还是没解决safari 兼容问题
渲染位置偏移,放大缩小后偏位更夸张,拖动元素偶尔出现残影

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2023 2023 年撰写 CSS 与 CSS、CSS preprocessor 相关的文章
Projects
None yet
Development

No branches or pull requests

2 participants