js перемещение стрелки(угол) в полукруге на координату щелчка

Есть подобие спидометра, полукруг(1-1000 значения) со стрелкой. Она стоит в некотором положении (.png). При щелчке на полукруге того как бы спидометра стрелка должна встать в ту позицию и значение запоминается.

Как это нормально сделать?

Пытаюсь вычислить угол https://m.fxyz.ru/2/70/71/451/ https://www.cyberforum.ru/javascript/thread914317.html, чтоб перемещать с css rotate но и того не выходит.

const touchSupport      = 'ontouchstart' in document.documentElement;
const slider            = document.getElementsByClassName('visualizer')[0];
const handle            = document.getElementsByClassName('visualizer__arrow')[0];
//const sliderInput         = document.getElementsByClassName('slider__value')[0];
const sliderWidth       = parseInt(getComputedStyle(slider).width, 10);
const sliderHeight      = parseInt(getComputedStyle(slider).height, 10);
const sliderOffsetLeft  = slider.getBoundingClientRect().left;
const sliderOffsetTop   = slider.offsetTop;

let isMouseDown = false;
let mouseX;
let mouseY;
let handlePosition;
let sliderValue;

function getMouseX(ev) {
    return sliderWidth / 2 - (ev.pageX - slider.offsetLeft);
}
function getMouseY(ev) {
    return ev.pageY - slider.offsetTop;
}

function mouseToPointsComparer() {
    //handle.style.transform = "rotate(" + Math.abs(260 + mouseX) + "deg)";
    handle.style.transform = "rotate(" + Math.round(Math.cos(mouseY/(125 - mouseX)) * 180 / Math.PI) + "deg)";

    console.log("+_+"+Math.round(Math.cos((mouseY/(125 - mouseX)) * 180 / Math.PI)))
    //console.log(  Math.sin(mouseY/(125 - mouseX)) * 180 / Math.PI );
//console.log(Math.atan2(mouseY, (125 - mouseX)) +"+++"+(mouseY/(125 - mouseX))  +"_"+Math.tan(mouseY/(125 - mouseX) ) );

    //slider.setAttribute('data-value', sliderValue);
    //sliderInput.value = sliderValue;
}

function moveHandle(ev) {
    mouseX = getMouseX(ev);
    mouseY = getMouseY(ev);
}

function cancelPageTouchmove(ev) {
    ev.preventDefault();
}

['mousedown', 'touchstart'].forEach(item => {
    slider.addEventListener(item, () => {
        isMouseDown = true;
        if (touchSupport) {
            document.addEventListener('touchmove', cancelPageTouchmove, {passive: false});
        }
        else {
            document.onselectstart = () => {return false;};
        }
    });
});

['mousemove', 'touchmove'].forEach(item => {
    document.addEventListener(item, moveHandle, false);
});

['mouseup', 'touchend'].forEach(item => {
    document.addEventListener(item, () => {
        if (isMouseDown) {
            isMouseDown = false;
            if (touchSupport) {
                document.removeEventListener('touchmove', cancelPageTouchmove, false);
            }
            else {
                document.onselectstart = () => {return true;};
            }
            mouseToPointsComparer();
        }
    });
});

['click', 'mousedown', 'touchstart'].forEach(item => {
    slider.addEventListener(item, ev => {
        mouseX = getMouseX(ev);
        mouseY = getMouseY(ev);
        mouseToPointsComparer();
    });
});

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

Автор решения: Stranger in the Q

Как-то так:

var deg = Math.atan2(
    mouseY,
    mouseX
) + Math.PI;

+PI - это разворот на 180 градусов, необходим чтобы компенсировать то, что ось Y в математике направлен вверх, а на канве - вниз

const touchSupport = 'ontouchstart' in document.documentElement;
const slider = document.getElementsByClassName('visualizer')[0];
const handle = document.getElementsByClassName('visualizer__arrow')[0];
let ic = document.querySelector('.visualizer__innerCircle')
const sliderWidth = parseInt(getComputedStyle(slider).width, 10);
const sliderHeight = parseInt(getComputedStyle(slider).height, 10);
const sliderOffsetLeft = slider.getBoundingClientRect().left;
const sliderOffsetTop = slider.offsetTop;

let isMouseDown = false;
let mouseX;
let mouseY;
let handlePosition;
let sliderValue;

function getMouseX(ev) {
  let r = ic.getBoundingClientRect();
  return ev.clientX - r.x - r.width/2;
}

function getMouseY(ev) {
  let r = ic.getBoundingClientRect();
  return ev.clientY - r.y - r.height/2;
}

function mouseToPointsComparer() {
  var deg = Math.atan2(
    mouseY,
    mouseX
  ) + Math.PI;

  handle.style.transform = "rotate(" + deg + "rad)";
}

function moveHandle(ev) {
  mouseX = getMouseX(ev);
  mouseY = getMouseY(ev);
}

function cancelPageTouchmove(ev) {
  ev.preventDefault();
}

['mousedown', 'touchstart'].forEach(item => {
  slider.addEventListener(item, () => {
    isMouseDown = true;
    if (touchSupport) {
      document.addEventListener('touchmove', cancelPageTouchmove, {
        passive: false
      });
    } else {
      document.onselectstart = () => {
        return false;
      };
    }
  });
});

