当前位置: 首页 > news >正文

React实现无缝滚动轮播图

实现效果:

 由于是演示代码,我是直接写在了App.tsx里面在

文件位置如下:

App.tsx代码如下:

import { useState, useEffect, useCallback, useRef } from "react";
import { ImageContainer } from "./view/ImageContainer";// 图片列表配置
const IMAGE_LIST = ["https://oss.cloudhubei.com.cn/cms/release/set35/20241014/9ec7a083198223c49c06e3ebbf8df33c.jpg","https://img0.baidu.com/it/u=3231399477,1564831636&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800","https://q4.itc.cn/images01/20240810/879397b2e3ed4bb8b35be2f272d26b7a.jpeg","https://img1.baidu.com/it/u=3484599935,468270965&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800",
];// 常量配置
const TRANSITION_DURATION = 500; // 过渡动画持续时间
const AUTO_PLAY_INTERVAL = 2000; // 自动播放间隔时间
const USER_INTERACTION_DELAY = 1000; // 用户交互后暂停自动播放的时间function App() {// 状态管理const [imgIndex, setImgIndex] = useState<number>(0); // 当前显示的图片索引const [isAutoPlaying, setIsAutoPlaying] = useState(false); // 是否自动播放const [isSliding, setIsSliding] = useState(false); // 是否正在滑动const [direction, setDirection] = useState<"left" | "right">("left"); // 滑动方向const [isPaused, setIsPaused] = useState(false); // 是否暂停(鼠标悬停时)const [translateX, setTranslateX] = useState(0); // 滑动距离const [userInteracting, setUserInteracting] = useState(false); // 用户是否正在交互const userInteractingTimer = useRef<NodeJS.Timeout | null>(null); // 用户交互定时器// 处理图片过渡效果const handleSlideTransition = useCallback((nextIndex: number, slideDirection: "left" | "right") => {if (isSliding) return;setDirection(slideDirection);setIsSliding(true);// 计算每张图片的宽度百分比const itemWidth = 100 / (IMAGE_LIST.length + 1);if (nextIndex >= IMAGE_LIST.length) {// 处理从最后一张到第一张的无缝滚动setTranslateX(-(nextIndex * itemWidth));setTimeout(() => {setIsSliding(false);setTranslateX(0);setImgIndex(0);}, TRANSITION_DURATION);} else {// 普通图片切换setTranslateX(-(nextIndex * itemWidth));setTimeout(() => {setImgIndex(nextIndex);setIsSliding(false);}, TRANSITION_DURATION);}},[isSliding]);// 处理用户交互状态const handleUserInteraction = useCallback(() => {setUserInteracting(true);if (userInteractingTimer.current) {clearTimeout(userInteractingTimer.current);}userInteractingTimer.current = setTimeout(() => {setUserInteracting(false);}, USER_INTERACTION_DELAY);}, []);// 下一张图片const handleNext = useCallback(() => {if (isSliding) return;handleUserInteraction();const nextIndex =imgIndex === IMAGE_LIST.length - 1 ? IMAGE_LIST.length : imgIndex + 1;handleSlideTransition(nextIndex, "left");}, [imgIndex, isSliding, handleSlideTransition, handleUserInteraction]);// 上一张图片const handlePrevious = useCallback(() => {if (isSliding) return;handleUserInteraction();const previousIndex = imgIndex === 0 ? IMAGE_LIST.length - 1 : imgIndex - 1;handleSlideTransition(previousIndex, "right");}, [imgIndex, isSliding, handleSlideTransition, handleUserInteraction]);// 点击指示器切换图片const handleDotClick = useCallback((index: number) => {if (isSliding || index === imgIndex) return;handleUserInteraction();// 处理特殊情况的无缝滚动if (imgIndex === IMAGE_LIST.length - 1 && index === 0) {handleSlideTransition(IMAGE_LIST.length, "left");} else if (imgIndex === 0 && index === IMAGE_LIST.length - 1) {handleSlideTransition(index, "right");} else {const slideDirection = index > imgIndex ? "left" : "right";handleSlideTransition(index, slideDirection);}},[imgIndex, isSliding, handleSlideTransition, handleUserInteraction]);// 鼠标悬停处理const handleMouseEnter = useCallback(() => {if (isAutoPlaying) {setIsPaused(true);}}, [isAutoPlaying]);const handleMouseLeave = useCallback(() => {if (isAutoPlaying) {setIsPaused(false);}}, [isAutoPlaying]);// 切换自动播放状态const handleToggleAutoPlay = useCallback(() => {setIsAutoPlaying((prev) => {if (!prev) {setUserInteracting(false);if (userInteractingTimer.current) {clearTimeout(userInteractingTimer.current);}}return !prev;});}, []);// 初始化useEffect(() => {setTranslateX(0);setImgIndex(0);}, []);// 清理定时器useEffect(() => {return () => {if (userInteractingTimer.current) {clearTimeout(userInteractingTimer.current);}};}, []);// 自动播放控制useEffect(() => {let timer: NodeJS.Timeout;if (isAutoPlaying && !isSliding && !isPaused && !userInteracting) {timer = setInterval(() => {const nextIndex =imgIndex === IMAGE_LIST.length - 1 ? IMAGE_LIST.length : imgIndex + 1;handleSlideTransition(nextIndex, "left");}, AUTO_PLAY_INTERVAL);}return () => {if (timer) {clearInterval(timer);}};}, [isAutoPlaying,isSliding,isPaused,userInteracting,imgIndex,handleSlideTransition,]);return (<div className={`App ${isSliding ? "sliding" : ""}`}><ImageContainercurrentIndex={imgIndex}onNext={handleNext}onPrevious={handlePrevious}onToggleAutoPlay={handleToggleAutoPlay}isAutoPlaying={isAutoPlaying}totalImages={IMAGE_LIST.length}onDotClick={handleDotClick}imgList={IMAGE_LIST}direction={direction}onMouseEnter={handleMouseEnter}onMouseLeave={handleMouseLeave}isSliding={isSliding}translateX={translateX}/></div>);
}export default App;

