简体   繁体   English

不正确 SVG 掩码 Position 使用相对位置时加载(Chrome)

[英]Incorrect SVG Mask Position On Load When Using Relative Positions (Chrome)

I am working on a project, where I want to show an image (blurred) and place 'magnifying glasses' (circle with a non blurred image) at positions of interest, that the user can interact with:我正在做一个项目,我想在其中显示图像(模糊)并将“放大镜”(带有非模糊图像的圆圈)放在感兴趣的位置,用户可以与之交互: 在此处输入图像描述

Therefore the image is placed inside a wrapper alongside an svg element.因此,图像与 svg 元素一起放置在包装器中。 The image should drive the size of the svg and therefore I'm not using the view-box attribute.该图像应驱动 svg 的大小,因此我没有使用view-box属性。 The SVG is positioned absolute and covers the whole wrapper. SVG 绝对定位并覆盖整个包装器。 Now I can place elements on the svg using relative positioning.现在我可以使用相对定位在 svg 上放置元素。 Then I place a circle at the position I want the magnifier to be (still working fine).然后我在 position 处放置一个圆圈,我希望放大镜成为(仍然工作正常)。 As a mask, I create another circle at the same position (inside a mask element) and use css mask(#id) on the unblurred image.作为蒙版,我在相同的 position(在蒙版元素内)创建了另一个圆圈,并在未模糊的图像上使用了 css蒙版(#id)

This works fine on Firefox, but on Chrome the mask does not show up.这在 Firefox 上运行良好,但在 Chrome 上不会显示掩码。

在此处输入图像描述

Only, when I resize the window, the mask appears and everything works as intended.只是,当我调整 window 的大小时,遮罩出现并且一切正常。

It seems like the mask element is not calculated correctly to its parent.似乎掩码元素未正确计算到其父元素。 And the mask is placed in the top left corner.面具放在左上角。 A resize of the window fixes this behaviour.调整 window 的大小可修复此行为。

HTML HTML

<div class="container">
  <img src="https://source.unsplash.com/2Ts5HnA67k8/400x200">
  <!-- SVG (purple) -->
  <!-- Setting a viewbox can solve the Issue, but I would prefer  not to. -->
  <div class="wrapper">
    <svg>
      <!-- Define the Mask -->
      <defs>

      </defs>

      <mask id="mask">
        <circle r="20%" cx="43%" cy="20%" fill="white"></circle>
      </mask>

      <!-- The stroke of the masked cicle. -->
      <circle r="20%" cx="43%" cy="20%" stroke="white" fill="transparent"></circle>

      <!-- The Element we want to mask. -->
      <image href="https://source.unsplash.com/2Ts5HnA67k8/400x200"></image>
    </svg>
  </div>
</div>

CSS CSS

img {
  filter: blur(4px);
}

image {
  mask: url(#mask);
}


svg {
  position: absolute;
  left:0;
  top:0;
  width:100%;
  height:100%;

How can I fix this behaviour for chrome on startup?如何在启动时修复 chrome 的这种行为? Or Is there a workaround, like triggering the recalculation of the mask layer manually?或者是否有解决方法,例如手动触发遮罩层的重新计算?

I created a working example here: https://codepen.io/Ukmasmu/pen/NWNrGoW我在这里创建了一个工作示例: https://codepen.io/Ukmasmu/pen/NWNrGoW

Any help would be highly appreciated: Thanks in advance :)任何帮助将不胜感激:提前致谢:)

I think the issue comes from Chrome's rendering engine not being able to properly deduce dimensions of the automatically-sized viewbox.我认为问题来自 Chrome 的渲染引擎无法正确推断自动调整大小的视图框的尺寸。 If you inspect the element and hover over the <circle> element in the <mask> element, you will realize that it has incorrectly computed dimensions, while in other browsers it resolves to the correct pixel value.如果您检查<mask>元素中的<circle>元素和 hover 元素,您会发现它计算的尺寸不正确,而在其他浏览器中它解析为正确的像素值。

