当前位置: 首页 > 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;通常以…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...