['mousemove', 'touchmove'].forEach(item => {
  document.addEventListener(item, moveHandle, false);
});

['mouseup', 'touchend'].forEach(item => {
  document.addEventListener(item, () => {
    if (isMouseDown) {
      isMouseDown = false;
      if (touchSupport) {
        document.removeEventListener('touchmove', cancelPageTouchmove, false);
      } else {
        document.onselectstart = () => {
          return true;
        };
      }
      mouseToPointsComparer();
    }
  });
});

['click', 'mousedown', 'touchstart'].forEach(item => {
  slider.addEventListener(item, ev => {
    mouseX = getMouseX(ev);
    mouseY = getMouseY(ev);
    mouseToPointsComparer();
  });
});
.visualizer__level {
  -webkit-transform: scale(1.69, 1.7);
  -moz-transform: scale(1.69, 1.7);
  -ms-transform: scale(1.69, 1.7);
  -o-transform: scale(1.69, 1.7);
  transform: scale(1.69, 1.7);
  position: relative;
  top: 0;
  left: 386px;
}

.visualizer__pie {
  -webkit-transition: all 1s;
  -o-transition: all 1s;
  -moz-transition: all 1s;
  transition: all 1s;
  position: absolute;
  width: 150px;
  height: 150px;
  -webkit-border-radius: 75px;
  -moz-border-radius: 75px;
  border-radius: 75px;
  clip: rect(0px, 75px, 150px, 0px);
}

.visualizer__hold {
  position: absolute;
  width: 150px;
  height: 150px;
  -webkit-border-radius: 75px;
  -moz-border-radius: 75px;
  border-radius: 75px;
  clip: rect(0px, 150px, 150px, 75px);
}

.visualizer__pieSlice1 {
  -webkit-transform: rotate(-33deg);
  -moz-transform: rotate(-33deg);
  -ms-transform: rotate(-33deg);
  -o-transform: rotate(-33deg);
  transform: rotate(-33deg);
}

.visualizer__pieSlice1 .visualizer__pie {
  background: #a3cd3b;
  -webkit-transform: rotate(68deg);
  -moz-transform: rotate(68deg);
  -ms-transform: rotate(68deg);
  -o-transform: rotate(68deg);
  transform: rotate(68deg);
}

.visualizer__pieSlice2 {
  -webkit-transform: rotate(39deg);
  -moz-transform: rotate(39deg);
  -ms-transform: rotate(39deg);
  -o-transform: rotate(39deg);
  transform: rotate(39deg);
}

.visualizer__pieSlice2 .visualizer__pie {
  background-color: #fb93d7;
  -webkit-transform: rotate(51deg);
  -moz-transform: rotate(51deg);
  -ms-transform: rotate(51deg);
  -o-transform: rotate(51deg);
  transform: rotate(51deg);
}

.visualizer__pieSlice3 {
  -webkit-transform: rotate(270deg);
  -moz-transform: rotate(270deg);
  -ms-transform: rotate(270deg);
  -o-transform: rotate(270deg);
  transform: rotate(270deg);
}

.visualizer__pieSlice3 .visualizer__pie {
  background-color: #33c815;
  -webkit-transform: rotate(53deg);
  -moz-transform: rotate(53deg);
  -ms-transform: rotate(53deg);
  -o-transform: rotate(53deg);
  transform: rotate(53deg);
}

.visualizer__innerCircle {
  position: absolute;
  width: 85px;
  height: 85px;
  background-color: #fff;
  -webkit-border-radius: 60px;
  -moz-border-radius: 60px;
  border-radius: 60px;
  top: 32px;
  left: 34px;
  color: white;
}

.visualizer__arrow {
  position: absolute;
  bottom: 48px;
  left: 283px;
  width: 57px;
  height: 39px;
  border: 1px solid red;
  border-left: 1px solid blue;
  content: "";
  background-image: url("../images/arrow.png");
  -webkit-transform-origin: -webkit-calc(100% - 8px) -webkit-calc(100% - 8px);
  -moz-transform-origin: -moz-calc(100% - 8px) -moz-calc(100% - 8px);
  -ms-transform-origin: calc(100% - 8px) calc(100% - 8px);
  -o-transform-origin: calc(100% - 8px) calc(100% - 8px);
  transform-origin: 50% 50%;
  -webkit-transition: all 0.5s;
  -o-transition: all 0.5s;
  -moz-transition: all 0.5s;
  transition: all 0.5s;
}
<div class="js-level__visualizer visualizer">
  <div class="visualizer__level">
    <div class="visualizer__pieSlice1 visualizer__hold">
      <div class="visualizer__pie"></div>
    </div>
    <div class="visualizer__pieSlice2 visualizer__hold">
      <div class="visualizer__pie"></div>
    </div>
    <div class="visualizer__pieSlice3 visualizer__hold">
      <div class="visualizer__pie"></div>
    </div>
    <div class="visualizer__innerCircle"></div>
  </div>
  <div class="visualizer__arrow"></div>
</div>

→ Ссылка