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

文章目录

  • 定位实现滑屏效果
    • 前置知识
      • CSS: touch-action属性
      • CSS: transform属性
      • 触摸事件
      • forEach
      • 回调占位符
    • 准备阶段
    • 实现移动效果
    • 实现跟手效果
    • 触摸结束优化
    • 完整代码
  • 滚动实现滑屏效果
    • 前置知识
      • CSS: scroll-snap-type属性
    • 准备阶段
    • 实现滑动效果
    • 实现吸附效果
    • 滚动条隐藏
    • 存在问题
    • 完整代码
  • scrollLeft实现滑屏效果(最佳实践)
    • 前置知识
      • DOM: scrollLeft
      • DOM: scrollWidth
      • @scroll事件
    • 准备阶段

    本文将详细介绍如何使用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>

使用节流优化性能

方法一: 通过lodash库

安装lodash:

npm install lodash-es
<script setup>
import { ref } from "vue";
import { throttle } from 'lodash-es'; // 或者使用自定义节流函数// ... existing code ...const handleTouchMove = throttle((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";}});
}, 16); // 约60fps的间隔// ... existing code ...
</script>

或者使用自定义的简单节流实现:

<script setup>
import { ref } from "vue";// ... existing code ...let lastTime = 0;
const handleTouchMove = (e) => {const now = Date.now();if (now - lastTime < 16) return; // 约60fpslastTime = now;moveX.value = e.touches[0].clientX - startX.value;// ... rest of the original code ...
};// ... existing code ...
</script>

滚动实现滑屏效果

前置知识

CSS: scroll-snap-type属性

scroll-snap-type 是 CSS 中的一个属性,用于控制滚动容器的滚动行为,特别是当用户滚动时,内容是否以及如何“吸附”到特定的停止点。
这个属性定义在滚动容器上,它决定了滚动的轴向以及吸附行为的严格程度。

基本语法:

scroll-snap-type: none | [ x | y | block | inline | both ] [ mandatory | proximity ]?;

取值说明:

  1. 轴向 (Axis)
    • none:不启用滚动吸附。这是默认值。
    • x:在水平轴上启用滚动吸附。
    • y:在垂直轴上启用滚动吸附。
    • block:在块级方向上启用吸附(在大多数书写模式下等同于 y)。
    • inline:在内联方向上启用吸附(在大多数书写模式下等同于 x)。
    • both:在两个轴上都启用吸附。
  2. 严格程度 (Strictness)
    • mandatory:强制吸附。当滚动操作结束时(例如用户松开鼠标、手指或滚动停止),视口必须停留在一个吸附点上。如果内容区域不足以滚动到下一个/上一个点,用户可能无法滚动。
    • proximity:邻近吸附。滚动操作结束时,如果当前视口位置足够接近一个吸附点,浏览器会自动滚动到该点。但如果用户停止在两个吸附点之间较远的位置,可能不会发生吸附。用户体验更灵活。

