问题描述
我正在一个项目中,我要显示图像(模糊)并将“放大镜”(带有非模糊图像的圆圈)放在感兴趣的位置,用户可以与以下位置进行交互:
因此,图像被放置在svg元素旁边的包装器内。该图像应驱动svg的大小,因此我不使用 view-box 属性。 SVG定位绝对,并覆盖整个包装纸。现在,我可以使用相对定位将元素放置在svg上。然后,在希望放大镜的位置放置一个圆圈(仍然可以正常工作)。作为蒙版,我在同一位置(蒙版元素内部)创建了另一个圆,并在未模糊的图像上使用css mask(#id)。
这在Firefox上可以正常使用,但在Chrome上不会显示遮罩。
只有当我调整窗口大小时,才会显示蒙版,并且一切都会按预期进行。
似乎mask元素的父元素计算不正确。并将遮罩放置在左上角。调整窗口大小可以解决此问题。
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
img {
filter: blur(4px);
}
image {
mask: url(#mask);
}
svg {
position: absolute;
left:0;
top:0;
width:100%;
height:100%;
如何在启动时修复Chrome的这种行为? 或者是否有解决方法,例如手动触发遮罩层的重新计算?
我在这里创建了一个工作示例: https://codepen.io/Ukmasmu/pen/NWNrGoW
任何帮助将不胜感激! 在此先感谢:)
解决方法
我认为问题出在Chrome的渲染引擎无法正确推断自动调整大小的视图框的尺寸。如果检查该元素并将鼠标悬停在<circle>
元素中的<mask>
元素上,您将意识到它的尺寸计算不正确,而在其他浏览器中,它将解析为正确的像素值。
更简单的方法是,在加载viewBox
元素时,简单地使用JS手动设置<img>
属性,以强制Chrome根据显式的viewbox值正确地重新渲染SVG。 / p>
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>
,
那绝对是一个错误,可能是bug=330815。
强制重涂面具将解决此问题。
这可以通过在页面加载时进行简单的类设置来实现,该类设置将强制进行display
或position
之类的更改:
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,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>
如果您真的想要没有JS的东西,也可以通过SMIL实现相同的破解。
例如,对href
内的<use>
的{{1}}值进行动画处理似乎可以做到:
<mask>
img {
filter: blur(4px);
}
image {
mask: url("#mask");
}
svg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(138,body {
width: 100%;
height: 100%;
margin: 0;
}
body {
background-color: #111;
display: flex;
justify-content: center;
align-items: center;
}