Вращение объектов средствами 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>

→ Ссылка