Javascript / Css matrix3d ​​функция прокрутки сокращает элемент при вращении - PullRequest
0 голосов
/ 31 января 2020

Я пытаюсь создать функцию, которая использует Css matrix3d ​​для преобразования элементов при прокрутке. Я использую библиотеку rematrix, найденную здесь ReMatrix , чтобы вычислить матрицу, а затем использовать прогресс в процентах в функции прокрутки, чтобы вычислить процент перемещения элемента по сцене. Все это прекрасно работает.

Проблема заключается в том, что при повороте элемента кажется, что он сжимается, а затем возвращается к нормальному размеру по мере развития сцены. Это ожидаемое поведение matrix3d?

Некоторые из начальных значений в 3dmatrix равны 1, поэтому я учитываю это, прибавляя 1, затем вычитая 1. Он отлично работает для всего, кроме вращения.

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

Вот пример скрипты и фрагмента Fiddle Демо

Примечание: я просто использую 700px для продвижения сцены в демо. Вы можете игнорировать эффекты после прокрутки 700px или когда коробка поворачивается на 90 градусов, это просто демонстрация.

let matrix;
const el = document.querySelector('.box');

const updateScroll = () => {

  const scrollPos = window.scrollY;
  const progress = scrollPos / 700;
  
  let m = [...matrix];
  m[0] = progress * (matrix[0] - 1) + 1;
  m[1] = progress * matrix[1];
  m[2] = progress * matrix[2];
  m[3] = progress * matrix[3];
  m[4] = progress * matrix[4];
  m[5] = progress * (matrix[5] - 1) + 1;
  m[6] = progress * matrix[6];
  m[7] = progress * matrix[7];
  m[8] = progress * matrix[8];
  m[9] = progress * matrix[9];
  m[10] = progress * (matrix[10] - 1) + 1;
  m[11] = progress * matrix[11];
  m[12] = progress * (matrix[12] / 100) * 100;
  m[13] = progress * (matrix[13] / 100) * 100;
  m[14] = progress * (matrix[14] / 100) * 100;
  m[15] = progress * (matrix[15] - 1) + 1;
  
  setTransform(el, toString(m));
}

const init = () => {
  const r1 = rotateZ(90);
  const t1 = translateY(700);
  matrix = multiply(t1,r1);
  window.addEventListener('scroll', updateScroll);
}

const setTransform = (el, transform) => {
  el.style.transform = transform;
  el.style.WebkitTransform = transform;
};

/*
*  
* REMATRIX Functions
* https://github.com/jlmakes/rematrix
*
*/


function translateY(distance) {
  const matrix = identity();
  matrix[13] = distance;
  return matrix;
}

function toString(source) {
  return `matrix3d(${format(source).join(', ')})`;
}

function rotateZ(angle) {
  const theta = (Math.PI / 180) * angle;
  const matrix = identity();

  matrix[0] = matrix[5] = Math.cos(theta).toFixed(6);
  matrix[1] = matrix[4] = Math.sin(theta).toFixed(6);
  matrix[4] *= -1;

  return matrix;
}

function format(source) {
  if (source.constructor !== Array) {
    throw new TypeError('Expected array.');
  }
  if (source.length === 16) {
    return source;
  }
  if (source.length === 6) {
    const matrix = identity();
    matrix[0] = source[0];
    matrix[1] = source[1];
    matrix[4] = source[2];
    matrix[5] = source[3];
    matrix[12] = source[4];
    matrix[13] = source[5];
    return matrix;
  }
  throw new RangeError('Expected array with either 6 or 16 values.');
}

function identity() {
  const matrix = [];
  for (let i = 0; i < 16; i++) {
    i % 5 == 0 ? matrix.push(1) : matrix.push(0);
  }
  return matrix;
}

function multiply(m, x) {
  const fm = format(m);
  const fx = format(x);
  const product = [];

  for (let i = 0; i < 4; i++) {
    const row = [fm[i], fm[i + 4], fm[i + 8], fm[i + 12]];
    for (let j = 0; j < 4; j++) {
      const k = j * 4;
      const col = [fx[k], fx[k + 1], fx[k + 2], fx[k + 3]];
      const result =
        row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3];

      product[i + k] = result;
    }
  }

  return product;
}

init();
body{
  min-height: 400vh;
}

.box{
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  margin: auto;
  width: 100px;
  height: 100px;
  background: green;
}
<div class="box"></div>

Любая помощь здесь будет оценена. Спасибо.

1 Ответ

0 голосов
/ 01 февраля 2020

Вращение матрицы - это не просто линейная интерполяция значений.

У вас уже есть правильная реализация вращения по одной оси, подразумевающая изменение значений масштаба и перекоса:

function rotateZ(angle) {
  const theta = (Math.PI / 180) * angle;
  const matrix = identity();

  matrix[0] = matrix[5] = Math.cos(theta).toFixed(6);
  matrix[1] = matrix[4] = Math.sin(theta).toFixed(6);
  matrix[4] *= -1;

  return matrix;
}

Так что ваша алгебра там сломана, что испортит значения шкалы и уменьшит ваш элемент.

Но у вас уже есть все, что вам нужно, просто используйте его.

const el = document.querySelector('.box');

const updateScroll = () => {

  const scrollPos = window.scrollY;
  const progress = Math.min(scrollPos / 700, 1);
  // create a new Matrix, correctly translated and rotated
  const t1 = translateY(scrollPos);
  const r1 = rotateZ( 360 * progress );
  const matrix = multiply(t1, r1);
  setTransform(el, toString(matrix));
}

const init = () => {
  window.addEventListener('scroll', updateScroll);
}

const setTransform = (el, transform) => {
  el.style.transform = transform;
  el.style.WebkitTransform = transform;
};

/*
*  
* REMATRIX Functions
* https://github.com/jlmakes/rematrix
*
*/


function translateY(distance) {
  const matrix = identity();
  matrix[13] = distance;
  return matrix;
}

function toString(source) {
  return `matrix3d(${format(source).join(', ')})`;
}

function rotateZ(angle) {
  const theta = (Math.PI / 180) * angle;
  const matrix = identity();

  matrix[0] = matrix[5] = Math.cos(theta).toFixed(6);
  matrix[1] = matrix[4] = Math.sin(theta).toFixed(6);
  matrix[4] *= -1;

  return matrix;
}

function format(source) {
  if (source.constructor !== Array) {
    throw new TypeError('Expected array.');
  }
  if (source.length === 16) {
    return source;
  }
  if (source.length === 6) {
    const matrix = identity();
    matrix[0] = source[0];
    matrix[1] = source[1];
    matrix[4] = source[2];
    matrix[5] = source[3];
    matrix[12] = source[4];
    matrix[13] = source[5];
    return matrix;
  }
  throw new RangeError('Expected array with either 6 or 16 values.');
}

function identity() {
  const matrix = [];
  for (let i = 0; i < 16; i++) {
    i % 5 == 0 ? matrix.push(1) : matrix.push(0);
  }
  return matrix;
}

function multiply(m, x) {
  const fm = format(m);
  const fx = format(x);
  const product = [];

  for (let i = 0; i < 4; i++) {
    const row = [fm[i], fm[i + 4], fm[i + 8], fm[i + 12]];
    for (let j = 0; j < 4; j++) {
      const k = j * 4;
      const col = [fx[k], fx[k + 1], fx[k + 2], fx[k + 3]];
      const result =
        row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3];

      product[i + k] = result;
    }
  }

  return product;
}

init();
body{
  height: 400vh;
  min-height: 700px;
}

.box{
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  margin: auto;
  width: 100px;
  height: 100px;
  background: green;
}
<div class="box"></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...