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

【轮播图】H5端轮播图、横向滑动、划屏效果实现方案——Vue3+CSS position

文章目录

  • 前置知识
    • CSS: touch-action属性
    • CSS: transform属性
    • 触摸事件
    • forEach
    • 回调占位符
  • 定位实现滑屏效果
    • 准备阶段
    • 实现移动效果
    • 实现跟手效果
    • 触摸结束优化
    • 完整代码

    本文将详细介绍如何使用Vue3实现移动端流畅的划屏交互效果。我们将深入探讨触摸事件处理、页面动态切换和动画优化的完整实现方案。文章将重点解析三个关键方法: handleTouchStart记录触摸起点、 handleTouchMove实现实时跟手效果、 handleTouchEnd处理页面切换逻辑,特别会讲解 pages.forEach循环在重置页面位置和恢复过渡动画中的重要作用。同时会分享性能优化技巧,包括transform动画的优势、直接DOM操作与Vue响应式的平衡,以及滑动阈值的合理设置,帮助开发者掌握移动端滑动交互的核心实现原理。

前置知识

CSS: touch-action属性

touch-action 是 CSS 的一个属性,用来控制元素如何响应触摸操作(如滑动、缩放等)。

常见取值:

  • auto:默认,浏览器自行处理触摸行为。
  • none:完全禁用浏览器默认的触摸行为(如滚动、缩放)。
  • pan-x:允许水平滑动,禁止垂直滑动和缩放。
  • pan-y:允许垂直滑动,禁止水平滑动和缩放。
  • manipulation:允许滑动,但禁用双击缩放(提高响应速度)。

示例:

/* 禁用默认触摸行为,防止滚动 */
.prevent-scroll {touch-action: none;
}/* 只允许垂直滑动 */
.vertical-only {touch-action: pan-y;
}

适用场景:

  • 自定义触摸手势(如画板、游戏)。
  • 防止滑动冲突(如地图内嵌滚动列表)。

简单说,它让开发者能精细控制触摸交互,避免浏览器默认行为的干扰

CSS: transform属性

transform: 是 CSS 中**把元素当成一张图来变形**的属性。
一次可以写多个 2D/3D 函数,空格隔开,按从左到右的顺序逐个执行。

  1. 常用 2D 函数
函数说明例子
translate(x, y)平移transform: translate(20px, -10px)
translateX(x) / translateY(y)单轴平移translateX(50%)
scale(sx, sy)缩放scale(1.2, .8)
rotate(angle)旋转rotate(45deg)
skew(ax, ay)倾斜skew(30deg, 10deg)
  1. 常用 3D 函数
函数说明
translate3d(x,y,z)三维平移
scale3d(sx,sy,sz)三维缩放
rotateX(a) / rotateY(a) / rotateZ(a)绕各轴旋转
  1. 组合示例
.box {transform: translateX(100px) rotate(45deg) scale(1.2);
}

先右移 100 px,再旋转 45°,最后放大 1.2 倍。

  1. 性能与注意点
  • 不占文档流:变形后原位置仍保留(不像 position:absolute 会脱离)。
  • 硬件加速:大多数浏览器对 transform 开 GPU 加速,动画较流畅。
  • 不影响兄弟元素:不会像 margin 那样推挤别人。

触摸事件

HTML 的触摸事件有 4 个核心,按触发顺序:

  • touchstart:手指刚碰到屏幕
  • touchmove:手指在屏幕上滑动(连续触发)
  • touchend:手指离开屏幕
  • touchcancel:系统中断(如来电、弹窗)

每个事件对象里都带 touches / changedTouches 等列表,可拿到触点坐标。

事件对象属性:

  • touches:当前屏幕上所有接触点集合。
  • targetTouches:事件绑定元素上的接触点集合。
  • changedTouches:触发此事件时状态改变的接触点集合(touchstart时即新出现的点)。

forEach

forEach 是数组的每个元素都跑一次的方法——给它一个回调函数,它把数组里的元素挨个传进去执行,不返回新数组,纯粹“副作用”。

基本语法:

