CSS расширенный контроль 3D вращения

В настоящее время я пытаюсь выяснить способ поворота объекта вдоль оси относительно пользователя, а не относительно объекта. Я делаю это с помощью Javascript (jQuery) и CSS transform rotate.

Я построил кодовую ручку, чтобы проверить это.

var bX = 0; //bufferX (user scroll input)
var bY = 0; //bufferY (user scroll input)
var cX = 0; //currentX (effective X rotation)
var cY = 0; //currentY (effective Y rotation)

var bBX = 0; // same second rotation properties
var bBY = 0; 
var cBX = 0; 
var cBY = 0; 
var cBZ = 0; // currentZ (effective Z rotation)

var pi = 3.1416

// Fidget animation
function fidget() {
 if (!(bX + bY == 0)) {
  var dX = bX * 0.1; // how much we rotate this frame
  var dY = bY * 0.1; 
    
  cX = (cX + dX) % (2 * pi); // set rotation value
    cY = (cY + dY) % (2 * pi); 
    
  bX -= dX; // update buffer
  bY -= dY;
 }
 $('#one').css('transform', 'rotateX(' + cX + 'rad) rotateY(' + cY + 'rad)');
  window.requestAnimationFrame(fidget);
}

function fidgetB() {
 if (!(bBX + bBY == 0)) {
  var dBX = bBX * 0.1;
  cBX = (cBX + dBX) % (2 * pi);

  var rBY = Math.cos(cBX); //Y ratio
  var rBZ = Math.sin(cBX); //Z ratio

  var dBY = bBY * 0.1; // deltaY 
  var dBZ = bBY * 0.1;
  cBY = (cBY + rBY * dBY) % (2 * pi); // current
  cBZ = (cBZ - rBZ * dBZ) % (2 * pi);

  bBX -= dBX; // buffer
  bBY -= (dBY);
 }
 $('#two').css('transform', 'rotateX(' + cBX + 'rad) rotateY(' + cBY + 'rad) rotateZ(' + cBZ + 'rad)');
  window.requestAnimationFrame(fidgetB);
}

var init = function () {
  
  // Fidget
 requestAnimationFrame(fidget);
  requestAnimationFrame(fidgetB);
  
  // scroll detection script 
!function(window,document){var prefix="",_addEventListener,support;function _addWheelListener(elem,eventName,callback,useCapture){elem[_addEventListener](prefix+eventName,"wheel"==support?callback:function(originalEvent){!originalEvent&&(originalEvent=window.event);var event={originalEvent:originalEvent,target:originalEvent.target||originalEvent.srcElement,type:"wheel",deltaMode:"MozMousePixelScroll"==originalEvent.type?0:1,deltaX:0,deltaY:0,deltaZ:0,preventDefault:function(){originalEvent.preventDefault?originalEvent.preventDefault():originalEvent.returnValue=!1}};return"mousewheel"==support?(event.deltaY=-.025*originalEvent.wheelDelta,originalEvent.wheelDeltaX&&(event.deltaX=-.025*originalEvent.wheelDeltaX)):event.deltaY=originalEvent.deltaY||originalEvent.detail,callback(event)},useCapture||!1)}window.addEventListener?_addEventListener="addEventListener":(_addEventListener="attachEvent",prefix="on"),support="onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll",window.addWheelListener=function(elem,callback,useCapture){_addWheelListener(elem,support,callback,useCapture),"DOMMouseScroll"==support&&_addWheelListener(elem,"MozMousePixelScroll",callback,useCapture)}}(window,document);

 window.addWheelListener(window, function (e) {
  e.preventDefault();
  bY -= e.deltaX / window.innerWidth;
  bX += e.deltaY / window.innerWidth;
    bBY -= e.deltaX / window.innerWidth;
  bBX += e.deltaY / window.innerWidth;
 });
};