ImageContainer.tsx代码如下:

import "./ImageContainer.css";// Props 类型定义
interface PropsType {currentIndex: number; // 当前显示的图片索引totalImages: number; // 图片总数imgList: string[]; // 图片列表isSliding: boolean; // 是否正在滑动isAutoPlaying: boolean; // 是否自动播放中direction: "left" | "right"; // 滑动方向translateX: number; // 滑动距离onNext: () => void; // 下一张回调onPrevious: () => void; // 上一张回调onToggleAutoPlay: () => void; // 切换自动播放回调onDotClick: (index: number) => void; // 点击指示器回调onMouseEnter: () => void; // 鼠标进入回调onMouseLeave: () => void; // 鼠标离开回调
}export const ImageContainer = (props: PropsType) => {// 创建包含额外图片的数组(在末尾添加第一张图片的副本,用于无缝滚动)const extendedImages = [...props.imgList, props.imgList[0]];// 计算滑动容器样式const sliderStyle = {transform: `translateX(${props.translateX}%)`,width: `${(props.totalImages + 1) * 100}%`, // 总宽度包含额外的图片};// 构建滑动容器的类名const sliderClassName = ["image-slider",props.direction,props.isSliding ? "sliding" : "",].filter(Boolean).join(" ");// 渲染控制按钮组const renderControls = () => (<div className="button-group"><button className="control-btn" onClick={props.onPrevious}>上一张</button><button className="control-btn" onClick={props.onToggleAutoPlay}>{props.isAutoPlaying ? "停止" : "自动播放"}</button><button className="control-btn" onClick={props.onNext}>下一张</button></div>);// 渲染指示器小圆点const renderDots = () => (<div className="dots-container">{Array.from({ length: props.totalImages }).map((_, index) => (<divkey={index}className={`dot ${index === props.currentIndex ? "active" : ""}`}onClick={() => props.onDotClick(index)}/>))}</div>);return (<divclassName="image-container"onMouseEnter={props.onMouseEnter}onMouseLeave={props.onMouseLeave}>{/* 图片滑动容器 */}<div className={sliderClassName} style={sliderStyle}>{extendedImages.map((img, index) => (<imgkey={index}className="fullscreen-img"src={img}alt={`slide-${index}`}style={{ width: `${100 / (props.totalImages + 1)}%` }}/>))}</div>{/* 控制器容器 */}<div className="controls-container">{renderControls()}{renderDots()}</div></div>);
};

ImageContainer.css代码如下:

