Я реализовал аналоговые часы с минутной стрелкой и часовой стрелкой. Цель этого упражнения - при перетаскивании повернуть стрелки часов на соответствующий угол метки часа; то есть, когда мышь двигается вокруг.
Некоторые примеры, которые я нашел, используют JQuery и другие библиотеки. Я пытаюсь сделать это только с ванилью js. Я посмотрел некоторые примеры, расположенные здесь, в stackoverflow, а также в других местах и изучая логи c и пытаясь понять документацию atan2 , это насколько я смог получить:
//rotate handler
const rotateHand = (event)=>{
const elem = event.target;
elem.style.cursor = "grabbing";
let rotating = true;
const clock = document.querySelector(".gamut__timePicker__clock");
const radius = 252 / 2;
const rotateHandler = (e)=>{
const radians = Math.atan2(e.pageX - radius, e.pageY - radius);
let rotateDegrees = (radians * (180 / Math.PI) * -1) -180;
if (rotating) {
elem.style.transform = `rotate(${rotateDegrees}deg)`;
}
};
document.addEventListener("mousemove", rotateHandler);
const cancelRotate = (event)=>{
elem.style.cursor = "grab";
rotating = !rotating;
document.removeEventListener("mousemove", rotateHandler);
document.removeEventListener("mouseup", cancelRotate);
};
document.addEventListener("mouseup", cancelRotate);
};
document.querySelector(".gamut__timePicker__minHand").addEventListener("mousedown", rotateHand);
document.querySelector(".gamut__timePicker__hrHand").addEventListener("mousedown", rotateHand);
@import url("https://fonts.googleapis.com/css?family=Roboto&display=swap");
.gamut__timePicker {
border: 1px solid gray;
width: 300px;
height: 335px;
display: -webkit-box;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
font-family: 'Roboto', sans-serif;
}
.gamut__timePicker__meridiamSelector {
height: 20px;
display: -webkit-box;
display: flex;
-webkit-box-pack: justify;
justify-content: space-between;
}
.gamut__timePicker__meridiamSelector--meridiam {
text-align: center;
-webkit-box-flex: 1;
flex-grow: 1;
border: 1px solid gray;
font-size: 0.875rem;
line-height: 20px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.gamut__timePicker__meridiamSelector--meridiam:hover {
background: #efefef;
}
.gamut__timePicker__meridiamSelector--selected {
background: #607d8b !important;
color: white;
}
.gamut__timePicker__clockWrapper {
-webkit-box-flex: 1;
flex-grow: 1;
align-self: stretch;
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
-webkit-box-align: center;
align-items: center;
position: relative;
}
.gamut__timePicker__clock {
border-radius: 50%;
width: 250px;
height: 250px;
border: 1px solid gray;
margin: 0 auto;
position: relative;
}
.gamut__timePicker__clock--minPointer {
width: 1px;
height: 10px;
background-color: #424242;
position: absolute;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.gamut__timePicker__clock--hrPointer {
position: absolute;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.gamut__timePicker__clock--hrPointer::before {
top: -4px;
left: -7px;
content: "\25BC";
position: absolute;
}
.gamut__timePicker__labels {
border-radius: 50%;
width: 210px;
height: 210px;
margin: 0 auto;
position: absolute;
pointer-events: none;
}
.gamut__timePicker__labels--hourLabel {
position: absolute;
-webkit-transform: translate(-50%, -45%);
transform: translate(-50%, -45%);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.gamut__timePicker__minHand {
width: 20px;
height: 100px;
background-image: url(https://res.cloudinary.com/dvzwvxhev/image/upload/v1578025454/clock_min.svg);
background-size: 100% 100%;
position: absolute;
-webkit-transform-origin: 49% 92%;
transform-origin: 49% 92%;
top: 15%;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
cursor: -webkit-grab;
cursor: grab;
}
.gamut__timePicker__hrHand {
width: 40px;
height: 70px;
background-image: url(https://res.cloudinary.com/dvzwvxhev/image/upload/v1578274567/clock_hr.svg);
background-size: 100% 100%;
position: absolute;
-webkit-transform-origin: 50% 93%;
transform-origin: 50% 93%;
top: 25%;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
cursor: -webkit-grab;
cursor: grab;
}
.gamut__timePicker__time {
height: 34px;
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
}
.gamut__timePicker__time > input {
width: 35px;
border: none;
border-bottom: 1px solid #ababab;
text-align: center;
font-size: 1.125rem;
outline: none;
height: 25px;
padding: 0;
}
.gamut__timePicker__time > input:focus {
border-bottom: 2px solid #607d8b;
}
.gamut__timePicker__time--separator {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-size: 1.125rem;
height: 25px;
}
.gamut__timePicker__done {
background: #efefef;
text-align: center;
border-top: 1px solid gray;
cursor: pointer;
}
<div class="gamut__timePicker" style="top: 31px; left: 8px; z-index: 9;"><div class="gamut__timePicker__meridiamSelector"><div data-meridiam-value="am" class="gamut__timePicker__meridiamSelector--meridiam gamut__timePicker__meridiamSelector--selected">AM</div><div data-meridiam-value="pm" class="gamut__timePicker__meridiamSelector--meridiam">PM</div></div><div class="gamut__timePicker__clockWrapper"><div class="gamut__timePicker__clock"><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(90deg); left: 100%; top: 50%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(96deg); left: 99.7261%; top: 55.2264%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(102deg); left: 98.9074%; top: 60.3956%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(108deg); left: 97.5528%; top: 65.4508%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(114deg); left: 95.6773%; top: 70.3368%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(120deg); left: 93.3013%; top: 75%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(126deg); left: 90.4508%; top: 79.3893%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(132deg); left: 87.1572%; top: 83.4565%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(138deg); left: 83.4565%; top: 87.1572%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(144deg); left: 79.3893%; top: 90.4508%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(150deg); left: 75%; top: 93.3013%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(156deg); left: 70.3368%; top: 95.6773%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(162deg); left: 65.4508%; top: 97.5528%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(168deg); left: 60.3956%; top: 98.9074%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(174deg); left: 55.2264%; top: 99.7261%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(180deg); left: 50%; top: 100%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(186deg); left: 44.7736%; top: 99.7261%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(192deg); left: 39.6044%; top: 98.9074%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(198deg); left: 34.5492%; top: 97.5528%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(204deg); left: 29.6632%; top: 95.6773%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(210deg); left: 25%; top: 93.3013%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(216deg); left: 20.6107%; top: 90.4508%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(222deg); left: 16.5435%; top: 87.1572%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(228deg); left: 12.8428%; top: 83.4565%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(234deg); left: 9.54915%; top: 79.3893%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(240deg); left: 6.69873%; top: 75%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(246deg); left: 4.32273%; top: 70.3368%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(252deg); left: 2.44717%; top: 65.4508%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(258deg); left: 1.09262%; top: 60.3956%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(264deg); left: 0.273905%; top: 55.2264%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(270deg); left: 0%; top: 50%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(276deg); left: 0.273905%; top: 44.7736%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(282deg); left: 1.09262%; top: 39.6044%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(288deg); left: 2.44717%; top: 34.5492%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(294deg); left: 4.32273%; top: 29.6632%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(300deg); left: 6.69873%; top: 25%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(306deg); left: 9.54915%; top: 20.6107%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(312deg); left: 12.8428%; top: 16.5435%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(318deg); left: 16.5435%; top: 12.8428%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(324deg); left: 20.6107%; top: 9.54915%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(330deg); left: 25%; top: 6.69873%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(336deg); left: 29.6632%; top: 4.32273%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(342deg); left: 34.5492%; top: 2.44717%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(348deg); left: 39.6044%; top: 1.09262%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(354deg); left: 44.7736%; top: 0.273905%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(360deg); left: 50%; top: 0%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(366deg); left: 55.2264%; top: 0.273905%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(372deg); left: 60.3956%; top: 1.09262%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(378deg); left: 65.4508%; top: 2.44717%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(384deg); left: 70.3368%; top: 4.32273%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(390deg); left: 75%; top: 6.69873%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(396deg); left: 79.3893%; top: 9.54915%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(402deg); left: 83.4565%; top: 12.8428%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(408deg); left: 87.1572%; top: 16.5435%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(414deg); left: 90.4508%; top: 20.6107%;"></div><div class="gamut__timePicker__clock--hrPointer" style="transform: rotate(420deg); left: 93.3013%; top: 25%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(426deg); left: 95.6773%; top: 29.6632%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(432deg); left: 97.5528%; top: 34.5492%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(438deg); left: 98.9074%; top: 39.6044%;"></div><div class="gamut__timePicker__clock--minPointer" style="transform: rotate(444deg); left: 99.7261%; top: 44.7736%;"></div></div><div class="gamut__timePicker__labels"><span class="gamut__timePicker__labels--hourLabel" style="left: 100%; top: 50%;">3</span><span class="gamut__timePicker__labels--hourLabel" style="left: 93.3013%; top: 75%;">4</span><span class="gamut__timePicker__labels--hourLabel" style="left: 75%; top: 93.3013%;">5</span><span class="gamut__timePicker__labels--hourLabel" style="left: 50%; top: 100%;">6</span><span class="gamut__timePicker__labels--hourLabel" style="left: 25%; top: 93.3013%;">7</span><span class="gamut__timePicker__labels--hourLabel" style="left: 6.69873%; top: 75%;">8</span><span class="gamut__timePicker__labels--hourLabel" style="left: 0%; top: 50%;">9</span><span class="gamut__timePicker__labels--hourLabel" style="left: 6.69873%; top: 25%;">10</span><span class="gamut__timePicker__labels--hourLabel" style="left: 25%; top: 6.69873%;">11</span><span class="gamut__timePicker__labels--hourLabel" style="left: 50%; top: 0%;">12</span><span class="gamut__timePicker__labels--hourLabel" style="left: 75%; top: 6.69873%;">1</span><span class="gamut__timePicker__labels--hourLabel" style="left: 93.3013%; top: 25%;">2</span></div><div class="gamut__timePicker__minHand" style="transform: rotate(0deg);"></div><div class="gamut__timePicker__hrHand" style="transform: rotate(240deg);"></div></div><div class="gamut__timePicker__time"><input type="text" data-time-param="hour"><span class="gamut__timePicker__time--separator">:</span><input type="text" data-time-param="minute"></div><div class="gamut__timePicker__done">DONE</div></div>
Как видно из приведенного фрагмента, стрелка часов вращается, но угол не точный, есть небольшое расхождение. Цель состоит в том, чтобы стрелка указывала на правильный час / минуту, когда указатель мыши находится под углом, который соответствует соответствующему часу.