如何用原生JS修改CSS动画元素X轴位置,拖拽后动画仍正常运行
实现思路
- 拖拽过程中用
transform: translateX()做临时位置偏移,不修改CSS动画控制的left属性,避免和动画的属性控制权冲突 - 拖拽启动时主动暂停CSS动画,避免动画运行干扰拖拽定位
- 拖拽结束后计算当前元素相对父容器的实际位置,换算为动画的进度比例,通过设置负的animation-delay让动画直接跳转到对应进度,再恢复动画播放,即可实现拖拽后动画从当前位置继续运行
完整实现代码
<style> .dummyAnimation{width:100%;position:relative;height:100px;} .bump{width:100px;height:100px;background:blue;animation:dummySequence 30s linear infinite;position:absolute;} .bump:hover{animation-play-state:paused;} @keyframes dummySequence{ 0%{left:0;} 100%{left:calc(100% - 100px)} } </style> <div class="dummyAnimation"> <div class="bump"></div> </div> <script> const bump = document.querySelector('.bump'); const parent = document.querySelector('.dummyAnimation'); // 动态获取CSS中设置的动画时长,不需要硬编码 const ANIMATION_DURATION = parseFloat(getComputedStyle(bump).animationDuration) * 1000; let isDragging = false; let startX = 0; let startTransformX = 0; bump.addEventListener('mousedown', (e) => { isDragging = true; startX = e.clientX; bump.style.animationPlayState = 'paused'; // 读取当前已有的transform偏移,避免多次拖拽偏移累计 const transform = getComputedStyle(bump).transform; startTransformX = transform === 'none' ? 0 : parseFloat(transform.split(',')[4]); e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const diffX = e.clientX - startX; let currentX = startTransformX + diffX; // 边界限制,禁止拖出父容器范围 const maxOffset = parent.offsetWidth - bump.offsetWidth; currentX = Math.max(0, Math.min(currentX, maxOffset)); bump.style.transform = `translateX(${currentX}px)`; }); document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; // 计算当前元素相对父容器的实际左偏移 const parentRect = parent.getBoundingClientRect(); const bumpRect = bump.getBoundingClientRect(); const currentLeft = bumpRect.left - parentRect.left; const totalDistance = parent.offsetWidth - bump.offsetWidth; // 换算动画进度 const progress = Math.max(0, Math.min(1, currentLeft / totalDistance)); // 负延迟让动画直接跳转到对应进度,单位转成秒 const delay = -(progress * ANIMATION_DURATION) / 1000; // 重置临时偏移,更新动画配置后恢复播放 bump.style.transform = 'none'; bump.style.animationDelay = `${delay}s`; bump.style.animationPlayState = 'running'; }); </script>
关键逻辑说明
- 负延迟的作用:CSS动画的负延迟值可以让动画直接跳转到对应时间点的状态,不需要等待延迟时间结束,刚好适配拖拽结束后从当前位置续播的需求
- 用transform做临时偏移的优势:transform属于合成层属性,性能远高于直接修改left,拖拽过程更流畅,且不会和动画控制的left属性产生冲突
- 全局事件绑定:拖拽的mousemove和mouseup绑定在document上,避免鼠标移动太快移出元素后拖拽失效
内容的提问来源于stack exchange,提问作者Viewer
相关产品推荐
相关产品推荐

