我目前正在探索FLIP技术的可能性,该技术将所有CSS转换仅减少到transform
和opacity
(因为GPU加速(。它涉及直接使用 Javascript 操作样式。虽然触发这样的转变并不难,但我发现自己无法逆转它。通常,当您停止悬停时,CSS 中定义的过渡(例如悬停(会自动反转。但CSS不足以触发(和逆转(点击时的过渡。我希望能够执行以下操作:
a( 单击一个项目(并通过例如.CSS类切换来触发大小更改(b( 计算其初始大小和新大小之间的差异,并使用transition
触发transform
c( 在更改其大小
时再次单击它 d(
从单击时的位置反转过渡
我的问题是 d( 步骤。由于某种原因,元素突然将其大小更改为既不是旧大小也不是新大小,而是更改为完全不同的大小。我有一个我在这里尝试做的事情的例子:
https://codesandbox.io/s/flamboyant-snowflake-47t59
单击一个正方形,然后在放大时再次单击它。
有没有可靠的方法可以正确地做我想做的事情?有好的选择吗?
我已经设法找到了一个可行的解决方案(它导入rematrix
库以使转换矩阵的计算更容易(。这个解决方案和理查德的更简单的解决方案都能够制作 60fps 的动画 - 我想区别在于每种方法可以处理的情况数量。
https://codesandbox.io/s/working-transform-reverse-urc16
我相信这就是你要找的。另外,在我看来,您不太完全了解requestAnimationFrame
。它旨在取代一种涉及递归调用setInterval
的技术,以每隔几毫秒更改一次元素的 CSS 属性(在此处阅读更多内容(。所以我建议先阅读更多关于requestAnimationFrame
的信息。
我想指出的是,下面的这个动画可以更容易地简单地使用 CSS 过渡来制作。此外,MDN提到两者或多或少具有相似的性能(阅读此处(。不过,这里有一个你想要的最小工作示例:
let clicked = false;
let square = document.querySelector(".small-sq");
let expandID
let retractID
square.style.transformOrigin = 'top left'
square.style.transform = 'scale(1)'
square.addEventListener('click', e => {
let timeStart = new Date()
let currentScale = parseFloat(square.style.transform.slice(6, -1))
if (!clicked) {
cancelAnimationFrame(retractID)
expandID = requestAnimationFrame(() => repeat(timeStart, currentScale, clicked))
}
else {
cancelAnimationFrame(expandID)
retractID = requestAnimationFrame(() => repeat(timeStart, currentScale, clicked))
}
clicked = !clicked
})
function repeat(timeStart, currentScale, status) {
let timeDifference = new Date() - timeStart
if (status) {
let scaleValue = currentScale + (1 * timeDifference / 5000) > 2 ? 2 : currentScale + (1 * timeDifference / 5000)
square.style.transform = `scale(${scaleValue})`
expandID = requestAnimationFrame(() => repeat(timeStart, scaleValue, status))
if (scaleValue >= 2)
cancelAnimationFrame(expandID)
}
else {
let scaleValue = currentScale - (1 * timeDifference / 5000) < 1 ? 1 : currentScale - (1 * timeDifference / 5000)
square.style.transform = `scale(${scaleValue})`
retractID = requestAnimationFrame(() => repeat(timeStart, scaleValue, status))
if (scaleValue <= 1)
cancelAnimationFrame(retractID)
}
}
.small-sq {
height: 100px;
width: 100px;
background-color: rgb(131, 42, 131);
}
.big-sq {
height: 500px;
width: 500px;
background-color: rgb(131, 42, 131);
}
<div id="app">
<div class="small-sq"></div>
</div>
如果使用 CSS 过渡,则可以将其转换为以下内容:
let square = document.querySelector('.small-sq')
square.addEventListener('click', e => {
square.classList.toggle('toggled')
})
.small-sq {
height: 100px;
width: 100px;
background-color: rgb(131, 42, 131);
transform-origin: left top;
transition: transform 1s linear;
}
.small-sq.toggled {
transform: scale(2)
}
<div id="app">
<div class="small-sq"></div>
</div>