Вращение объектов средствами CSS. Непонятно как работает направление вращения
Если посмотреть на картинку, мы видим, что кубик в начальном положении (с одной точкой) двигается за курсором. При движении курсора влево-вправо, кубик вращается по оси Y. Собственно, этого я и пытаюсь добиться от кубика.
Но когда кубик переворачивается к стороне с пятью точками, при движнии влево-вправо кубик вращается по оси Z. Потом перевернув кубик на три точки, сначала он вращается в нужном направлении, а в конце gif-ки вращается в обратном направлении.
Как сделать направление вращения предсказуемым? Например, чтобы кубик всегда вращался как в изначальном положении.
let [action,angleX,angleY,map,touchX,touchY] = ["idle",0,0,new WeakMap,0,0];
const dice = [first, second, third, fourth, fifth, sixth];
dice.forEach(el =>
map.set(el, [...document.styleSheets[0].rules]
.find(rule=>`#${el.id}`==rule.selectorText).style.transform
));
const ontouchstart=e => {
if(e.touches.length==1){
[touchX,touchY]=[e.touches[0].pageX,e.touches[0].pageY];}
else {
[touchX,touchY]=[0,0];
}
};
const ontouchend =()=>[touchX,touchY]=[0,0];
const ontouchmove=e=>{
if(touchX&&touchY){
[angleX,angleY]=[
angleX-e.touches[0].pageY+touchY,
angleY+e.touches[0].pageX-touchX]
.map((a) => (a > 360 ? a - 360 : a < 0 ? a + 360 : a));
dice.map(el => el.style.transform =
`rotateX(${angleX}deg) rotateY(${angleY}deg) ` + map.get(el));
[touchX,touchY]=[e.touches[0].pageX,e.touches[0].pageY];
}
}
const onmousedown = () => action = "rotate";
const onmouseup = () => action = "idle";
const onmousemove = e => {
if (action == "rotate") {
[angleX, angleY] = [ angleX - e.movementY, angleY + e.movementX ]
.map((a) => (a > 360 ? a - 360 : a < 0 ? a + 360 : a));
dice.map(el => el.style.transform =
`rotateX(${angleX}deg) rotateY(${angleY}deg) ` + map.get(el));
}
};
Object.assign(document.body, {onmousedown, onmousemove,ontouchstart,ontouchmove});
Object.assign(window, {onmouseup,ontouchend});
* {
box-sizing: border-box;
}
body,
div {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
perspective: 100vh;
transform-style: preserve-3d;
background-color: #333;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.face {
background-color: #cccc;
border-radius: 0;
border: 3px solid #000;
padding: 3vh;
margin: 3px;
display: flex;
width: 25vh;
height: 25vh;
position: fixed;
left: calc(50vw - 12.5vh);
top: calc(50vh - 12.5vh);
pointer-events: none;
/* backdrop-filter: blur(2px); */
}
.column {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.pip {
background-color: #000;
width: 4vh;
height: 4vh;
display: inline-block;
border-radius: 4rem;
}
#first {
justify-content: center;
align-items: center;
transform: translateZ(12.5vh);
}
#second {
justify-content: space-between;
transform: rotateX(90deg) translateZ(-12.5vh);
}
#second .pip:nth-of-type(2) {
align-self: flex-end;
}
#third {
justify-content: space-between;
align-items: center;
transform: rotateY(90deg) translateZ(-12.5vh);
}
#third .pip:nth-of-type(1) {
align-self: flex-start;
}
#third .pip:nth-of-type(3) {
align-self: flex-end;
}
#fourth {
justify-content: space-between;
transform: rotateY(180deg) translateZ(12.5vh);
}
#fifth {
justify-content: space-between;
justify-content: space-between;
transform: rotateX(90deg) translateZ(12.5vh);
}
#fifth .column:nth-of-type(2) {
align-self: center;
}
#sixth {
justify-content: space-between;
transform: rotateY(90deg) translateZ(12.5vh);
}
<div id="first" class="face"><span class="pip"></span></div>
<div id="second" class="face">
<span class="pip"></span>
<span class="pip"></span>
</div>
<div id="third" class="face">
<span class="pip"></span>
<span class="pip"></span>
<span class="pip"></span>
</div>
<div id="fourth" class="face">
<div class="column">
<span class="pip"></span> <span class="pip"></span>
</div>
<div class="column">
<span class="pip"></span> <span class="pip"></span>
</div>
</div>
<div id="fifth" class="face">
<div class="column">
<span class="pip"></span> <span class="pip"></span>
</div>
<div class="column"><span class="pip"></span></div>
<div class="column">
<span class="pip"></span> <span class="pip"></span>
</div>
</div>
<div id="sixth" class="face">
<div class="column">
<span class="pip"></span> <span class="pip"></span>
<span class="pip"></span>
</div>
<div class="column">
<span class="pip"></span> <span class="pip"></span>
<span class="pip"></span>
</div>
</div>
Ответы (1 шт):
Автор решения: DiD
→ Ссылка
Решение не идеальное, но вроде работает. Основано на сохранении матрицы вращения.
let [action,touchX,touchY] = [0,0,0];
const {sign, hypot} = Math;
const [onmousedown, onmouseup, ontouchstart] = [() => action = 1, () => [action,touchX,touchY] = [0,0,0],
(e) => [action,touchX,touchY] = e.touches.length == 1 ? [1,e.touches[0].pageX,e.touches[0].pageY] : [0,0,0] ];
const rotate = (x,y) => {
const t = getComputedStyle(surface).transform;
surface.style.transform = `rotate3d(${sign(-y)},${sign(x)},0,${hypot(x,y)}deg) `+('none'==t?'':t);
}
const onmousemove = e => {
if (action) rotate(e.movementX,e.movementY);
};
const ontouchmove = e => {
if(action){
rotate(e.touches[0].pageX-touchX,e.touches[0].pageY-touchY);
[touchX,touchY]=[e.touches[0].pageX,e.touches[0].pageY];
}
}
Object.assign(window, {onmousedown, onmousemove, onmouseup, ontouchstart,
ontouchend: onmouseup, ontouchcancel: onmouseup, ontouchmove});
* { box-sizing: border-box; }
body, div, p, i { margin: 0; padding: 0; }
body{ background-color: #333; }
body, #surface { width: 100vw; height: 100vh; perspective: 50vh; transform-style: preserve-3d; }
#surface{ perspective: 1000vh; }
.face {
background-color: #cccc; border: 3px solid #000;
padding: 3vh; margin: 3px; width: 25vh; height: 25vh;
left: calc(50vw - 12.5vh); top: calc(50vh - 12.5vh);
display: flex; position: fixed;
}
#surface, .face, p, i { pointer-events: none; }
p { display: flex; flex-direction: column; justify-content: space-between; }
i { background-color: #000; width: 4vh; height: 4vh; display: inline-block; border-radius: 4rem; }
#first { justify-content: center; align-items: center; transform: translateZ(12.5vh);}
#second { justify-content: space-between; transform: rotateX(90deg) translateZ(-12.5vh); }
#second i:nth-of-type(2) { align-self: flex-end;}
#third { justify-content: space-between; align-items: center; transform: rotateY(90deg) translateZ(-12.5vh); }
#third i:nth-of-type(1) { align-self: flex-start;}
#third i:nth-of-type(3) { align-self: flex-end;}
#fourth { justify-content: space-between; transform: rotateY(180deg) translateZ(12.5vh); }
#fifth { justify-content: space-between; transform: rotateX(90deg) translateZ(12.5vh);}
#fifth p:nth-of-type(2) { align-self: center; }
#sixth { justify-content: space-between; transform: rotateY(90deg) translateZ(12.5vh); }
<div id="surface">
<div id="first" class="face"><i></i></div>
<div id="second" class="face"><i></i><i></i></div>
<div id="third" class="face"><i></i><i></i><i></i></div>
<div id="fourth" class="face"><p><i></i><i></i></p><p><i></i><i></i></p></div>
<div id="fifth" class="face"><p><i></i><i></i></p><p><i></i></p><p><i></i><i></i></p></div>
<div id="sixth" class="face"><p><i></i><i></i><i></i></p><p><i></i><i></i><i></i></p></div>
</div>
