部分 SVG 蒙版不透明且颜色反转
Part of SVG Mask Is Opaque and Color Is Inverted
我想做的是向 SVG 蒙版添加带纹理的框架或边框。
请注意,在第二个演示中,蒙版中使用的图像 #frame
似乎没有任何透明度并且颜色是反转的(因此实际上黑色显示为纯白色),与工作演示中的蒙版图像 #rectangle > image
不同。
但是,我可以在两个演示之间发现的唯一区别是第一个工作演示将 feGaussianBlur
应用于 g
元素。我试过在第二个演示中将 #eye
和 #frame
分组,但这似乎没有任何效果。
我错过了什么?
玩你在 CodePen 中的工作,我注意到你在 #frame
图像和外部背景中得到的白色来自默认的 SVG 文档背景颜色,它没有定义并且看起来是白色的。
如果您为 #svg
元素定义 background-color
样式,比如 #f00
(红色),您将看到背景和 #frame
图像都为红色.
最后,我发现将 #frame
图像的 opacity
调整为 0.4
可以更好地融合蒙版框架和背景。您可以重新绘制 #frame
图像以更好地匹配图像矩形边界处的背景。
我刚刚在你的 CodePen 中添加了 background-color
(白色)和 opacity
(0.4),如下所示:
#svg {
width: 6144px;
height: 4608px;
position: absolute;
left: -3072px;
top: -2304px;
background-color: #fff;
}
#frame {
opacity: .4;
}
您需要将 <mask>
视为独立的灰度图像,它将应用于目标元素。
那里每个黑色像素将从目标中移除,而每个白色和透明像素将保持不变,换句话说,蒙版中的颜色越深,目标上的透明度就越高。
所以这是两个面具
.bg {
width: 100%;
height: 100%;
fill: #666;
}
#background {
fill: #999;
}
#eye {
fill: #fff;
}
.fake-mask {
filter: grayscale(100%);
}
svg{width: 40vw; display: inline-block}
<svg viewBox='0 0 800 800'>
<defs>
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
</defs>
<!-- <mask id="myMask"> -->
<g class="fake-mask">
<rect class='bg' width="800" height="800"/>
<g id="rectangle" filter="url(#blurMe)">
<rect width="300" height="400" x="120" rx='10' ry='10' fill="white" />
<image
xlink:href='https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'
width="200" height="200"/>
</g>
</g>
<!-- </mask> -->
</svg><svg viewBox='0 0 800 800'>
<!-- <mask id='mask'> -->
<g class="fake-mask">
<rect id='background' x='0' y='0' width='6144' height='4608' />
<rect id='eye' x='0' y='0' width='500' height='500' />
<image id='frame' xlink:href='https://newvitruvian.com/images/speckled-vector-distress.png' x='0' y='0' width='500' height='500' preserveAspectRatio='none' />
</g>
<!-- </mask> -->
</svg>
如您所见,图片的边框比背景矩形更暗,这意味着目标在此图片的边框比背景更透明。
要解决此问题,您必须使图像的黑色像素成为与背景相同的灰色阴影,以便目标可以获得统一的不透明度。
虽然可以使用过滤器来完成,但请注意,它可能会破坏性能。
const
bdy = document.body,
svg = document.getElementById('svg'),
bkg = document.getElementById('background'),
eye = document.getElementById('eye'),
frm = document.getElementById('frame')
let
eyeW = 0.35,
eyeH = 0.75,
mousednX = 0,
mousednY = 0
// position maps on load
//
window.addEventListener('load', position)
function position(){
const
box = svg.getBoundingClientRect()
svg.style.left = -(box.width - innerWidth) / 2 + 'px'
svg.style.top = -(box.height - innerHeight) / 2 + 'px'
const
x = -(svg.getBoundingClientRect().left) + innerWidth * (1 - eyeW) / 2,
y = -(svg.getBoundingClientRect().top) + innerHeight * (1 - eyeH) / 2
eye.setAttribute('width', innerWidth * eyeW)
eye.setAttribute('height', innerHeight * eyeH)
eye.setAttribute('x', x)
eye.setAttribute('y', y)
frm.setAttribute('width', innerWidth * eyeW)
frm.setAttribute('height', innerHeight * eyeH)
frm.setAttribute('x', x)
frm.setAttribute('y', y)
}
// drag functionality to explore map
//
bdy.addEventListener('mousedown', mousedown)
window.addEventListener('mouseup', mouseup)
function mousedown(e){
e.preventDefault()
mousednX = e.clientX
mousednY = e.clientY
bdy.addEventListener('mousemove', mousemove)
}
function mouseup(){
bdy.removeEventListener('mousemove', mousemove)
}
function mousemove(e){
adjustX = e.clientX - mousednX
adjustY = e.clientY - mousednY
if (svg.getBoundingClientRect().left + adjustX < 0 && svg.getBoundingClientRect().right + adjustX > innerWidth){
svg.style.left = svg.getBoundingClientRect().left + adjustX + 'px'
} else if (svg.getBoundingClientRect().left + adjustX >= 0){
svg.style.left = 0 + 'px'
} else {
svg.style.left = -(svg.getBoundingClientRect().width - innerWidth)
}
if (svg.getBoundingClientRect().top + adjustY < 0 && svg.getBoundingClientRect().bottom + adjustY > innerHeight){
svg.style.top = svg.getBoundingClientRect().top + adjustY + 'px'
} else if (svg.getBoundingClientRect().top + adjustY >= 0){
svg.style.top = 0 + 'px'
} else {
svg.style.top = -(svg.getBoundingClientRect().height - innerHeight)
}
mousednX = e.clientX
mousednY = e.clientY
}
// center eye on cursor position
//
bdy.addEventListener('mousemove', moveEye)
function moveEye(e){
const
x = -(svg.getBoundingClientRect().left) + e.clientX - eyeW * innerWidth / 2,
y = -(svg.getBoundingClientRect().top) + e.clientY - eyeH * innerHeight / 2
eye.setAttribute('x', x)
eye.setAttribute('y', y)
frm.setAttribute('x', x)
frm.setAttribute('y', y)
}
body {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
}
#svg {
width: 6144px;
height: 4608px;
position: absolute;
left: -3072px;
top: -2304px;
}
#eye {
fill: #FFF;
}
#map {
width: 6144px;
height: 4608px;
mask: url('#mask');
}
<svg id='svg' viewBox='0 0 6144 4608' version='1.1'>
<filter id="contrast">
<feComponentTransfer>
<feFuncR type="linear" slope="0.4" intercept="0.2"/>
<feFuncG type="linear" slope="0.4" intercept="0.2"/>
<feFuncB type="linear" slope="0.4" intercept="0.2"/>
</feComponentTransfer>
</filter>
<mask id='mask'>
<g filter="url(#contrast)">
<rect id='background' x='0' y='0' width='6144' height='4608' fill="#000"/>
<rect id='eye' x='0' y='0' width='0' height='0' />
<image id='frame' xlink:href='https://newvitruvian.com/images/speckled-vector-distress.png' x='0' y='0' width='0' height='0' preserveAspectRatio='none'/>
</g>
</mask>
<image id='map' xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg' x='0' y='0' width='6144' height='4608' mask="url(#myMask)"/>
</svg>
我想做的是向 SVG 蒙版添加带纹理的框架或边框。
请注意,在第二个演示中,蒙版中使用的图像 #frame
似乎没有任何透明度并且颜色是反转的(因此实际上黑色显示为纯白色),与工作演示中的蒙版图像 #rectangle > image
不同。
但是,我可以在两个演示之间发现的唯一区别是第一个工作演示将 feGaussianBlur
应用于 g
元素。我试过在第二个演示中将 #eye
和 #frame
分组,但这似乎没有任何效果。
我错过了什么?
玩你在 CodePen 中的工作,我注意到你在 #frame
图像和外部背景中得到的白色来自默认的 SVG 文档背景颜色,它没有定义并且看起来是白色的。
如果您为 #svg
元素定义 background-color
样式,比如 #f00
(红色),您将看到背景和 #frame
图像都为红色.
最后,我发现将 #frame
图像的 opacity
调整为 0.4
可以更好地融合蒙版框架和背景。您可以重新绘制 #frame
图像以更好地匹配图像矩形边界处的背景。
我刚刚在你的 CodePen 中添加了 background-color
(白色)和 opacity
(0.4),如下所示:
#svg {
width: 6144px;
height: 4608px;
position: absolute;
left: -3072px;
top: -2304px;
background-color: #fff;
}
#frame {
opacity: .4;
}
您需要将 <mask>
视为独立的灰度图像,它将应用于目标元素。
那里每个黑色像素将从目标中移除,而每个白色和透明像素将保持不变,换句话说,蒙版中的颜色越深,目标上的透明度就越高。
所以这是两个面具
.bg {
width: 100%;
height: 100%;
fill: #666;
}
#background {
fill: #999;
}
#eye {
fill: #fff;
}
.fake-mask {
filter: grayscale(100%);
}
svg{width: 40vw; display: inline-block}
<svg viewBox='0 0 800 800'>
<defs>
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
</defs>
<!-- <mask id="myMask"> -->
<g class="fake-mask">
<rect class='bg' width="800" height="800"/>
<g id="rectangle" filter="url(#blurMe)">
<rect width="300" height="400" x="120" rx='10' ry='10' fill="white" />
<image
xlink:href='https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'
width="200" height="200"/>
</g>
</g>
<!-- </mask> -->
</svg><svg viewBox='0 0 800 800'>
<!-- <mask id='mask'> -->
<g class="fake-mask">
<rect id='background' x='0' y='0' width='6144' height='4608' />
<rect id='eye' x='0' y='0' width='500' height='500' />
<image id='frame' xlink:href='https://newvitruvian.com/images/speckled-vector-distress.png' x='0' y='0' width='500' height='500' preserveAspectRatio='none' />
</g>
<!-- </mask> -->
</svg>
如您所见,图片的边框比背景矩形更暗,这意味着目标在此图片的边框比背景更透明。
要解决此问题,您必须使图像的黑色像素成为与背景相同的灰色阴影,以便目标可以获得统一的不透明度。
虽然可以使用过滤器来完成,但请注意,它可能会破坏性能。
const
bdy = document.body,
svg = document.getElementById('svg'),
bkg = document.getElementById('background'),
eye = document.getElementById('eye'),
frm = document.getElementById('frame')
let
eyeW = 0.35,
eyeH = 0.75,
mousednX = 0,
mousednY = 0
// position maps on load
//
window.addEventListener('load', position)
function position(){
const
box = svg.getBoundingClientRect()
svg.style.left = -(box.width - innerWidth) / 2 + 'px'
svg.style.top = -(box.height - innerHeight) / 2 + 'px'
const
x = -(svg.getBoundingClientRect().left) + innerWidth * (1 - eyeW) / 2,
y = -(svg.getBoundingClientRect().top) + innerHeight * (1 - eyeH) / 2
eye.setAttribute('width', innerWidth * eyeW)
eye.setAttribute('height', innerHeight * eyeH)
eye.setAttribute('x', x)
eye.setAttribute('y', y)
frm.setAttribute('width', innerWidth * eyeW)
frm.setAttribute('height', innerHeight * eyeH)
frm.setAttribute('x', x)
frm.setAttribute('y', y)
}
// drag functionality to explore map
//
bdy.addEventListener('mousedown', mousedown)
window.addEventListener('mouseup', mouseup)
function mousedown(e){
e.preventDefault()
mousednX = e.clientX
mousednY = e.clientY
bdy.addEventListener('mousemove', mousemove)
}
function mouseup(){
bdy.removeEventListener('mousemove', mousemove)
}
function mousemove(e){
adjustX = e.clientX - mousednX
adjustY = e.clientY - mousednY
if (svg.getBoundingClientRect().left + adjustX < 0 && svg.getBoundingClientRect().right + adjustX > innerWidth){
svg.style.left = svg.getBoundingClientRect().left + adjustX + 'px'
} else if (svg.getBoundingClientRect().left + adjustX >= 0){
svg.style.left = 0 + 'px'
} else {
svg.style.left = -(svg.getBoundingClientRect().width - innerWidth)
}
if (svg.getBoundingClientRect().top + adjustY < 0 && svg.getBoundingClientRect().bottom + adjustY > innerHeight){
svg.style.top = svg.getBoundingClientRect().top + adjustY + 'px'
} else if (svg.getBoundingClientRect().top + adjustY >= 0){
svg.style.top = 0 + 'px'
} else {
svg.style.top = -(svg.getBoundingClientRect().height - innerHeight)
}
mousednX = e.clientX
mousednY = e.clientY
}
// center eye on cursor position
//
bdy.addEventListener('mousemove', moveEye)
function moveEye(e){
const
x = -(svg.getBoundingClientRect().left) + e.clientX - eyeW * innerWidth / 2,
y = -(svg.getBoundingClientRect().top) + e.clientY - eyeH * innerHeight / 2
eye.setAttribute('x', x)
eye.setAttribute('y', y)
frm.setAttribute('x', x)
frm.setAttribute('y', y)
}
body {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
}
#svg {
width: 6144px;
height: 4608px;
position: absolute;
left: -3072px;
top: -2304px;
}
#eye {
fill: #FFF;
}
#map {
width: 6144px;
height: 4608px;
mask: url('#mask');
}
<svg id='svg' viewBox='0 0 6144 4608' version='1.1'>
<filter id="contrast">
<feComponentTransfer>
<feFuncR type="linear" slope="0.4" intercept="0.2"/>
<feFuncG type="linear" slope="0.4" intercept="0.2"/>
<feFuncB type="linear" slope="0.4" intercept="0.2"/>
</feComponentTransfer>
</filter>
<mask id='mask'>
<g filter="url(#contrast)">
<rect id='background' x='0' y='0' width='6144' height='4608' fill="#000"/>
<rect id='eye' x='0' y='0' width='0' height='0' />
<image id='frame' xlink:href='https://newvitruvian.com/images/speckled-vector-distress.png' x='0' y='0' width='0' height='0' preserveAspectRatio='none'/>
</g>
</mask>
<image id='map' xlink:href='https://i.postimg.cc/hvH4yn2Q/map.jpg' x='0' y='0' width='6144' height='4608' mask="url(#myMask)"/>
</svg>