jQuery(document).ready(function ($) {
 init();
});
html, html * {
 margin: 0;
 padding: 0;
 -webkit-box-sizing: border-box;
 box-sizing: border-box;
  color:white;
  text-align: center;
  font-size: 6vmin;
  font-family: sans-serif;
}

#intro {
 position:absolute;
 width: 100%;
 height: 100%;
  -webkit-perspective: 200vmax;
 perspective: 200vmax;
 -webkit-perspective-origin: 50% 50%;
 perspective-origin: 50% 50%;
  transform-style: preserve-3d;
}

#one {
  left: 33%;
}

#two {
  left: 66%;
}

.square {
  background-color: black;
  width: 50vmin;
  height: 50vmin;
  line-height: 50vmin;
  top: 50%;
  margin-top: -25vmin;
  margin-left: -25vmin;
  
 transform-style: preserve-3d;
 
 position: absolute;
 -webkit-backface-visibility: visible;
 backface-visibility: visible;
 -webkit-transform-origin: 50% 37%;
 transform-origin: 50% 37%;
 /* -webkit-animation: rotate 25s linear infinite;
 animation: rotate 25s linear infinite; */
 pointer-events: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="intro">
  <div id="one" class="square">Object</div>
  <div id="two" class="square">User</div>
</div>

[ https://codepen.io/rsepierre/pen/XqVJKR][1]

Для управления вращением любого из этих квадратов вы прокручиваете колесико мыши / сенсорную панель (Shift + колесо, чтобы прокрутить ось Y. Добавил бы поддержку касания при разборке)


  • Вращение левого квадрата относительно объекта:

если вы только прокручиваете влево <-> вправо, нет проблем, это работает. То же самое для <-> вниз.

Теперь, если вы положите элемент в горизонтальное положение (прокрутите на 90° вниз <-> вверх), а затем попытаетесь повернуть на другую ось (влево <-> вправо), он будет вращаться сам по себе. Это означает, что для пользователя это будет выглядеть как вращение оси Z (как часы).


  • Вращение правого квадрата - моя попытка сделать вращение относительно пользователя:

если вы прокрутите влево <-> вправо, нет проблем, это работает. То же самое для <-> вниз.

если вы снова положите элемент в горизонтальное положение (прокрутите на 90° вниз <-> вверх), а затем попытаетесь повернуть на другую ось (прокрутите влево <-> вправо), он будет вращаться относительно пользователя. Элемент вращается по оси "Z", но будет выглядеть как вращение оси Y с точки зрения пользователя.

И это действительно работает: ЕСЛИ вы делаете прокрутку вверх <-> с шагом 90° и никогда не переходите между ними. Даже если при прокрутке вверх <-> вниз вы получите угол в 89° вместо угла в 90°, он постепенно станет беспорядочным.


  • Моя лучшая ставка на решение:

Я понял, что трехмерные повороты не являются коммутативными (порядок, в котором вы применяете повороты, будет влиять на конечный результат, поэтому вращение только по оси Y облажается, а вращение по оси X никогда не происходит).

Одним простым решением было бы просто добавить каждый новый пользовательский ввод в новый поворот (rotateX(100) rotateY(5) rotateY(20) rotateX(105)... на неопределенный срок.

Но очевидно, что я делаю это 60 раз в секунду, это очень быстро станет лукавым пореем CPU/GPU, который вы видели в последнее время.

Из-за этого я считаю, что каким-то образом я должен сделать всю математику за сценой в JS и применить только один поворот rotate3D(X,Y,Z, угол) css.

Дело в том, что я ничего не знаю о математике за матрицами вращения. Я нашел этот конвертер для преобразования 3D-вращений в 3D-вращение, но я думаю, что мне нужно понять, с чего начать любую математику.

Любая помощь высоко ценится.

1 ответ

Нашел этот стековый пост. Это не совсем та же ситуация, но на самом деле та же проблема.

Выложу обновленный код когда я туда доберусь.

тем временем:

Вращающийся куб CSS на фиксированных осях

Другие вопросы по тегам