Часть маски SVG непрозрачна, а цвет перевернут - PullRequest
0 голосов
/ 22 января 2019

Я пытаюсь добавить текстурированную рамку или рамку к маске SVG.

  1. Здесь - рабочая демонстрация того, чего я хотел бы достичь.

  2. Здесь - это то, с чем я работаю.

Обратите внимание, что во второй демонстрации изображение, используемое в маске, #frame, кажется, не имеет никакой прозрачности, и цвет инвертируется (так что то, что на самом деле является черным, отображается как чисто белый), в отличие от изображения маски #rectangle > image, в рабочем демо.

Однако единственное различие, которое я могу заметить между обеими демонстрациями, заключается в том, что первая рабочая демонстрация применяет feGaussianBlur к элементу g. Я попытался сгруппировать #eye и #frame во втором демо, но это, похоже, не дало никакого эффекта.

Чего мне не хватает?

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Вы должны видеть ваше <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>
0 голосов
/ 22 января 2019

Играя с вашей работой в CodePen, я заметил, что белый цвет, который вы получаете на изображении #frame, и внешний фон получаются из цвета фона документа SVG по умолчанию, который не определен и выглядит белым.

Если вы определите стиль background-color для элемента #svg, скажем #f00 (красный), вы увидите как фон, так и изображение #frame красным.

Наконец, я нашелчто настройка opacity изображения #frame на 0.4 дает лучшее сочетание маскированного фона и фона.Вы можете перекрасить изображение #frame, чтобы лучше соответствовать фону на границах прямоугольника изображения.

Я просто добавил background-color (белый) и opacity (0.4) в вашем CodePen какследует:

#svg {
  width: 6144px;
  height: 4608px;
  position: absolute;
  left: -3072px;
  top: -2304px;
  background-color: #fff;
}

#frame {
  opacity: .4;
}
...