Как исключить фигуры в svg или холсте?

Прости мой английский

Я использую svg с фильтрами, но столкнулся со следующей проблемой.

Это база для svg. Ожидаемый результат:

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
  <g>
    <circle id="2" cx="50" cy="50" r="50"/>
    <g id="1">
      <rect x="0" y="0" width="50" height="50" fill="#ccc"/>
      <rect x="50" y="50" width="50" height="50" fill="#ccc"/>
    </g>
  </g>
</svg>

А вот с фильтром feComposite:

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">

  <defs>
    <filter id="myFilter1">
      <feImage href="#1" result="1"/>
      <feImage href="#2" result="2"/>
      <feComposite in="1" in2="2" operator="xor"/>
    </filter>
  </defs>
  
  <g filter="url(#myFilter1)">
    <circle id="2" cx="50" cy="50" r="50"/>
    <g id="1">
      <rect x="0" y="0" width="50" height="50" fill="#ccc"/>
      <rect x="50" y="50" width="50" height="50" fill="#ccc"/>
    </g>
  </g>
</svg>

Как видите, изображение сдвинуто. Если вы проверите код, блоки не будут соответствовать видимому изображению:

enter image description here

Вот с добавлением интерактивности:

const value = (max = 100000000, min = 0) => Math.round(Math.random() * (max - min)) + min;

const createCircle = (size) => {
  const r = value(10, 3);
  const cx = value(size - r - 10, r + 10);
  const cy = value(size - r - 10, r + 10);
  return {
    r,
    cx,
    cy
  }
};

const createCircles = (counts, size) => Array(counts).fill().map(() => createCircle(size));


class App extends React.Component {

    constructor(props) {
      super(props);

      this.state = {
        position: {
          x: 0,
          y: 0,
        }
      };

      this.size = 300;

      this.circlesData = createCircles(100, this.size);

      const getCoords = (c, i) => c + (this.state.position.x * 0.002 * c * (i % 2 ? 1 : -1));

      this.circles = () => this.circlesData.map((item, i) => <circle key = {`circles_12dew1_${i}`} cx={getCoords(item.cx, i)} cy={getCoords(item.cy, i)} r={item.r}/>);

    }

    onMouseMove = e => {
      const position = {
        x: e.pageX,
        y: e.pageY,
      };
      this.setState({position});
    }

    render() {
      return (
      <div className = "App" >
        <svg onMouseMove={this.onMouseMove} ref = {elem => this.svg = elem} xmlns = "http://www.w3.org/2000/svg" width = {this.size} height = {this.size} viewBox={`0 0 ${this.size} ${this.size}`}>
          
          <defs>
            <filter id="myFilter1">
             <feImage href="#1" result="1"/>
             <feImage href="#2" result="2"/>
             <feComposite in ="1" in2="2" operator="xor"/>
            </filter>
 
          </defs>

          <g id = "3" filter = "url(#myFilter1)" >
            <circle id = "2" cx={this.size / 2 + 100} cy={this.size / 2 + 100} r={this.size / 3}/>
            <g id="1"> {this.circles()} </g> 
          </g>
        </svg> 
      </div>
    );
  }
}


ReactDOM.render( < App / > , document.getElementById('root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Как видите, большой круг движется, но этого не должно быть.

Как исправить?

Или как сделать исключение фигур как в интерактивном примере без svg? Например, используя холст, спасибо!

2
0
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поведение при размещении изображения, которое вы видите, связано с тем, что элементы <feImage> позиционируются с использованием области фильтра или области примитива фильтра.

https://www.w3.org/TR/SVG11/single-page.html#filters-feImageElement

По умолчанию область фильтра - это область, которая со всех сторон на 10% больше исходного объекта.

x = "- 10%" y = "- 10%" width = "120%" height = "120%"

Это сделано для обслуживания примитивов фильтра, таких как <feGuassianBlur>, которые выходят за пределы исходного размера и в противном случае были бы обрезаны.

Чтобы изображения располагались так, как вы хотите, измените область фильтра или область примитива фильтра, чтобы она была того же размера, что и исходный объект. Например:

<feImage href="#1" x="0" y="0" width="100%" height="100%" result="1"/>

Обновленная демоверсия:

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">

  <defs>
    <filter id="myFilter1">
      <feImage href="#1" x="0" y="0" width="100%" height="100%" result="1"/>
      <feImage href="#2" x="0" y="0" width="100%" height="100%" result="2"/>
      <feComposite in="1" in2="2" operator="xor"/>
    </filter>
  </defs>
  
  <g filter="url(#myFilter1)">
    <circle id="2" cx="50" cy="50" r="50"/>
    <g id="1">
      <rect x="0" y="0" width="50" height="50" fill="#ccc"/>
      <rect x="50" y="50" width="50" height="50" fill="#ccc"/>
    </g>
  </g>
</svg>

Другие вопросы по теме