前端实现无限循环滚动三种方式(react hooks示例):js || css || 插件

一、js 滚动事件 scroll

  1. 鼠标移入停止自动滚动,且可以手动上下滚动,可以随时滚动到顶部或底部;鼠标移出自动滚动。
  2. 滚动数据根据需要改为图片或html元素,或者从接口读取数据组装元素。
  3. 实现首尾衔接的思路,分为每条数据固定高度、每条数据不固定高度。如果每条数据固定高度,计算一条数据的高度,每滚动一条数据的高度,就把第一条数据 shift 删除, push 放到末尾;如果每条数据不固定高度,元素渲染完成后,动态计算并保留每条数据外层元素的高度和边距,假设为 height,滚动距离达到 height,则 shift 这条数据并 push 末尾。但首尾衔接,在手动滚动到顶部或底部,回不到数据最初始的状态。
  4. 横向滚动,将 clientHeight,scrollHeight,scrollTop 改为 对应横向属性值。
  5. 渲染数据变化时,或者在模态框中渲染,或依据一些外在条件变化而需要重新渲染时,必须清除所有定时器。
  6. 封装组件使用,需考虑:依赖项,滚动方向,速率,每条数据渲染形式。
  7. 实际开发如果出现滚动停不下来,或者滚动速度加快,排查是否有地方没有及时清除定时器。

在这里插入图片描述

代码
import { useState, useEffect, useRef } from 'react'

// 定时器
let intervalTimer, timeoutTimer

const Index = () => {
    // 滚动容器
    const domRef = useRef()

    // 滚动数据
    const [list, setList] = useState([])

    useEffect(() => {
        // 模拟数据
        const arr = new Array()
        for(let i = 0; i < 30; i++) {
            const obj = {
                key: i,
                value: `第 ${i + 1} 条数据`
            }
            arr.push(obj)
        }
        setList(arr)
    }, [])

    useEffect(() => {
        if(domRef.current) {
            // 开始滚动
            autoScroll()
            // 鼠标移入,清除滚动
            domRef.current.onmouseenter = () => {
                window.clearInterval(intervalTimer)
                window.clearTimeout(timeoutTimer)
            }
            // 鼠标离开,重新滚动
            domRef.current.onmouseleave = () => {
                autoScroll()
            }
        }
    }, [domRef])

    const autoScroll = () => {
        if(timeoutTimer) {
            window.clearTimeout(timeoutTimer)
        }
        timeoutTimer = setTimeout(() => {
            // 容器高度
            const clientH = domRef.current && domRef.current.clientHeight || 0
            // 滚动高度
            const scrollH = domRef.current && domRef.current.scrollHeight || 0
            if(intervalTimer) {
                window.clearInterval(intervalTimer)
            }
            // 无滚动
            if(scrollH == clientH) return
            intervalTimer = setInterval(() => {
                if(domRef.current) {
                    const scrollTop = domRef.current.scrollTop
                    const distance = scrollH - clientH
                    // 计算滚动距离
                    if(scrollTop >= distance) {
                        // 滚动距离大于可滚动高度时,回到顶部
                        domRef.current.scrollTop = -1
                    }
                    domRef.current.scrollTop += 1
                }
            }, 40)
        }, 2000)
    }

    return (
        <div ref={domRef} style={{width: 300, height: 500, overflow: 'auto'}}>
        {
            list.map(item => <li key={item.key}>{item.value}</li>)
        }
    </div>
    )
}

export default Index

二、css

  1. 鼠标移入停止自动滚动,不能手动滚动;鼠标移出自动滚动。
  2. 主要利用 animation 动画属性,定义 keyframes 动画,动态改变元素 y 坐标。
  3. translateY 移动的距离值,是根据每条元素的高度和容器高度来计算的,比如容器高度500,总共30条数据,每条数据高度22,translateY = 22 * 30 - 500 = 160,再根据实际展示效果微调。
代码
import { useState, useEffect } from 'react'

import styles from './index.less'

const Index = () => {
    // 滚动数据
    const [list, setList] = useState([])

    useEffect(() => {
        // 模拟数据
        const arr = new Array()
        for (let i = 0; i < 30; i++) {
            const obj = {
                key: i,
                value: `第 ${i + 1} 条数据`
            }
            arr.push(obj)
        }
        setList(arr)
    }, [])

    return (
        <div className={styles.wrap}>
            {
                list.map(item => <li key={item.key} className={styles.list}>{item.value}</li>)
            }
            {/* 把数据重复渲染一次,效果即是首尾衔接,对应的位移高度需要加上容器高度,即 translatY(-(160 + 500)) = translatY(-660) */}
            {/* {
                list.map(item => <li key={item.key} className={styles.list}>{item.value}</li>)
            } */}
        </div>
    )
}

export default Index