示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>滚动吸附示例</title><style>body {font-family: Arial, sans-serif;margin: 0;padding: 20px;}h1 {text-align: center;margin-bottom: 30px;}/* 垂直方向强制吸附 */.container {scroll-snap-type: y mandatory;overflow-y: scroll;height: 400px;border: 2px solid #333;margin-bottom: 40px;}/* 水平方向邻近吸附 */.horizontal-slider {display: flex;scroll-snap-type: x proximity;overflow-x: scroll;scroll-behavior: smooth;border: 2px solid #333;margin-bottom: 40px;}/* 子元素需要设置吸附点 */.item {scroll-snap-align: start;flex: 0 0 100%;height: 300px;display: flex;align-items: center;justify-content: center;font-size: 24px;font-weight: bold;color: white;}/* 垂直容器中的项目样式 */.container .item {height: 400px;}/* 为不同项目设置不同背景色 */.item:nth-child(1) {background-color: #FF5733;}.item:nth-child(2) {background-color: #33FF57;}.item:nth-child(3) {background-color: #3357FF;}.item:nth-child(4) {background-color: #F333FF;}</style>
</head>
<body><h1>滚动吸附效果演示</h1><h2>垂直滚动吸附(强制)</h2><div class="container"><div class="item">垂直项目 1</div><div class="item">垂直项目 2</div><div class="item">垂直项目 3</div><div class="item">垂直项目 4</div></div><h2>水平滚动吸附(邻近)</h2><div class="horizontal-slider"><div class="item">水平项目 1</div><div class="item">水平项目 2</div><div class="item">水平项目 3</div><div class="item">水平项目 4</div></div><p>说明:垂直滚动容器设置了强制吸附(y mandatory),滚动结束后会精确停在项目顶部。水平滚动容器设置了邻近吸附(x proximity),滚动结束后可能会停在项目附近。</p>
</body>
</html>

在这里插入图片描述

准备阶段

基本结构代码:

<template><div class="scroll-container"><!-- 页面内容 --><divv-for="(page, index) in pages":key="page.id"class="page":style="{ backgroundColor: page.color }">{{ page.name }}</div></div>
</template><script setup>
import { ref, onMounted } 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 },
];</script><style scoped>
.scroll-container {
}
.page {
}
</style>

和上面步骤一样,同样的给内容来个沾满全屏的宽度高度以及page重点文字样式:

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

实现滑动效果

接下来的布局就是和上面不同的时候啦🤗,我们要给scroll-container一个flex属性,让所有的子元素横向排列,排列完之后大家会发现page子元素并没有想我们想想中的那样沾满这个页面以此排列,而是都挤在一个页面了。

在这里插入图片描述
这是因为flex布局的缘由,使得每个page的宽度被压缩了。要让页面不被压缩,需要修改 .page 的样式,将 flex-shrink: 0 添加到样式中,这样 flex 容器就不会压缩这些页面了。

.scroll-container {/*...*/overflow-x: auto;
}
.page {/*...*/flex-shrink: 0; /* 添加这行防止页面被压缩 */
}

现在就解决了页面布局问题啦😄。

实现吸附效果

添加上scroll-snap-type属性,让滚动条有吸附效果:

.scroll-container {/*...*/scroll-snap-type: x mandatory;-webkit-overflow-scrolling: touch; /* 启用平滑滚动 */
}
.page {/*...*/flex-shrink: 0; /* 添加这行防止页面被压缩 */scroll-snap-align: start;
}

划划屏幕,是不是发现现在就已经实现了我们之前想要实现的划屏效果啦😲!没错,就是这么简单,几乎纯CSS样式就可以搞定啦🤩!

滚动条隐藏

接下来优化一下下细节,先来去掉底部下方的横向滚动条:
在这里插入图片描述
要去掉横向滚动条,可以修改 .scroll-container 的样式,将 overflow-x 从 auto 改为 hidden。

.scroll-container {/*...*//* 使用浏览器私有伪元素隐藏滚动条​ */scrollbar-width: none; /* Firefox */-ms-overflow-style: none; /* IE/Edge */
}
/* WebKit 浏览器:隐藏滚动条但保留滚动功能 */
.scroll-container::-webkit-scrollbar {display: none; /* Chrome/Safari/Opera */
}

存在问题

可以很清晰的观察到一个现象——当用力滑动时会一下子跳过多页。
在这里插入图片描述

这是因为 scroll-snap-type: x mandatory 在惯性滚动下的典型表现。浏览器的滚动捕捉机制虽然会强制最终停在某个捕捉点上,但它不会限制滚动的“速度”或“距离”。当你用力快速滑动时,系统会产生很大的惯性,滚动容器会高速“掠过”中间的捕捉点,最终可能停在更远的一个点上。
遗憾的是,纯 CSS 目前没有直接的属性可以限制“每次滚动只移动一个捕捉点”。mandatory 只保证“停在点上”,不保证“只移动一步”。小编这里也是还没有找到适合的实现方式👉👈,就当作是这个方法的缺陷吧😖。