array.forEach((value, index, array) => {/* 干点啥 */
});
  • value:当前元素
  • index:当前下标(可选)
  • array:原数组本身(几乎不用)

示例:

['a','b','c'].forEach((v,i)=>console.log(i,v));
// 0 a
// 1 b
// 2 c

回调占位符

在 JavaScript 的 forEach 回调里:

pages.forEach((_, i) => { ... })

_ 就是占位符:

  • 含义:“这个位置的参数我不打算用,随便起个名字占坑”。

  • 这里_对应的是数组元素(page 对象),但循环体里只用索引 i,所以用_表示“忽略它”。

这是一种常见习惯写法,读代码的人一看就明白“这个值被故意忽略了”。


定位实现滑屏效果

实现了一个典型的移动端页面滑动容器,具有以下特点:

  • 支持左右滑动切换不同颜色的页面
  • 滑动时有实时跟手效果
  • 滑动结束后有平滑的过渡动画
  • 边界检测防止越界

准备阶段

基本结构代码:

<template><divclass="page-container"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"><!-- 页面内容 --><divv-for="(page, index) in pages":key="page.id"class="page":style="{ backgroundColor: page.color }">{{ page.name }}</div></div>
</template><script setup>
import { ref } from "vue";const pages = [{ color: "#ff7675", name: "屏幕 1", id: 0 },{ color: "#74b9ff", name: "屏幕 2", id: 1 },{ color: "#55efc4", name: "屏幕 3", id: 2 },{ color: "#a29bfe", name: "屏幕 4", id: 3 },{ color: "#ffcb20", name: "屏幕 5", id: 4 },
];// 触摸开始
const handleTouchStart = (e) => {};// 触摸移动
const handleTouchMove = (e) => {};// 触摸结束
const handleTouchEnd = () => {};</script><style scoped>
.page-container {}
.page {}
</style>

目前没有样式的效果如下:
在这里插入图片描述
所有内容都是堆叠在一起的,而目前我们想要的效果是每个屏幕沾满一个页面,然后通过滑动来切换,

先给内容来个沾满全屏的宽度高度

.page-container {width: 100vw;height: 100vh;
}
.page {width: 100%;height: 100%;
}

在这里插入图片描述
在处理一下page中的文字样式吧:

.page {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;font-size: 50px;font-weight: 700;color: aliceblue;
}

在这里插入图片描述
现在所有页面都是呈现竖向排列的,与我们横向滑动的效果相违背,所以需要使用定位效果将 所有页面重叠在一起,然后再做移动效果来实现划屏效果

.page-container {width: 100vw;height: 100vh;position: relative;overflow: hidden;touch-action: none;
}
.page {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;font-size: 50px;font-weight: 700;color: aliceblue;position: absolute;
}

在这里插入图片描述

现在,所有屏幕都堆叠在一起了! 接下来使用transform属性让所有屏幕从左至右以此排列:
在这里插入图片描述

:style="{transform: `translateX(${index * 100}%)`,backgroundColor: page.color,}"

可能看不出效果,我们可以调小page 的宽高查看:width: 20%; height: 20%;
在这里插入图片描述

实现移动效果

接下来的内容就是重点啦!🫵💯

移动实现原理:视窗不变,让每个page分别向负方向移动
在这里插入图片描述

<!-- 页面内容 --><divv-for="(page, index) in pages":key="page.id"class="page":style="{transform: `translateX(${index * 100 - currentIndex * 100}%)`,backgroundColor: page.color,}">{{ page.name }}</div>// 声明一个currentIndex 
import { ref } from "vue";
const currentIndex = ref(0); // 当前页面索引

可以通过改变const currentIndex = ref(2);中的变量来查看页面效果。
在这里插入图片描述
晓得了切换原理,接下来实现触摸事件啦🫴
流程示意图:
在这里插入图片描述

