记录工作中小思考
背景
对该API的挖掘,源于一个需求
- 用css3实现一个文字无限循环滚动的组件,不要用setInterval
- 当鼠标点击上去的时候,动画停止,用户可以通过滚动条滚动查看
- 鼠标离开时,动画继续
文字无限滚动,网上还是有很多参考的,这个方案不用担心。纠结的是,鼠标hover上去暂停、恢复动画是没问题的,但是动画的滚动是没有scrollTop的,那怎么知道滚动了多少,怎么可能只用css就实现上述需求呢??而且鼠标hover离开还要让滚动条消失出现的,这需求没法做吧。。。
格局小了,又没说只能用一个div,可以用两个div来实现,一个div是无动画效果有滚动条的div,另一个是有动画效果无滚动条的div,鼠标hover上去的时候,切换到无动画效果的div上,鼠标离开时再切回到有动画效果的div上
那么实现这个需求的关键点是,1需要知道动画的滚动的距离,2需要将滚动条滚动的距离告诉动画,让动画接着滚🤣
起初我对动画效果的了解主要是css3的animation,于是我就查怎么知道animation的滚动距离,然后发现了CSS Object Model (CSSOM),可以用js操作css的API,允许动态的读取和修改css样式,之前没这么用过,所以还真不知道。但是我在使用的时候,可能用法也不对把,能获取到页面其他的样式,但是总是获取不到动画样式,于是我就发现这个方法可能不是太适合,就继续查阅别的方法。
找了好久,发现了Web Animation API - 在 JavaScript 中释放 CSS keyframes 的力量,这太关键了,一下子解决了技术难点。 关键:effect.getComputedTiming().progress、currentTime
好了,方案都有了,开始实现
效果
实现
功能点
-
文字无限循环滚动
-
滚动动画效果和鼠标滚动随鼠标是否hover进行切换,并且两中方式滚动的元素位置要衔接好 主要用到:
effect.getComputedTiming().progress:动画的滚动进度,乘上文字区域高度,就知道滚动了多少距离了 currentTime:通过设置这个属性可以控制动画当前滚动的位置
在线效果及代码(代码很好理解,直接看代码吧)
附vue组件代码,上下滚动效果,其他的滚动方向自行调整translate3d y轴参数
<template>
<div
:id='scrollWrapperId'
:class='wrapperClassName'
>
<div
class="scroll-hidden"
:style="{height:height,width:width}"
>
<div class='scroll-content'>
<slot></slot>
<slot></slot>
</div>
</div>
<div
class="scroll-auto"
:style="{width:width}"
>
<div>
<slot></slot>
<slot></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Scroll',
props: {
wrapperClassName: String,
scrollWrapperId: {
type: String,
default: 'scroll-wrapper'
},
height: String, // 带单位
width: {
type: String,
default: '100%'
},
options: { // keyframes参数
type: Object,
default: () => ({
duration: 3000
})
}
},
mounted() {
let scrollTop;
const scrollHiddenDom = document.querySelector(`#${this.scrollWrapperId} > .scroll-hidden`);
const scrollAutoDom = document.querySelector(`#${this.scrollWrapperId} > .scroll-auto`);
const animationContentDom = document.querySelector(`#${this.scrollWrapperId} > .scroll-hidden > .scroll-content`);
const offsetHeight = animationContentDom.offsetHeight / 2;
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/263e8ed773cf4a488b9fa3930341bd3e~tplv-k3u1fbpfcp-watermark.image)
const keyFrames = [
{
transform: 'translate3d(0, 0, 0)',
offset: 0 // 0%
}, {
transform: 'translate3d(0, -50%, 0)',
offset: 1 // 100%
}
];
const keyFramesOptions = {
duration: this.options.duration / 1,
delay: 0,
iterations: Infinity,
easing: 'linear',
fill: 'forwards', // 定义当动画不再播放时,动画应如何将样式应用于其目标 当动画未播放时,目标元素将保留最后关键帧中定义的计算样式
direction: 'normal'// 定义动画是否应该正常播放
};
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/146f6fc2ec6a4741a189a0f817a18f01~tplv-k3u1fbpfcp-watermark.image)
if (!animationContentDom.animate) { // 如果浏览器不支持滚动 就直接展示滑动区域
scrollHiddenDom.style.visibility = 'hidden';
scrollHiddenDom.style.height = 0;
scrollAutoDom.style.visibility = 'visible';
scrollAutoDom.style.height = this.height;
return;
}
const myanimation = animationContentDom.animate(keyFrames, Object.assign({}, keyFramesOptions, this.options));
scrollHiddenDom.addEventListener('mouseenter', () => {
myanimation.pause();
const animationProcess = myanimation.effect.getComputedTiming().progress; // animation滚动进度 范围0-1
scrollTop = offsetHeight * animationProcess;
scrollHiddenDom.style.visibility = 'hidden';
scrollHiddenDom.style.height = 0;
scrollAutoDom.style.visibility = 'visible';
scrollAutoDom.style.height = this.height;
scrollAutoDom.scrollTop = scrollTop;
});
scrollAutoDom.addEventListener('mouseleave', () => {
scrollHiddenDom.style.visibility = 'visible';
scrollHiddenDom.style.height = this.height;
scrollAutoDom.style.visibility = 'hidden';
scrollAutoDom.style.height = 0;
// 通过滚动的距离,计算对应动画的时间值 jump to x second from start of animation
myanimation.currentTime = scrollAutoDom.scrollTop / (offsetHeight / this.options.duration / 1);
myanimation.play();
});
}
};
</script>
<style lang='scss' scoped>
.scroll-hidden {
visibility: visible;
overflow: hidden;
}
.scroll-auto {
visibility: hidden;
height: 0;
overflow: auto;
}
</style>
参考: luchun.github.io/web-animati… www.xiabingbao.com/css3/2017/0… www.cnblogs.com/coco1s/p/13…