完整代码

<template><div class="scroll-container" ><!-- 页面内容 --><divv-for="(page, index) in pages":key="page.id"class="page":style="{ backgroundColor: page.color }">{{ page.name }}</div></div>
</template><script setup>
import { ref, onMounted } 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 },
];</script><style scoped>
.scroll-container {height: 100vh;width: 100vw;display: flex;overflow-x: auto;scroll-snap-type: x mandatory;-webkit-overflow-scrolling: touch; /* 启用平滑滚动 *//* 使用浏览器私有伪元素隐藏滚动条​ */scrollbar-width: none; /* Firefox */-ms-overflow-style: none; /* IE/Edge */
}
/* WebKit 浏览器:隐藏滚动条但保留滚动功能 */
.scroll-container::-webkit-scrollbar {display: none; /* Chrome/Safari/Opera */
}
.page {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;font-size: 50px;font-weight: 700;color: aliceblue;flex-shrink: 0; /* 添加这行防止页面被压缩 */scroll-snap-align: start;
}
</style>

scrollLeft实现滑屏效果(最佳实践)

为了解决上面使用滚动实现的划屏效果痛点,我们不得不抛弃CSS属性,该用JS来实现我们想要的效果。具体是那种JS方法呢? 其实你已经学过了,就是第一种中的方法,通过触摸事件来控制屏幕滑动,不过这一次我们不是控制定位位置来改变位置,而是通过控制横向滚动条的滚动位置来改变位置。

前置知识

DOM: scrollLeft

scrollLeft 是一个 JavaScript DOM 属性,它用于获取或设置一个可滚动元素(如 div, body 等)的水平滚动条相对于最左侧的偏移量。

作用:

  • 获取:读取元素当前水平滚动多少像素。
  • 设置:将元素的水平滚动移动到指定的位置。

基本语法:

// 获取元素当前的水平滚动偏移量(像素)
let currentScroll = element.scrollLeft;// 设置元素的水平滚动偏移量(像素)
element.scrollLeft = value;
  • element:一个具有滚动能力的 DOM 元素(即其 overflow 属性为 autoscroll,并且内容超出其宽度)。
  • value:一个非负的数值,表示希望滚动条距离最左侧的像素数。

PS:

  • 一个元素可滚动的最大 scrollLeft 值可以通过 element.scrollWidth - element.clientWidth 计算得出。
  • scrollTop 用于垂直方向的滚动。

实例:

<div id="scroller" style="width: 200px; overflow-x: auto; white-space: nowrap;"><span>这是一个很长很长很长很长很长很长的文本行,需要水平滚动才能看完。</span>
</div>
<button onclick="scrollToRight()">滚动到底部</button>
const scroller = document.getElementById('scroller');// 获取当前滚动位置
console.log('当前水平滚动位置:', scroller.scrollLeft); // 例如: 0// 将滚动条滚动到最右侧
scroller.scrollLeft = scroller.scrollWidth - scroller.clientWidth;// 或者滚动到特定位置
scroller.scrollLeft = 50;// 滚动一定距离(例如向右滚动 20 像素)
scroller.scrollLeft += 20;// 检测是否滚动到了最右边
if (scroller.scrollLeft >= scroller.scrollWidth - scroller.clientWidth) {console.log('已滚动到底部!');
}

DOM: scrollWidth

scrollWidth 是一个 只读 的 JavaScript DOM 属性,它返回一个元素的内容(包括由于溢出而不可见的部分)的整个宽度,以像素为单位。
简单来说,scrollWidth 告诉你这个元素的“内容”到底有多宽,即使这些内容因为容器大小限制而被隐藏(溢出)了。
“scrollWidth = 可视内容宽 + 被卷起来的内容宽”

  • 内容区域的总宽度(包括由于溢出而在视口外不可见的部分)。
  • 包含 元素的 padding,不包含 border、margin、垂直滚动条的宽度。
  • 如果元素没有溢出,scrollWidth 与 clientWidth(可见区域宽度)通常相等;出现水平溢出时,scrollWidth > clientWidth