const currentIndex = ref(0); // 当前页面索引
const startX = ref(0);
const moveX = ref(0);
// 触摸开始:记录起点
const handleTouchStart = (e) => {startX.value = e.touches[0].clientX;
};// 触摸移动:实时跟随手指滑动
const handleTouchMove = (e) => {moveX.value = e.touches[0].clientX - startX.value;
};// 触摸结束:判断滑动方向并切换页面
const handleTouchEnd = () => {if (Math.abs(moveX.value)) {if (moveX.value > 0 && currentIndex.value > 0) {currentIndex.value--; // 向右滑,上一页} else if (moveX.value < 0 && currentIndex.value < pages.length - 1) {currentIndex.value++; // 向左滑,下一页}}
};

现在就可以通过滑动屏幕来实现切换效果了:
在这里插入图片描述

触发的有点灵敏,我们给handleTouchEnd添加一个滑动阈值

const handleTouchEnd = () => {const threshold = 50; // 滑动阈值(像素)if (Math.abs(moveX.value) > threshold) {if (moveX.value > 0 && currentIndex.value > 0) {currentIndex.value--; // 向右滑,上一页} else if (moveX.value < 0 && currentIndex.value < pages.length - 1) {currentIndex.value++; // 向左滑,下一页}}
};

再添加一个css平滑过渡效果:

.page {//...transition: transform 0.3s ease; /* 平滑过渡 */
}

在这里插入图片描述

实现跟手效果

现在就要点丝滑😻效果了,不过还不够还要添加跟手效果

  <!-- 页面内容 -->
<divv-for="(page, index) in pages":key="page.id"class="page"ref="pageRef":style="{transform: `translateX(${index * 100 - currentIndex * 100}%)`,backgroundColor: page.color,}"
>{{ page.name }}
</div>const pageRef = ref(null);
// 触摸移动:实时跟随手指滑动
const handleTouchMove = (e) => {moveX.value = e.touches[0].clientX - startX.value;// 跟手效果pages.forEach((_, i) => {const page = pageRef.value[i];if (page) {page.style.transform = `translateX(${i * 100 - currentIndex.value * 100 + moveX.value / 10}%)`;page.style.transition = "none"; // 禁用过渡效果,保证跟手效果流畅}});
};

在这里插入图片描述

实现原理详解

  1. 获取移动距离
    • moveX.value = e.touches[0].clientX - startX.value 计算出手指从触摸开始到当前位置的水平移动距离
  2. 实时更新页面位置
    • 遍历所有页面元素,为每个页面计算新的transform
    • 基础位置:i * 100 - currentIndex.value * 100 确保页面按顺序排列
    • 跟手偏移:+ moveX.value / 10 添加移动距离的1/10作为跟手效果(除以10是为了降低跟手灵敏度)
  3. 禁用过渡效果
    • page.style.transition = "none" 临时禁用CSS过渡效果,确保跟手时的即时响应

触摸结束优化

// 触摸结束:判断滑动方向并切换页面
const handleTouchEnd = () => {//....// 重置位置并启用过渡动画pages.forEach((_, i) => {const page = document.querySelectorAll(".page")[i];if (page) {page.style.transform = `translateX(${i * 100 - currentIndex.value * 100}%)`;page.style.transition = "transform 0.3s ease";}});moveX.value = 0;
};

handleTouchEnd 里那段 pages.forEach(...) 只有一句话:把 5 张“页面”一次性摆到正确位置,并补上过渡动画

  1. 计算目标位置

    i * 100 - currentIndex.value * 100
    
    • i * 100:第 i 张默认排在“第 i 屏”位置(0%、100%、200% …)。
    • 减去 currentIndex * 100:把当前要显示的那一页拉回 0%(即屏幕正中)。
      结果:所有页瞬间排成一排,当前页居中,其余页在左右两侧。
  2. 设置样式

    • transform 赋刚才算出的值,让页面“归位”。
    • transition = "transform 0.3s ease":把被 handleTouchMove 关掉的过渡重新打开,回弹时有动画。
  3. 重置 moveX.value = 0:为下一次手势准备。

最后得到的效果如下:( 对此我们的轮播图实现效果也完成啦🎉)
在这里插入图片描述

完整代码

<template><divclass="page-container"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"><!-- 页面内容 --><divv-for="(page, index) in pages":key="page.id"class="page"ref="pageRef":style="{transform: `translateX(${index * 100 - currentIndex * 100}%)`,backgroundColor: page.color,}">{{ page.name }}</div></div>
</template><script setup>
import { ref } from "vue";const pages = [{ color: "#ff7675", name: "屏幕 1", id: 0 },{ color: "#74b9ff", name: "屏幕 2", id: 1 },{ color: "#55efc4", name: "屏幕 3", id: 2 },{ color: "#a29bfe", name: "屏幕 4", id: 3 },{ color: "#ffcb20", name: "屏幕 5", id: 4 },
];
const currentIndex = ref(0); // 当前页面索引
const startX = ref(0);
const moveX = ref(0);
// 触摸开始:记录起点
const handleTouchStart = (e) => {startX.value = e.touches[0].clientX;
};const pageRef = ref(null);
// 触摸移动:实时跟随手指滑动
const handleTouchMove = (e) => {moveX.value = e.touches[0].clientX - startX.value;// 跟手效果pages.forEach((_, i) => {const page = pageRef.value[i];if (page) {page.style.transform = `translateX(${i * 100 - currentIndex.value * 100 + moveX.value / 10}%)`;page.style.transition = "none"; // 禁用过渡效果,保证跟手效果流畅}});
};// 触摸结束:判断滑动方向并切换页面
const handleTouchEnd = () => {const threshold = 200; // 滑动阈值(像素)if (Math.abs(moveX.value) > threshold) {if (moveX.value > 0 && currentIndex.value > 0) {currentIndex.value--; // 向右滑,上一页} else if (moveX.value < 0 && currentIndex.value < pages.length - 1) {currentIndex.value++; // 向左滑,下一页}}// 重置位置并启用过渡动画pages.forEach((_, i) => {const page = pageRef.value[i];if (page) {page.style.transform = `translateX(${i * 100 - currentIndex.value * 100}%)`;page.style.transition = "transform 0.3s ease";}});moveX.value = 0;
};
</script><style scoped>
.page-container {width: 100vw;height: 100vh;position: relative;overflow: hidden;touch-action: none;
}
.page {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;font-size: 50px;font-weight: 700;color: aliceblue;position: absolute;transition: transform 0.3s ease; /* 平滑过渡 */
}
</style>
http://www.lryc.cn/news/611186.html

相关文章:

  • 从开发到售后:Q-Tester基于ODX标准的统一诊断平台
  • 闸机控制系统从设计到实现全解析:第 4 篇:Redis 缓存与分布式锁实现
  • STM32设置GPIO模式
  • Dify工作流三剑客:参数提取、变量赋值与聚合详解
  • Starrocks中的 Query Profile以及explain analyze及trace命令中的区别
  • Linux系统:基础I/O
  • 基于python的二手车价格预测及可视化系统,采用集成学习算法和diango框架
  • [按键精灵]
  • Pytorch基础入门2
  • AlmaLinux8 平替 manylinux_2_28-python 的 GPG密钥管理、安装 cuda sdk
  • gRPC Keepalive 机制详解与最佳实践
  • 微软Dragon Ambient eXperience (DAX) 深度解析
  • Linux 调度器函数sched_*系统调用及示例
  • Java JDBC连接池深度解析与实战指南
  • Transformer的并行计算与长序列处理瓶颈
  • Linux lvm逻辑卷管理
  • 猜数字游戏 Java
  • 【C++】模板深入进阶
  • Java技术栈/面试题合集(13)-网络篇
  • [Linux]学习笔记系列 -- [arm]boot
  • Android 之 Kotlin 和 MVVM 架构的 Android 登录示例
  • 腾讯云对象存储服务COS
  • QtPromise第三方库的介绍和使用
  • 人工智能领域、图欧科技、IMYAI智能助手2025年1月更新月报
  • ubuntu24中部署k8s 1.30.x-底层用docker
  • 相机拍摄的DNG格式照片日期如何修改?你可以用这款工具修改
  • Android异常信号处理详解
  • 【网络运维】Linux:系统启动原理与配置
  • Coze开源了!意味着什么?
  • 在Linux上部署RabbitMQ、Redis、ElasticSearch