5

Я хочу сделать исключение фигур. Вот здесь я использую фильтр:

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

А вот здесь без фильтра:

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

Как видно, при применении фильтра, фигуры съезжают. Почему это происходит и как исправить?

Методом тыка я обнаружил, что дело в feImage. А также смещение зависит от количества элементов и от их расположения. Сделал, чтобы позиция элементов изменялась от положения мыши:

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) =&gt; c + (this.state.position.x * 0.002 * c * (i % 2 ? 1 : -1));

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

}

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

render() {
  return (
  &lt;div className = "App" &gt;
    &lt;svg onMouseMove={this.onMouseMove} ref = {elem =&gt; this.svg = elem} xmlns = "http://www.w3.org/2000/svg" width = {this.size} height = {this.size} viewBox={`0 0 ${this.size} ${this.size}`}&gt;

      &lt;defs&gt;
        &lt;filter id="myFilter1"&gt;
         &lt;feImage href="#1" result="1"/&gt;
         &lt;feImage href="#2" result="2"/&gt;
         &lt;feComposite in ="1" in2="2" operator="xor"/&gt;
        &lt;/filter&gt;

      &lt;/defs&gt;

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

} }

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 фигур?? Или как исправить, без костылей, feImage?

Igor
  • 1,211

1 Answers1

3

Все оказалось довольно просто. <feImage> по умолчанию имеет следующие атрибуты:

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

И именно из-за этого и смещается изображение.

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

Нужно выставить следующие атрибуты и все будет работать корректно:

x="0" y="0" width="100%" height="100%"

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

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) =&gt; c + (this.state.position.x * 0.002 * c * (i % 2 ? 1 : -1));

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

}

componentDidMount() {
  this.svg.addEventListener('mousemove', e =&gt; {
    const position = {
      x: e.pageX,
      y: e.pageY,
    };
    this.setState({position})
  });
}

render() {
  return (
  &lt;div className = "App" &gt;
    &lt;svg ref = {elem =&gt; this.svg = elem} xmlns = "http://www.w3.org/2000/svg" width = {this.size} height = {this.size} viewBox={`0 0 ${this.size} ${this.size}`}&gt;

      &lt;defs&gt;
        &lt;filter id="myFilter1"&gt;
         &lt;feImage x="0" y="0" width="100%" height="100%"  href="#1" result="1"/&gt;
         &lt;feImage x="0" y="0" width="100%" height="100%"  href="#2" result="2"/&gt;
         &lt;feComposite in ="1" in2="2" operator="xor"/&gt;
        &lt;/filter&gt;

        &lt;filter id="myFilter2"&gt;
          &lt;feImage x="0" y="0" width="100%" height="100%"  href="#4" result="1"/&gt;
          &lt;feImage x="0" y="0" width="100%" height="100%"  href="#3" result="2"/&gt;
          &lt;feComposite in="1" operator="in"/&gt;
        &lt;/filter&gt; 
      &lt;/defs&gt;

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

} }

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>

Спасибо большое Paul LeBeau

Этот же вопрос на англоязычном stackoverflow

Igor
  • 1,211