The easier way is to simply use JS to set the viewBox attribute manually when the <img> element is loaded, to force Chrome to properly re-render the SVG based on an explicit viewbox value.更简单的方法是在加载<img>元素时简单地使用 JS 手动设置viewBox属性,以强制 Chrome 根据显式 viewbox 值正确地重新呈现 SVG。

 const img = document.querySelector('img'); const svg = document.querySelector('svg'); img.addEventListener('load', (e) => { svg.setAttribute('viewBox', `0 0 ${img.clientWidth} ${img.clientHeight}`) });
 img { filter: blur(4px); } image { mask: url(#mask); } svg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(138, 43, 226, 0.2); }.container { background-color: gray; position: relative; } html, body { width: 100%; height: 100%; margin: 0; } body { background-color: #111; display: flex; justify-content: center; align-items: center; }
 <div class="container"> <img src="https://source.unsplash.com/2Ts5HnA67k8/400x200"> <,-- SVG (purple) --> <.-- Setting a viewbox can solve the Issue. but I would prefer not to. --> <div class="wrapper"> <svg> <:-- Define the Mask --> <defs> </defs> <mask id="mask"> <circle r="20%" cx="43%" cy="20%" fill="white"></circle> </mask> <.-- The stroke of the masked cicle. --> <circle r="20%" cx="43%" cy="20%" stroke="white" fill="transparent"></circle> <!-- The Element we want to mask. --> <image href="https://source.unsplash.com/2Ts5HnA67k8/400x200"></image> </svg> </div> </div>

That's definitely a bug, probably bug=330815 .这绝对是一个错误,可能是bug=330815

Forcing a repaint of the mask will workaround the issue.强制重新绘制蒙版将解决此问题。

This could be achieved with a simple class setting at page load that would force something like display or position change:这可以通过在页面加载时进行简单的 class 设置来实现,该设置将强制displayposition更改:

 onload = evt => document.body.classList.add('loaded');
 /* Chrome hack around bug 330815 */.loaded mask { display: block; } img { filter: blur(4px); } image { mask: url("#mask"); } svg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(138, 43, 226, 0.2); }.container { background-color: gray; position: relative; } html, body { width: 100%; height: 100%; margin: 0; } body { background-color: #111; display: flex; justify-content: center; align-items: center; }
 <div class="container"> <img src="https://source.unsplash.com/2Ts5HnA67k8/400x200"> <,-- SVG (purple) --> <.-- Setting a viewbox can solve the Issue. but I would prefer not to. --> <div class="wrapper"> <svg> <:-- Define the Mask --> <defs> <mask id="mask"> <circle r="20%" cx="43%" cy="20%" fill="white"/> </mask> </defs> <.-- The stroke of the masked cicle. --> <circle r="20%" cx="43%" cy="20%" stroke="white" fill="none"/> <!-- The Element we want to mask. --> <image mask="url(#mask)" href="https://source.unsplash.com/2Ts5HnA67k8/400x200"></image> </svg> </div> </div>

If you really want something without JS, you could also achieve the same hack through SMIL.如果你真的想要没有 JS 的东西,你也可以通过 SMIL 实现同样的破解。
For instance, animating the href value of an <use> inside the <mask> seems to do it:例如,在<mask>中为<use>href值设置动画似乎可以做到这一点:

 img { filter: blur(4px); } image { mask: url("#mask"); } svg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(138, 43, 226, 0.2); }.container { background-color: gray; position: relative; } html, body { width: 100%; height: 100%; margin: 0; } body { background-color: #111; display: flex; justify-content: center; align-items: center; }
 <div class="container"> <img src="https://source.unsplash.com/2Ts5HnA67k8/400x200"> <,-- SVG (purple) --> <.-- Setting a viewbox can solve the Issue. but I would prefer not to. --> <div class="wrapper"> <svg> <:-- Define the Mask --> <defs> <circle r="20%" cx="43%" cy="20%" id="circle"/> <mask id="mask"> <use x="0" href="#circle" fill="white"> <.-- Chrome hack around bug 330815 --> <animate attributeName="href" to="#circle" begin="0" dur="0s"/> </use> </mask> </defs> <.-- The stroke of the masked cicle. --> <use href="#circle" fill="transparent" stroke="white"/> <!-- The Element we want to mask. --> <image mask="url(#mask)" href="https://source.unsplash.com/2Ts5HnA67k8/400x200"></image> </svg> </div> </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM