react 封装无缝滚动组件

news2025/3/31 14:02:20

记录,以防忘记

SeamlessScroll.tsx

import React, { useEffect, useRef, useState } from 'react';

interface SeamlessScrollProps {
    children: React.ReactNode;
    speed?: number; // 滚动速度,单位:像素/秒
    minItems?: number; // 最小项目数量,默认3个
    className?: string;
    style?: React.CSSProperties;
}

/**
 * 无缝滚动组件(从下往上滚动)
 * @param children - 需要滚动的内容
 * @param speed - 滚动速度,默认 50 像素/秒
 * @param minItems - 最小项目数量,默认3个,只有超过这个数量才会滚动
 * @param className - 自定义类名
 * @param style - 自定义样式
 */
const SeamlessScroll = ({ children, speed = 50, minItems = 3, className = '', style = {} }: SeamlessScrollProps) => {
    const containerRef = useRef<HTMLDivElement>(null);
    const contentRef = useRef<HTMLDivElement>(null);
    const [isHovered, setIsHovered] = useState(false);
    const animationFrameRef = useRef<number>();
    const lastTimeRef = useRef<number>(0);
    const positionRef = useRef<number>(0);
    const [shouldScroll, setShouldScroll] = useState(false);

    // 检查是否需要滚动
    useEffect(() => {
        if (contentRef.current) {
            const itemCount = React.Children.count(children);
            setShouldScroll(itemCount > minItems);
        }
    }, [children, minItems]);

    // 计算滚动距离
    const calculateScrollDistance = (deltaTime: number) => {
        return (speed * deltaTime) / 1000; // 将速度转换为每毫秒的滚动距离
    };

    // 处理滚动动画
    const handleScroll = (timestamp: number) => {
        if (!containerRef.current || !contentRef.current || !shouldScroll) return;

        const deltaTime = timestamp - lastTimeRef.current;
        lastTimeRef.current = timestamp;

        const scrollDistance = calculateScrollDistance(deltaTime);
        const content = contentRef.current;

        // 更新位置
        positionRef.current += scrollDistance;

        // 当滚动到一半时重置位置
        if (positionRef.current >= content.offsetHeight / 2) {
            positionRef.current = 0;
        }

        // 应用变换
        content.style.transform = `translateY(-${positionRef.current}px)`;

        if (!isHovered) {
            animationFrameRef.current = requestAnimationFrame(handleScroll);
        }
    };

    // 开始滚动
    useEffect(() => {
        if (!isHovered && shouldScroll) {
            lastTimeRef.current = performance.now();
            animationFrameRef.current = requestAnimationFrame(handleScroll);
        }

        return () => {
            if (animationFrameRef.current) {
                cancelAnimationFrame(animationFrameRef.current);
            }
        };
    }, [isHovered, speed, shouldScroll]);

    // 复制内容以实现无缝滚动
    const duplicatedContent = (
        <div
            ref={contentRef}
            style={{ display: 'block', width: '100%', willChange: 'transform', transition: 'none' }}        >
            {children}
            {shouldScroll && children}
        </div>
    );

    return (
        <div
            ref={containerRef}
            className={`seamless-scroll ${className}`}
            style={{
                position: 'relative',
                overflow: 'hidden',
                ...style,
            }}
            onMouseEnter={() => setIsHovered(true)}
            onMouseLeave={() => setIsHovered(false)}
        >
            {duplicatedContent}
        </div>
    );
};

export default SeamlessScroll; 

使用

<SeamlessScroll speed={30} minItems={4} style={{ height: '700px' }}>
    <div style={{ padding: '10px' }}>
        <List dataSource={data} renderItem={(item) => (
            <List.Item>
                <Card style={{ width: '100%' }}>
                    <h3>{item.title}</h3>
                    <p>{item.content}</p>
                </Card>
            </List.Item>
        )}
        />
    </div>
</SeamlessScroll>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[ComfyUI] 如何升级自定义节点(Custom Nodes)

ComfyUI 提供了灵活的 自定义节点(Custom Nodes) 功能,允许用户扩展其能力。随着插件的更新,保持 Custom Nodes 处于最新状态是确保兼容性和功能完整性的关键。 1. 手动升级(Git Pull 方式) 如果你的 自定义节点 是通过 Git 克隆的,可以使用 Git 命令来升级: 步骤: …

linux和windows是采用何种机制保存密码的?

传统Linux的不足&#xff1a; 1&#xff09;存在特权用户root 任何人只要得到root的权限&#xff0c;对于整个系统都可以为所欲为。这一点Windows也一样。 &#xff12;)对于文件的访问权划分不够细 在linux系统里&#xff0c;对于文件的操作&#xff0c;只有「所有者」…

matlab打开两个工程

1、问题描述 写代码时&#xff0c;需要实时参考别人的代码&#xff0c;需要同时打开2个模型&#xff0c;当模型在同一个工程内时&#xff0c;这是可以直接打开的&#xff0c;如图所示 2、解决方案 再打开一个MATLAB主窗口 这个时候就可以同时打开多个模型了 3、正确的打开方…

HarmonyOS主题管理工具封装:动态切换、持久化存储与常见问题解析

注&#xff1a;适用版本&#xff08;Harmony OS NEXT / 5.0 / API 12 &#xff09; 一、效果展示 二、技术栈 HarmonyOS ArkUI框架 使用AppStorage实现跨组件状态管理&#xff0c;PersistentStorage持久化存储用户偏好。 系统配置常量 ConfigurationConstant.Color…

60V单通道高精度线性恒流LED驱动器防60V反接SOD123封装

产品描述: PC561A 系列产品是用于产生单通道、高精度恒流源&#xff08; Constant Current Regulator&#xff0c; CCR&#xff09; 的LED 驱动芯片&#xff0c;为各类 LED 照明应用提供高性价比恒流方案。PC561A 采用晶体管自偏置技术&#xff0c;可在超宽工作电压范围内维持…

学习threejs,使用Sprite精灵、SpriteMaterial精灵材质

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.Sprite1.1.1 ☘️代码…

Cent OS7+Docker+Dify

由于我之前安装了Dify v1.0.0&#xff0c;出现了一些问题&#xff1a;无法删除&#xff0c;包括&#xff1a;知识库中的文件、应用、智能体、工作流&#xff0c;都无法删除。现在把服务器初始化&#xff0c;一步步重新安装&#xff0c;从0到有。 目录 1、服务器重装系统和配置…

本地AI大模型部署革命:Ollama部署和API调试教程

Ollama&#xff1a;你的私人AI助手 还在为云端AI服务的高昂费用和隐私问题而烦恼吗&#xff1f;Ollama横空出世&#xff0c;它是一款专为本地环境打造的大模型部署神器&#xff0c;让你轻松在自己的设备上运行各种强大的AI模型。无论你是开发者还是普通用户&#xff0c;Ollama…

centos7 linux VMware虚拟机新添加的网卡,能看到网卡名称,但是看不到网卡的配置文件

问题现象&#xff1a;VMware虚拟机新添加的网卡&#xff0c;能看到网卡&#xff0c;但是看不到网卡的配置文件 解决方案&#xff1a; nmcli connection show nmcli connection add con-name ens36 ifname ens36 type ethernet #创建一个网卡连接配置文件&#xff0c;这里con…

K8S学习之基础五十一:k8s部署jenkins

k8s部署jenkins 创建nfs共享目录&#xff0c; mkdir -p /data/v2 echo /data/v2 *(rw,no_root_squash) > /etc/exports exportfs -arv创建pv、pvc vi pv.yaml apiVersion: v1 kind: PersistentVolume metadata:name: jenkins-k8s-pv spec:capacity:storage: 1GiaccessMod…

在 Mermaid 流程图里“驯服”quot;的魔法指南!!!