/* 容器样式 */
.image-container {position: relative;width: 100vw;height: 100vh;overflow: hidden;background-color: #000;
}/* 滑动容器样式 */
.image-slider {position: relative;height: 100%;display: flex;
}/* 滑动过渡效果 */
.image-slider.sliding {transition: transform 0.5s ease-out;
}/* 图片样式 */
.fullscreen-img {height: 100%;object-fit: cover;flex-shrink: 0;
}/* 控制器容器样式 */
.controls-container {position: fixed;bottom: 40px;left: 0;right: 0;display: flex;flex-direction: column;align-items: center;gap: 20px;z-index: 10;
}/* 按钮组样式 */
.button-group {display: flex;gap: 20px;
}.control-btn {padding: 10px 20px;border: none;border-radius: 20px;background-color: rgba(0, 0, 0, 0.5);color: white;cursor: pointer;transition: background-color 0.3s;font-size: 14px;
}.control-btn:hover {background-color: rgba(0, 0, 0, 0.8);
}/* 指示器样式 */
.dots-container {display: flex;gap: 12px;
}.dot {width: 12px;height: 12px;border-radius: 50%;background-color: rgba(255, 255, 255, 0.5);cursor: pointer;transition: all 0.3s;
}.dot:hover:not(.active) {background-color: rgba(255, 255, 255, 0.7);transform: scale(1.1);
}.dot.active {background-color: white;transform: scale(1.1);
}

相关文章:

React实现无缝滚动轮播图

实现效果&#xff1a; 由于是演示代码&#xff0c;我是直接写在了App.tsx里面在 文件位置如下&#xff1a; App.tsx代码如下&#xff1a; import { useState, useEffect, useCallback, useRef } from "react"; import { ImageContainer } from "./view/ImageC…...

deepseek+mermaid【自动生成流程图】

成果&#xff1a; 第一步打开deepseek官网(或百度版&#xff08;更快一点&#xff09;)&#xff1a; 百度AI搜索 - 办公学习一站解决 第二步&#xff0c;生成对应的Mermaid流程图&#xff1a; 丢给deepseek代码&#xff0c;或题目要求 生成mermaid代码 第三步将代码复制到me…...

分布式锁的简单实现

1. 什么是分布式锁&#xff1f; 在分布式系统中&#xff0c;也会涉及到多个节点访问同一个公共资源的情况&#xff0c;和 Java 中多线程的锁不一样&#xff0c;由于分布式系统中涉及到多个进程多个主机&#xff0c;所以说 Java 中 synchronized 就不合适了。 2. 分布式锁的简…...

C语言(19)----------->函数(2)

本文介绍了C语言的return语句及其它在C语言函数中的作用&#xff0c;以及介绍了二维数组和一维数组传参时的一些注意事项和使用数组传参时的方法。 若没有学习过C语言的一维数组和二维数组&#xff0c;建议参考如下文章&#xff1a; C语言&#xff08;15&#xff09;--------…...

动态扩缩容引发的JVM堆内存震荡:从原理到实践的GC调优指南

目录 一、典型案例&#xff1a;系统发布后的GC雪崩事件 &#xff08;一&#xff09;故障现象 1. 刚刚启动时 GC 次数较多 2. 堆内存锯齿状波动 3. GC日志特征&#xff1a;Allocation Failure &#xff08;二&#xff09;问题定位 二、原理深度解析&#xff1a;JVM内存弹…...

为何在用户注销时使用 location.href 而非 Vue Router 的 router.push

在开发 Web 应用时&#xff0c;用户注销功能的设计看似简单&#xff0c;但背后隐藏着对状态管理、安全性和用户体验的深层考量。以下将详细探讨为何许多项目在注销跳转时选择 location.href&#xff08;强制刷新页面&#xff09;而非 Vue Router 的 router.push&#xff08;单页…...

开源工具推荐:Uptime Kuma监控

1. 概述 Github&#xff1a;louislam/uptime-kuma: A fancy self-hosted monitoring tool Uptime Kuma is an easy-to-use self-hosted monitoring tool. Uptime Kuma 是一款开源的监控工具&#xff0c;可以帮助你实时监测网站或服务的状态&#xff0c;并在发生故障时及时通…...

《基于Selenium的论坛系统自动化测试实战报告》

一、项目背景与技术选型 项目简介 目标系统&#xff1a;论坛系统 核心功能&#xff1a;用户注册/登录、会话框发送信息、好友列表、信息发送 技术栈&#xff1a;html Springboot MySQL数据库 为什么选择Selenium 支持多浏览器兼容性测试&#xff08;Chrome/Firefox/Edge&…...

深入解析SQL Server高级SQL技巧

