所以我有这个简单的例子,它模仿棋盘中的可拖动棋子。
在
pointerup
我想执行回弹动画到片段起始位置。
好吧,如果我不指定 fill
模式(使用 fill: 'none'
)并在动画完成时手动翻译元素,它就可以工作。
为什么当我指定了
fill: 'forwards'
时它不起作用?我还尝试在动画完成后使用 commitStyles()
将这些样式提交到内联 css,但之后拖动仍然不起作用。您可以在开发工具中看到该部分应该移动,但它仍然留在原处。向前填充应该保持最终动画帧的状态,这基本上就是我想要我的作品所在的位置,不知道为什么它会使其之后不可拖动。
我认为 Web 动画 api 样式和我的样式存在一些冲突,但我无法弄清楚到底是什么问题。 这是否记录在某处,我找不到任何相关内容。
const board = document.querySelector('section')
const piece = document.querySelector('div')
const pieceOffsetSize = 35 // half piece size
const lastPos = { x: 0, y: 0 }
let dragging = false
function translateElement(elm, dx, dy) {
elm.style.transform = `translate(${dx}px, ${dy}px)`
}
function performSnapback(elm) {
const opts = { duration: 300 } // no fill mode (fill: 'none')
const keyframes = [{ transform: `translate(${lastPos.x}px, ${lastPos.y}px)` }]
const animation = elm.animate(keyframes, opts)
animation.onfinish = () => {
translateElement(elm, lastPos.x, lastPos.y) // after finish, i translate element manually
}
}
function performSnapback2(elm) {
const opts = { duration: 300, fill: 'forwards' } // tried fill: 'both' also and does not work
const keyframes = [{ transform: `translate(${lastPos.x}px, ${lastPos.y}px)` }]
const animation = elm.animate(keyframes, opts)
animation.onfinish = () => {
animation.commitStyles() // tried with and without this and does not work
}
}
piece.onpointerdown = e => {
dragging = true
e.target.setPointerCapture(e.pointerId)
e.target.style.userSelect = 'none'
const deltaX = e.clientX - board.offsetLeft - pieceOffsetSize
const deltaY = e.clientY - board.offsetTop - pieceOffsetSize
translateElement(e.target, deltaX, deltaY)
}
piece.onpointerup = e => {
dragging = false
e.target.releasePointerCapture(e.pointerId)
e.target.style.userSelect = 'auto'
// performSnapback(e.target) // WORKS
performSnapback2(e.target) // DOES NOT WORK
}
piece.onpointermove = e => {
if (!dragging) return
const deltaX = e.clientX - board.offsetLeft - pieceOffsetSize
const deltaY = e.clientY - board.offsetTop - pieceOffsetSize
translateElement(e.target, deltaX, deltaY)
}
body { padding: 0; margin: 0; }
section { width: 400px; height: 400px; outline: 2px solid darkviolet; margin: 100px; position: relative; }
div { position: absolute; background: plum; width: 70px; height: 70px; }
<section>
<div style="transform: translate(0px, 0px)"></div>
</section>
如果您通过设置
none
来移动元素,则必须使用 animation
。
检查
none
上的文档:
无
动画在不执行时不会将任何样式应用于目标。 该元素将使用应用于其的任何其他 CSS 规则来显示。这是默认值。
这正是您所需要的。
animation.onfinish
也不需要。
第二次拖动仍会执行
performSnapback
至 x=0
和 y=0
,因为您尚未更新 lastPos
。
const board = document.querySelector('section')
const piece = document.querySelector('div')
const pieceOffsetSize = 35 // half piece size
const lastPos = { x: 0, y: 0 }
let dragging = false
function translateElement(elm, dx, dy) {
elm.style.transform = `translate(${dx}px, ${dy}px)`
}
function performSnapback2(elm) {
const opts = { duration: 300, fill: 'none' };
const keyframes = [{ transform: `translate(${lastPos.x}px, ${lastPos.y}px)` }]
elm.animate(keyframes, opts)
}
piece.onpointerdown = function(e) {
dragging = true
e.target.setPointerCapture(e.pointerId)
const deltaX = e.clientX - board.offsetLeft - pieceOffsetSize
const deltaY = e.clientY - board.offsetTop - pieceOffsetSize
translateElement(e.target, deltaX, deltaY)
}
piece.onpointerup = e => {
dragging = false
e.target.releasePointerCapture(e.pointerId)
e.target.style.userSelect = 'auto'
performSnapback2(e.target)
}
piece.onpointermove = e => {
if (!dragging) return
const deltaX = e.clientX - board.offsetLeft - pieceOffsetSize
const deltaY = e.clientY - board.offsetTop - pieceOffsetSize
translateElement(e.target, deltaX, deltaY)
}
body { padding: 0; margin: 0; }
section { width: 400px; height: 400px; outline: 2px solid darkviolet; margin: 100px; position: relative; }
div { position: absolute; background: plum; width: 70px; height: 70px; z-index: 1; }
<section>
<div style="transform: translate(0px, 0px)"></div>
</section>