javascript - Javascript / Css matrix3d onscroll函数在旋转时缩小元素
问题描述
我正在尝试制作一个使用 Css matrix3d 来转换滚动元素的函数。我正在使用此处找到的 rematrix 库ReMatrix来计算矩阵,然后使用 onscroll 函数中的百分比进度来计算元素在场景中移动的百分比。所有这一切都很好。
问题是当旋转元素似乎缩小然后通过场景的进展恢复到正常大小时。这是 matrix3d 的预期行为吗?
3dmatrix 中的一些初始值是 1,所以我通过加 1 然后减 1 来解释这一点。它适用于除旋转之外的所有内容。
我在这里的数学中是否遗漏了一些东西,我太愚蠢了,无法弄清楚它以正确的值开始和结束,但在整个场景中缩小和增长。
这是一个示例小提琴和片段小提琴演示
注意:我只是在演示中使用 700px 来进行场景进度。您可以在滚动 700 像素后忽略效果,或者当框旋转超过 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>
任何人在这里的帮助将不胜感激。谢谢。
解决方案
矩阵旋转不仅仅是值的线性插值。
您已经正确实现了单轴旋转,这意味着修改比例和倾斜值:
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>
推荐阅读
- qt - QWebEngineView - 处理媒体会话 API
- python - 我的 Win 10 python 安装不会创建颜色。ANSI 字符未解释,Colorama,所有其他模块都可以安装,但不要“着色”。
- r - 使用插件将 RevealJS 演示文稿发布到 RStudio Connect?
- eloquent - Laravel 更新事件(观察者):SQLSTATE[22001] 错误
- python - Numpy:列表中所有向量的点积
- r - 使用汇总统计数据忽略 NA 值
- python-3.x - 检查用户是否是机器人的问题
- javascript - 每个边缘的 d3.js 自定义链接距离
- php - PHP-Mailform 在 Godaddy 上不起作用,根据 Godaddy 的规范更新设置不起作用
- salesforce-commerce-cloud - SFCC进口目录