与相关属性的对比(横向):

属性包含内容包含 padding包含 border包含 margin包含隐藏部分
scrollWidth
clientWidth
offsetWidth

垂直方向把 “Width” 换成 “Height” 即可。

@scroll事件

@scroll 是 Vue.js 框架中的一个事件监听修饰符,用于监听 DOM 元素上的 scroll 事件。
基本语法:

<template><!-- 监听某个元素的滚动 --><div @scroll="handleScroll" class="scrollable-container"><!-- 可滚动的内容 --></div><!-- 或者监听整个窗口的滚动 (通常在 mounted 钩子中用 addEventListener) --><!-- 但在 Vue 模板中,@scroll 通常用于具体元素 -->
</template><script>
export default {methods: {handleScroll(event) {// event 是原生的 UIEvent 对象console.log('滚动了!');// 获取滚动元素const element = event.target;// 获取滚动位置console.log('scrollTop:', element.scrollTop);console.log('scrollLeft:', element.scrollLeft);// 其他滚动相关的逻辑...}}
}
</script>

核心作用:@scroll 事件在元素的滚动条滚动时被触发

它可以用于:

  • 监听滚动位置:获取 scrollTopscrollLeft,判断用户滚动到了哪里。
  • 实现懒加载:当用户滚动到页面底部或某个区域附近时,动态加载更多内容。
  • 实现吸顶效果:当页面滚动超过某个元素的位置时,改变该元素的样式(如 position: fixed)。
  • 检测滚动方向:通过比较前后两次的 scrollTop 值来判断用户是向上还是向下滚动。
  • 滚动动画控制:根据滚动位置触发动画或改变元素状态。

准备阶段

基本结构代码:

<template><divclass="scroll-container"ref="containerRef"@touchstart="touchstart"@touchend="touchend"><!-- 页面内容 --><divv-for="(page, index) in pages":key="page.id"class="page":style="{ backgroundColor: page.color }">{{ page.name }}</div></div>
</template><script setup>
import { ref, onMounted } 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 containerRef = ref(null);const touchstart = (e) => {};
const touchend = (e) => {};
</script><style scoped>
.scroll-container {height: 100vh;width: 100vw;display: flex;overflow-x: auto;scroll-snap-type: x mandatory;-webkit-overflow-scrolling: touch; /* 启用平滑滚动 *//* 使用浏览器私有伪元素隐藏滚动条​ */scrollbar-width: none; /* Firefox */-ms-overflow-style: none; /* IE/Edge */touch-action: none; /* 禁止触摸动作 */
}
/* WebKit 浏览器:隐藏滚动条但保留滚动功能 */
.scroll-container::-webkit-scrollbar {display: none; /* Chrome/Safari/Opera */
}
.page {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;font-size: 50px;font-weight: 700;color: aliceblue;flex-shrink: 0; /* 添加这行防止页面被压缩 */scroll-snap-align: start;
}
</style>

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

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

相关文章

忘记了WordPress管理员密码的找回方法

WordPress管理员密码找回方法 如果您忘记了WordPress管理员密码&#xff0c;可以通过以下几种方法找回或重置&#xff1a; 方法1&#xff1a;通过电子邮件重置(最简单) 访问您的WordPress登录页面(通常是wodepress.com/wp-admin或wodepress.com/wp-login.php) 点击”忘记密…

RAFT:让语言模型更聪明地用文档答题

RAFT&#xff1a;让语言模型更聪明地用文档答题 作者注&#xff1a; 本文旨在面向零基础读者介绍 UC Berkeley 提出的 RAFT&#xff08;Retrieval-Augmented Fine-Tuning&#xff09;方法。它是一种训练语言模型的新方式&#xff0c;让模型更好地利用“外部知识”——比如文档、…

【紧急预警】NVIDIA Triton推理服务器漏洞链可导致RCE!

2025 年 8 月 4 日消息&#xff0c;NVIDIA 旗下的 Triton 推理服务器&#xff08;一款支持 Windows 和 Linux 系统、用于大规模运行 AI 模型的开源平台&#xff09;被曝出一系列安全漏洞。这些漏洞一旦被利用&#xff0c;攻击者有可能完全接管存在漏洞的服务器。 Wiz 安全公司…

基于深度学习的医学图像分析:使用PixelCNN实现医学图像生成

前言 医学图像分析是计算机视觉领域中的一个重要应用&#xff0c;特别是在医学图像生成任务中&#xff0c;深度学习技术已经取得了显著的进展。医学图像生成是指通过深度学习模型生成医学图像&#xff0c;这对于医学研究、疾病模拟和图像增强等任务具有重要意义。近年来&#x…

React ahooks——副作用类hooks之useDebounceFn

useDebounceFn 是 ahooks 提供的用于函数防抖的 Hook&#xff0c;它可以确保一个函数在连续触发时只执行最后一次。一、基本用法import { useDebounceFn } from ahooks; import { Button } from antd;const Demo () > {const { run } useDebounceFn(() > {console.log(…

【机器学习深度学习】 知识蒸馏

目录 前言 一、什么是知识蒸馏&#xff1f; 二、知识蒸馏的核心意义 2.1 降低算力与成本 2.2 加速推理与边缘部署 2.3 推动行业应用落地 2.4 技术自主可控 三、知识蒸馏的本质&#xff1a;大模型的知识传承 四、知识蒸馏的“四重红利” 五、DeepSeek的知识蒸馏实践 …

Python高级编程与实践:Python高级数据结构与编程技巧

高级数据结构&#xff1a;掌握Python中的高效编程技巧 学习目标 通过本课程&#xff0c;学员将深入了解Python中的高级数据结构&#xff0c;包括列表推导式、字典推导式、集合推导式和生成器表达式。学员将学习如何利用这些结构来编写更简洁、更高效的代码&#xff0c;并了解它…

【C++】Stack and Queue and Functor

本文是小编巩固自身而作&#xff0c;如有错误&#xff0c;欢迎指出&#xff01;本次我们介绍STL中的stack和queue和其相关的一些容器和仿函数一.stack and queue1.适配器stack和queue其实不是真正意义上的容器&#xff0c;而是容器适配器&#xff0c;而容器适配器又是什么呢&am…

Python爬虫实战:研究OpenCV技术构建图像数据处理系统

1. 引言 1.1 研究背景 在当今数字化时代,图像作为一种重要的信息载体,广泛存在于各类网站、社交媒体和在线平台中。这些图像数据涵盖了从自然风光、人物肖像到商品展示、新闻事件等丰富内容,为数据分析和模式识别提供了宝贵的资源。随着计算机视觉技术的快速发展,对大规模…

电感矩阵-信号完整性分析

电感矩阵:正如电容矩阵用于存储许多信号路径和返回路径的所有电容量&#xff0c;我们也需要一个矩阵存储许多导线的回路自感和回路互感值。需要牢记的是&#xff0c;这里的电感元件是回路电感。当信号沿传输线传播时&#xff0c;电流回路沿信号路径传输&#xff0c;然后立即从返…

JUC相关知识点总结

Java JUC&#xff08;java.util.concurrent&#xff09;是Java并发编程的核心工具包&#xff0c;提供了丰富的并发工具类和框架。以下是JUC的主要知识点&#xff0c;按难易程度分类&#xff0c;供你参考&#xff1a; 1. 基础概念与工具类 1.1 并发与并行&#xff08;易&#x…

激光频率梳 3D 测量方案革新:攻克光学扫描遮挡,130mm 深孔测量精度达 2um

一、深孔测量的光学遮挡难题在精密制造领域&#xff0c;130mm 级深孔&#xff08;如航空发动机燃油孔、模具冷却孔&#xff09;的 3D 测量长期受困于光学遮挡。传统激光扫描技术依赖直射光束&#xff0c;当深径比超过 10:1 时&#xff0c;孔壁中下部形成大量扫描盲区&#xff0…

clickhouse 中文数据的正则匹配

中文数据的正则匹配 在ClickHouse中,正则匹配通常用于数据的筛选、格式化等操作。以下是一些常用的正则匹配技巧: 1. 匹配中文字符 要匹配中文字符,可以使用以下正则表达式: SELECT * FROM my_table WHERE my_column REGEXP [\\x{4e00}-\\x{9fa5}];这里的 \\x{4e00}-\\…

[驱动开发篇] Can通信进阶 --- CanFD 的三次采样

驱动开发篇] Can通信进阶 --- Can报文的三次采样一、CAN FD的采样次数1.1. 标准规定1.2. 传统标准CAN采样1.3. CAN FD的采样策略1.3.1. 基础采样策略1.4. 配置位置1.5. 常见步骤二、CAN FD与标准CAN在采样机制上的主要区别三、使用建议四. 芯片厂商实现4.1. 实际市面情况4.2. 例…

分布式文件系统06-分布式中间件弹性扩容与rebalance冲平衡

分布式中间件弹性扩容与rebalance冲平衡176_如果宕机的数据节点事后再次重启会发生什么事情&#xff1f;某个之前某个宕机的数据节点DataNode-A又重启后&#xff0c;肯定会再次注册&#xff0c;并进行全量上报的流程&#xff0c;此时&#xff0c;就会导致DataNode-A上的文件副本…

芯祥科技:工业/车规级BMS芯片厂商 规格选型对比

芯祥科技公司专注于工业和车规级BMS芯片&#xff0c;电源芯片及可编程模拟芯片的研发与销售&#xff0c;客户遍及新能源储能&#xff0c;汽车&#xff0c;电脑&#xff0c;服务器及电动工具等领域。并具有创业公司成功经验&#xff0c;平均具有逾17年以上的芯片研发和市场销售经…

莫队基础(Mo‘s algorithm)

莫队算法简介 莫队算法是一种用于高效处理离线区间查询问题的算法&#xff0c;由莫涛&#xff08;Mo Tao&#xff09;在2009年提出。其核心思想是通过对查询区间进行分块和排序&#xff0c;利用前一次查询的结果来减少计算量&#xff0c;从而将时间复杂度优化至接近线性。 莫…

板卡两个ADC,一个JESD204b sync正常,另一个JESD204B同步不上的问题

目录 1.问题来源: 2.问题分析 进一步测试表现: 抓取204B高速链路数据如上所示。 说明不是配置流程的问题 1.问题来源: 在工控机上和部分电脑上面出现时钟锁不住的现象,无法正常使用板卡。 经过分析,发现板卡上有两片ADC,其中一片的ADC的sync信号经过测量,是正常的,…

Android10 系统休眠调试相关

Android10 系统休眠调试相关实时打印休眠日志(实测好像没作用)&#xff1a;echo 1 > /sys/module/printk/parameters/console_suspend查看唤醒锁&#xff1a;cat sys/power/wake_lock msm8953_64:/ # cat sys/power/wake_lock PowerManager.SuspendLockout PowerManagerServ…

一文掌握Bard机器翻译,以及用python调用的4种方式(现已升级为 Gemini)

文章目录一、Bard机器翻译概述1.1. Bard机器翻译介绍1.2 Bard机器翻译的核心特点1.3 技术背景1.4 与同类模型对比二、Bard机器翻译案例2.1 官方 REST API&#xff08;推荐生产&#xff09;2.2 通过Google Cloud API调用2.3 私有化部署方案2.4 开源镜像 PyBard&#xff08;无需 …