SQL Server 是一种功能强大的关系型数据库管理系统&#xff0c;广泛应用于各种数据驱动的应用程序中。在开发过程中&#xff0c;掌握一些高级SQL技巧&#xff0c;不仅能提高查询性能&#xff0c;还能优化开发效率。这篇文章将全面深入地探讨SQL Server中的一些高级技巧&#xf…...

分布式中间件:环境准备

在当今数字化的时代&#xff0c;分布式系统已经成为了开发领域的主流。分布式中间件在其中扮演着至关重要的角色&#xff0c;它能够帮助我们更好地处理高并发、高可用等复杂的业务场景。在这个系列的博客中&#xff0c;我将带大家深入学习分布式中间件的相关知识&#xff0c;主…...

c# winform程序 vs2022 打包生成安装包

最近&#xff0c;利用c# winform程序该客户开发一套进销存管理系统&#xff0c;项目在部署前&#xff0c;需要生成安装包&#xff0c;以便部署在客户电脑上面。总结步骤如下&#xff1a; 1、在打包之前 (VS中需要包括Microsoft visual studio installer projects扩展项目)&…...

探索Elasticsearch:文档的CRUD

在企业环境中&#xff0c;Elasticsearch对文档操作的支持不仅是实现高效搜索的关键&#xff0c;更是数据驱动决策的重要支柱。它通过强大的索引机制和灵活的查询语言&#xff0c;使企业能够实时处理和分析海量文档数据&#xff0c;迅速获取有价值的洞察&#xff0c;从而加速创新…...

面试基础--Spring Boot启动流程及源码实现

深度解析Spring Boot启动流程及源码实现 一、Spring Boot启动全景图&#xff08;含核心阶段&#xff09; #mermaid-svg-dYTQ6WPa3o6vKFHh {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dYTQ6WPa3o6vKFHh .error-i…...

火语言RPA--PDF提取图片

【组件功能】&#xff1a;提取PDF文档指定位置图片 配置预览 配置说明 文件路径 支持T或# 默认FLOW输入项 待提取图片的PDF文件的完整路径。 提取位置 全部、指定页、指定范围3种位置供选择。 PDF文件密码 支持T或# 打开PDF文件的密码。 页码 支持T或# 提取指定页的页…...

力扣977.有序数组的平方(双指针)

给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 方法一&#xff1a;直接将每个元素的平方压入ans数组中&#xff0c;再对ans数组进行排序 class Solution { public:vector<int> sort…...

QT——文件IO

QFile 类 构造函数 QFile() 无参构造 仅仅构建一个QFile 对象&#xff0c;不设定文件名 QFile(文件名) 构建一个QFile对象的同时&#xff0c;设定文件名 但是注意&#xff0c;仅仅设定文件名&#xff0c;并不会打开该文件 设定文件名 QFile file file.setFileName…...

分布式中间件:Redis介绍

目录 Redis 概述 Redis 的特点 高性能 丰富的数据结构 持久化 分布式特性 简单易用 Redis 的数据结构 字符串&#xff08;String&#xff09; 哈希&#xff08;Hash&#xff09; 列表&#xff08;List&#xff09; 集合&#xff08;Set&#xff09; 有序集合&…...

服务器和本地电脑之间如何传输文件

在服务器和本地电脑之间传输文件可以通过多种方式实现&#xff0c;常见的方法包括使用 SFTP&#xff08;安全文件传输协议&#xff09;、SCP&#xff08;安全复制协议&#xff09;、FTP&#xff08;文件传输协议&#xff09;、rsync、以及 云存储 等工具。以下是几种常见的方法…...

经验分享:用一张表解决并发冲突!数据库事务锁的核心实现逻辑

背景 对于一些内部使用的管理系统来说&#xff0c;可能没有引入Redis&#xff0c;又想基于现有的基础设施处理并发问题&#xff0c;而数据库是每个应用都避不开的基础设施之一&#xff0c;因此分享个我曾经维护过的一个系统中&#xff0c;使用数据库表来实现事务锁的方式。 之…...

嵌入式学习前要了解的基础知识

一、电压和电流 在嵌入式开发中&#xff0c;电压和电流是两个基本的电气概念&#xff0c;对于理解和设计电子电路至关重要。它们直接影响到嵌入式系统的性能、功耗、可靠性和安全性。 电压&#xff08;Voltage&#xff09; 电压是电场力推动电荷移动的能力&#xff0c;通常以…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...