&#x1f409; 在 Mermaid 流程图里“驯服”"的魔法指南 在使用 Mermaid 画流程图时&#xff0c;是不是经常遇到想秀一波 &quot; 却被它“反杀”的情况&#xff1f;&#x1f3af; 今天就来教大家如何在这头代码野兽的嘴里&#xff0c;抢回我们的双引号实体编码&#…

GitHub美化个人主页3D图表显示配置操作

这个功能主要是用的这个开源仓库&#xff1a;https://github.com/yoshi389111/github-profile-3d-contrib 想看效果的话&#xff0c;我的个人主页&#xff1a;https://github.com/Sjj1024 开始操作 1.创建自己的github主页属性项目——跟你github用户名一致即可&#xff0c;…

HarmonyOS NEXT 鸿蒙中关系型数据库@ohos.data.relationalStore API 9+

核心API ohos.data.relationalStore API 9 数据库 数据库是存储和管理数据的系统 数据库&#xff08;Database&#xff09;是一个以特定方式组织、存储和管理数据的集合&#xff0c;通常用于支持各种应用程序和系统的运行。它不仅是存放数据的仓库&#xff0c;还通过一定的…

【JavaScript】JavaScript Promises实践指南

【JavaScript】JavaScript Promises实践指南 你了解JavaScript中的Promises吗&#xff1f;这是一个很多人一开始就放弃的主题&#xff0c;但我会尽量让它变得尽可能简单。 1. “Promise”到底是什么&#xff1f; “Promise”是异步编程中的一个基本概念&#xff0c;特别是在J…

git push的时候出现无法访问的解决

fatal: 无法访问 https://github.com/...&#xff1a;gnutls_handshake() failed: Error in the pull function. push的时候没有输入自己的github账号密码&#xff0c;为了解决每次push都要登录github这个问题&#xff0c;采用ssh密钥的方式认证&#xff0c;可以免去每次都输入…

为什么大模型在 OCR 任务上表现不佳?

编者按&#xff1a; 你是否曾经用最先进的大语言模型处理企业文档&#xff0c;却发现它把财务报表中的“$1,234.56”读成了“123456”&#xff1f;或者在处理医疗记录时&#xff0c;将“0.5mg”误读为“5mg”&#xff1f;对于依赖数据准确性的运营和采购团队来说&#xff0c;这…

HCIP(VLAN综合实验)

实验拓补图 实验分析 一、实验目的 掌握VLAN的创建和配置方法理解VLAN在局域网中的作用学习如何通过VLAN实现网络隔离和通信 二、实验环境 交换机&#xff08;SW1、SW2、SW3&#xff09;个人电脑&#xff08;PC1、PC2、PC3、PC4、PC5、PC6&#xff09;路由器&#xff08;R1…

每日算法-250328

记录今天学习和解决的LeetCode算法题。 92. 反转链表 II 题目 思路 本题要求反转链表中从 left 到 right 位置的节点。我们可以采用 头插法 的思路来反转指定区间的链表。 具体来说&#xff0c;我们首先定位到 left 位置节点的前一个节点 prev。然后&#xff0c;从 left 位置…

从 Word 到 HTML:使用 Aspose.Words 轻松实现 Word 文档的高保真转换

从 Word 到 HTML&#xff1a;使用 Aspose.Words 轻松实现 Word 文档的高保真转换 前言一、环境准备二、核心代码实现1. 将 Word 转换为 HTML 文件流2. 优化超链接样式 三、测试效果四、总结 前言 在日常开发中&#xff0c;我们经常需要将 Word 文档转换为 HTML&#xff0c;用于…

RSA 简介及 C# 和 js 实现【加密知多少系列_4】

〇、简介 谈及 RSA 加密算法&#xff0c;我们就需要先了解下这两个专业名词&#xff0c;对称加密和非对称加密。 对称加密&#xff1a;在同一密钥的加持下&#xff0c;发送方将未加密的原文&#xff0c;通过算法加密成密文&#xff1b;相对的接收方通过算法将密文解密出来原文…