Как изменить заливку круга на основе цвета фона с помощью javascript?

В основном я хочу, чтобы круг менял цвет, когда ползунок перемещает его по флагу: зеленый на белом фоне, белый на зеленом прямоугольнике.

function move() {
  let slider = document.querySelector("#slider");
  let circle = document.querySelector("#player");
  circle.setAttribute("cx", slider.value);
}
<h1>Flag</h1>
<svg width="375" height="375">
  <g id="flag">
    <rect x="0" y="0" width="125" height="375" fill="green"/>
    <rect x="250" y="0" width="125" height="375" fill="green"/>
  </g>
  <circle id="player" cx="187.5" cy="187.5" r="10" fill="green" />
</svg><br>
<input id="slider" type="range" min="0" max="375" value="150" style="width:370px" oninput="move()">

Свободный перевод вопроса how to change circle fill based on background color with javascript? от участника @thegoldmolar.


Ответы (2 шт):

Автор решения: Alexandr_TT

Нет простого способа сделать это с помощью SVG-фильтров или режимов наложения и т.д. Вам, вероятно, придется просто переключить цвет самостоятельно в зависимости от позиции круга. См. ниже.

function move() {
  let slider = document.querySelector("#slider");
  let circle = document.querySelector("#player");
  circle.setAttribute("cx", slider.value);
  circle.setAttribute('fill', (slider.value >= 125 && slider.value < 250) ? 'green' : 'white');
}
<h1>Flag</h1>
<svg width="375" height="375">
  <g id="flag">
    <rect x="0" y="0" width="125" height="375" fill="green"/>
    <rect x="250" y="0" width="125" height="375" fill="green"/>
  </g>
  <circle id="player" cx="187.5" cy="187.5" r="10" fill="green"/>
</svg><br>
<input id="slider" type="range" min="0" max="375" value="150" style="width:370px" oninput="move()">

Свободный перевод ответа от участника @Paul LeBeau.

→ Ссылка
Автор решения: UModeL

Для такой простой задачи можно использовать SVG-фильтр, а конкретно - <feComposite> со значением xor. Чтобы всё сработало, нужно понимать алгоритм действия фильтров, а он, примерно, таков:

  • В памяти вычисляются слои с фигурами, заливками и т.п.;
  • Слои растрируются с помощью <feImage>;
  • К растрированным слоям применяется нужный фильтр;
  • Итог рендерится (выводится на экран).

function move() {
  let slider = document.querySelector("#slider");
  let circle = document.querySelector("#player");
  circle.setAttribute("cx", slider.value);
}
<h1>Flag</h1>
<svg width="375" height="375" viewBox="-37.5 -37.5 375 375">
  <defs>
    <g id="flag">
      <rect x="0" y="0" width="125" height="375" fill="green" />
      <rect x="250" y="0" width="125" height="375" fill="green" />
    </g>
    <circle id="player" cx="187.5" cy="187.5" r="10" fill="green" />
    <filter id="imageXor">
      <feImage xlink:href="#flag" result="lay1" />
      <feImage xlink:href="#player" result="lay2" />
      <feComposite operator="xor" in="lay1" in2="lay2" />
    </filter>
  </defs>
  <use xlink:href="#flag" filter="url(#imageXor)"></use>
</svg>
<br>
<input id="slider" type="range" min="0" max="375" value="187.5" style="width:370px" oninput="move()">

Таким образом, необходимо помнить, что очень многие фильтры работают именно с растром. Часто про это забывают и пытаются напрямую использовать векторный (читай, параметрический) объект с растровым (в случае с картинками и градиентами) или таким же вектором.

→ Ссылка