SVG蒙版的一部分不透明且颜色反转 [英] Part of SVG Mask Is Opaque and Color Is Inverted

查看:90
本文介绍了SVG蒙版的一部分不透明且颜色反转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要尝试的是在SVG蒙版中添加带纹理的框架或边框.

  1. 此处是我想要实现的工作示例. /p>

  2. 这就是我正在使用的.

请注意,在第二个演示中,与遮罩图像不同,遮罩中使用的图像#frame似乎没有任何透明度,并且颜色反转了(因此,实际上黑色显示为纯白色) ,在工作演示中为#rectangle > image.

但是,我可以在两个演示之间发现的唯一区别是,第一个正常工作的演示将feGaussianBlur应用于g元素.我已经尝试在第二个演示中将#eye#frame分组,但这似乎没有任何效果.

我想念什么?

解决方案

您需要将<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> 

What I'm trying to do is add a textured frame or border to an SVG mask.

  1. Here is a working demo of what I'd like to achieve.

  2. Here is what I'm working with.

Notice that in the second demo the image used in the mask, #frame, does not seem to have any transparency whatsoever and the color is inverted (so that what is actually black displays as pure white), unlike the mask image, #rectangle > image, in the working demo.

However, the only difference that I can spot between both demos is that the first, working demo applies a feGaussianBlur to a g element. I've tried grouping #eye and #frame in the second demo, but this didn't seem to have any effect.

What am I missing?

解决方案

You need to see your <mask> as a standalone grayscale image, that will get applied to the target element.
There every black pixels will be removed from the target, while every white and transparent ones will stay untouched or in other words, the darker it is in the mask, the more transparent it will be on the target.

So here are both masks

.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>

As you can see, your image's border is darker than the background rectangle, this means that the target will be more transparent at this image's border than in the background.

To solve this issue, you would have to make the black pixels of your image become the same shade of gray as the background, so that the target can get an unified opacity.

And while it is possible to do it with filters, note that it may kill the performances.

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蒙版的一部分不透明且颜色反转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