PC/タッチデバイス両対応の回転操作のサンプル
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<title>要素の回転操作サンプル</title>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<style>
.wheel {
position: absolute;
border: 3px #fff double;
border-radius: 50%;
cursor: pointer;
background-image: repeating-conic-gradient(#fffe, #888e 60deg, #fffe 180deg);
user-select: none;
box-sizing: border-box;
}
</style>
</head>
<body>
<script>
'use strict';
{
// タッチデバイスか否かでイベント名振り分け
const isTouchDevice = window.ontouchstart !== undefined;
const ev = {
down: isTouchDevice ? 'touchstart' : 'mousedown',
move: isTouchDevice ? 'touchmove' : 'mousemove',
up : isTouchDevice ? 'touchend' : 'mouseup',
};
// 操作時の各値保持用オブジェクト
const p = {};
// 要素生成
const body = document.querySelector('body');
for(let i = 0; i < 30; i++) {
body.insertAdjacentHTML('beforeend', "<div class='wheel'></div>");
const e = body.lastChild;
e.style.width = e.style.height = `${Math.floor(50 + Math.random() * 150)}px`;
e.style.left = `${Math.random() * (window.innerWidth * 1.5 - e.clientWidth)}px`;
e.style.top = `${Math.random() * (window.innerHeight * 1.5 - e.clientHeight)}px`;
e.style.backgroundColor = `hsl(${Math.random() * 360}deg, 40%, 50%)`;
// タッチデバイスで操作時のスクロール抑制
if(isTouchDevice) {
e.addEventListener('touchmove', e => e.preventDefault());
}
}
// マウスボタンorタッチディスプレイ押下
window.addEventListener(ev.down, e => {
// 対象要素でなければreturn
if(!e.target.classList.contains('wheel')) {
return;
}
p.target = e.target;
const e_ = isTouchDevice ? e.touches[0] : e;
// 押下開始時の要素の角度
p.startDeg = + p.target.style.transform.replace(/[^\d.-]/g, '');
// 要素の中心座標
p.cx = p.target.offsetLeft + p.target.offsetWidth / 2;
p.cy = p.target.offsetTop + p.target.offsetHeight / 2;
// 中心座標に対するクリック位置の角度
// (0時位置を0°、6時位置へ向かって時計回りに ~+180°、反時計回りに ~-180°)
p.clickDeg = Math.atan2(e_.pageX - p.cx, p.cy - e_.pageY) * 180 / Math.PI;
});
// 回転操作
window.addEventListener(ev.move, e => {
if(!p.target) {
return;
}
const e_ = isTouchDevice ? e.touches[0] : e;
// 要素の元の角度と初期押下位置角度を加味した操作後の角度 (0~360°)
const d = (Math.atan2(e_.pageX - p.cx, p.cy - e_.pageY) * 180 / Math.PI
+ p.startDeg - p.clickDeg + 360) % 360;
// 要素に角度を反映
p.target.style.transform = `rotate(${d}deg)`;
});
// 開放
window.addEventListener(ev.up, () => delete p.target);
}
</script>
</body>
これまで回転操作系のUIをいくつか作成していますが、回転操作で行なっている処理は設定の細かな差こそありますが基本はどれもだいたい同じです。