.wrap {
    width: 500px;
    height: 500px;
    overflow: hidden;

    .list {
        width: 300px;
        height: 22px;
        position: relative;
        -webkit-animation-name: vscroll;
           -moz-animation-name: vscroll;
             -o-animation-name: vscroll;
                animation-name: vscroll;
        -webkit-animation-duration: 5s;
           -moz-animation-duration: 5s;
             -o-animation-duration: 5s;
                animation-duration: 5s;
        -webkit-animation-direction: normal;
           -moz-animation-direction: normal;
             -o-animation-direction: normal;
                animation-direction: normal;
        -webkit-animation-iteration-count: infinite;
           -moz-animation-iteration-count: infinite;
             -o-animation-iteration-count: infinite;
                animation-iteration-count: infinite;
        -webkit-animation-timing-function: linear;
           -moz-animation-timing-function: linear;
             -o-animation-timing-function: linear;
                animation-timing-function: linear;
    }
}


.wrap:hover .list {
    -webkit-animation-play-state: paused;
}

@-webkit-keyframes vscroll {
    0% {
        -webkit-transform: translateY(0);
        transform: translateY(0);
    }

    100% {
        -webkit-transform: translateY(-160px);
        transform: translateY(-160px);
    }
}

@-moz-keyframes vscroll {
    0% {
        -webkit-transform: translateY(0);
        -moz-transform: translateY(0);
             transform: translateY(0);
    }

    100% {
        -webkit-transform: translateY(-160px);
        -moz-transform: translateY(-160px);
             transform: translateY(-160px);
    }
}

@-o-keyframes vscroll {
    0% {
        -webkit-transform: translateY(0);
        -o-transform: translateY(0);
           transform: translateY(0);
    }

    100% {
        -webkit-transform: translateY(-160px);
        -o-transform: translateY(-160px);
           transform: translateY(-160px);
    }
}

@keyframes vscroll {
    0% {
        -webkit-transform: translateY(0);
        -moz-transform: translateY(0);
          -o-transform: translateY(0);
             transform: translateY(0);
    }

    100% {
        -webkit-transform: translateY(-160px);
        -moz-transform: translateY(-160px);
          -o-transform: translateY(-160px);
             transform: translateY(-160px);
    }
}

三、插件(react-fast-marquee、animejs)

示例:react-fast-marquee
  1. 鼠标移入停止自动滚动,不能手动滚动;鼠标移出自动滚动。
  2. 无法点击单个元素触发事件,marquee 把点击作为了开始和暂停滚动的属性,需要修改源码使用。
  3. marquee 只能左右滚动,竖向滚动需要自定义样式覆盖以及编写动画(黑科技:把容器旋转90°,但样式写起来比较麻烦)。
  4. 使用时需要根据需求重写或覆盖样式。
关键代码
import Marquee from 'react-fast-marquee'

<Marquee
paruseOnHover
direction='left'
speed={8}
className='mymarquee'
>
{
	list.map(item => <li key={item.key}>{item.value}</li>)
}
</Marquee>
// 去除默认的半透明背景
.marquee-container .overlay::before, .marquee-container .overlay::after {
	background: none;
}

// 竖向滚动
.mymarquee .marquee {
	// 默认 flex 布局,根据实际作更改
	display: block !important;
	// 改为竖向
	animation: vscroll var(--duration) linear var(--delay) var (--iteration-count);
}

// 竖向滚动动画
@keyframes vscroll {
	0% {
		transform: translateY(0%)
	}
	100% {
		transform: translateY(-100%)
	}
}
  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
可以使用useState和useEffect两个Hooks来实现滚动数字效果。 首先,我们需要定义一个state来保存当前数字的值,以及一个目标数字的值。然后,使用useEffect监听目标数字的变化,当目标数字变化时,启动一个计时器,每隔一定时间更新当前数字的值,直到当前数字等于目标数字。 下面是一个简单的实现示例: ```jsx import React, { useState, useEffect } from 'react'; const ScrollNumber = ({ number }) => { const [currentNumber, setCurrentNumber] = useState(0); const [targetNumber, setTargetNumber] = useState(number); useEffect(() => { setTargetNumber(number); }, [number]); useEffect(() => { let timer = null; if (currentNumber < targetNumber) { timer = setInterval(() => { setCurrentNumber(currentNumber + 1); }, 50); } return () => clearInterval(timer); }, [currentNumber, targetNumber]); return ( <span>{currentNumber.toLocaleString()}</span> ); } export default ScrollNumber; ``` 在上面的示例中,我们定义了一个ScrollNumber组件,接受一个number属性作为目标数字,当number变化时,使用useEffect更新targetNumber的值。然后,使用第二个useEffect监听currentNumber和targetNumber的变化,如果currentNumber小于targetNumber,启动一个计时器,每隔50ms更新一次currentNumber的值,直到currentNumber等于targetNumber。最后,返回一个包含当前数字的span元素。 使用方式如下: ```jsx import React from 'react'; import ScrollNumber from './ScrollNumber'; const App = () => { return ( <div> <h1>ScrollNumber Demo</h1> <ScrollNumber number={123456789} /> </div> ); } export default App; ``` 这样就可以在页面上看到一个滚动数字效果了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梅花三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值