时间:2022-03-04 11:22:32 | 栏目:JavaScript代码 | 点击:次
可视化大屏项目使用最多的组件就是数字组件,展示数据的一个变化,为了提高视觉效果,需要给数字增加一个滚动效果,实现一个数字到另一个数字逐步递增的滚动动画。
先上一个思维导图:
先列举所有的可能的值形成一个纵向的列表,然后固定一个容器,匀速更改数字的偏移值。
下面来介绍一下这种方案的实现,元素值从0到9一共十个值,每个数字占纵向列表的10%,所以纵向偏移值依次为为0% —> -90%
实现:
<ul> <li> <span>0123456789</span> </li> </ul>
ul{ margin-top: 200px; } ul li{ margin:0 auto; width: 20px; height: 30px; text-align: center; border:2px solid rgba(221,221,221,1); border-radius:4px; } ul li span{ position: absolute; color: #fff; top: 30%; left: 50%; transform: translate(-50%,0); transition: transform 500ms ease-in-out; writing-mode: vertical-rl; text-orientation: upright; letter-spacing: 17px; }
let spanDom = document.querySelector('span') let start = 0 setInterval(() =>{ start++ if(start>9){ start = 0 } spanDom.style.transform = `translate(-50%,-${start*10}%)` }, 1000)
上述代码存在一个问题,当我们从9到0的时候,容器偏移从-90%直接到了0%。 但是由于设定了固定的过渡动画时间,就会出现一个向反方向滚动的情况,为了解决这个问题,可以参考无缝滚动的思路
<ul> <li> <span>01234567890</span> </li> </ul>
let spanDom = document.querySelector('span') let start = 0 var timer = setInterval(fn, 1000); function fn() { start++ clearInterval(timer) timer = setInterval(fn,start >10 ? 0 : 1000); if(start>10){ spanDom.style.transition = `none` start = 0 }else{ spanDom.style.transition = `transform 500ms ease-in-out` } spanDom.style.transform = `translate(-50%,-${start/11*100}%)` }
仔细看动图的效果,事实上在在视口只有两个元素,一个值之前的值,一个为当前的值,滚动偏移值只需设置translateY(-100%)
具体思路:
下面是调整后的代码结构:
<div className={styles.slider}> {[prev, cur].map((item, index) => ( <span key={index} className={`${styles['slider-text']} ${playing && styles['slider-ani']} ${(prev === 0 && cur === 0 && index ===0) && styles['slider-hide']}`}> {item} </span> ))} </div>
const { value} = props const [prev, setPrev] = useState(0) const [cur, setCur] = useState(0) const [playing, setPlaying] = useState(false) const play = (pre, current) => { setPrev(pre) setCur(current) setPlaying(false) setTimeout(() => { setPlaying(true) }, 20) } useEffect(() => { if (!Number.isNaN(value)) { play(cur, value) } else { setPrev(value) setCur(value) } }, [value])
.slider { display: flex; flex-direction: column; height: 36px; margin-top: 24%; overflow: hidden; text-align: left; } .slider-text { display: block; height: 100%; transform: translateY(0%); } .slider-ani { transform: translateY(-100%); transition: transform 1s ease; } .slider-hide { opacity: 0; }
实现多个滚轮的向上滚动的数字组件
实现一个数字的逐渐递增的滚动动画,并且要在指定时间内完成。要看到流畅的动画效果,就需要在更新元素状态时以一定的频率进行,JS动画都是通过在很短的时间内不停的渲染/绘制元素做到的,所以计时器一直都是Javascript动画的核心技术,关键就是刷新的间隔时间,刷新时间需要尽量短,这样动画效果才能显得更加流畅,不卡顿;同时刷新间隔又不能太短,需要确保浏览器有能力渲染动画 。
大多数电脑显示器的刷新频率是 60Hz,即每秒重绘 60次。因此平滑动画的最佳循环间隔是通常是 1000ms/60,约等于16.6ms
动画开始,记录开始动画的时间 startTimeRef.current
const startTimeRef = useRef(Date.now()); const [t, setT] = useState(Date.now());
之后每一帧动画,记录从开始动画经过了多长时间,计算出当前帧的所到达的数字应该是多少,即currentValue
useEffect(() => { const rafFunc = () => { const now = Date.now(); const t = now - startTimeRef.current; if (t >= period) { setT(period); } else { setT(t); requestAnimationFrame(rafFunc); } }; let raf; if (autoScroll) { raf = requestAnimationFrame(rafFunc); startTimeRef.current = Date.now(); } else { raf && cancelAnimationFrame(raf); } return () => raf && cancelAnimationFrame(raf); }, [period, autoScroll]); const currentValue = useMemo(() => ((to - from) / period) * t + from, [t, period, from, to]);
针对当前每个数字位上的数字进行比较,如果有变化,进行偏移量的变化,偏移量体现在当前数字位上的数字与下一位数字之间的差值,这个变化每一帧都串起来形成了滚动动画