1. 案例分析
1.1. 功能
- 切换 回弹
滑动到1/3处就切换 -> 切换
没有到1/3就回到当前图片 -> 回弹

- 限制边界
第一页/最后一页就不能滑动了
1.2. 页面
scroll-wrapper > slide-page
scroll-wrapper 可以在 slide-page中随意滑动
2. Flex布局和样式分析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 1000px -> 100rem
document.documentElement.style.fontSize = document.documentElement.clientWidth / 37.5 + 'px';
</script>
<style>
body {
margin: 0
}
html,
body,
#app {
height: 100%;
}
div {
width: 100%;
display: flex;
flex-direction: column;
}
.slide-page {
position: relative;
height: 100%;
overflow: hidden;
}
.scroll-wrapper {
position: absolute;
width: 150rem;
height: 100%;
flex-direction: row;
}
.page-item {
height: 100%;
flex: 1;
}
.page-item .inner {
height: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
}
.page-item:nth-child(1) .inner {
background-color: orange;
}
.page-item:nth-child(2) .inner {
background-color: red;
}
.page-item:nth-child(3) .inner {
background-color: pink;
}
.page-item:nth-child(4) .inner {
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<div class="slide-page">
<div class="scroll-wrapper">
<div class="page-item">
<div class="inner">1</div>
</div>
<div class="page-item">
<div class="inner">2</div>
</div>
<div class="page-item">
<div class="inner">3</div>
</div>
<div class="page-item">
<div class="inner">4</div>
</div>
</div>
</div>
</div>
</body>
</html>
3. 功能实现
3.1. 绑定事件处理函数
拿元素
绑定事件处理函数
编辑绑定的函数方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 1000px -> 100rem
document.documentElement.style.fontSize = document.documentElement.clientWidth / 37.5 + 'px';
</script>
<style>
body {
margin: 0
}
html,
body,
#app {
height: 100%;
}
div {
width: 100%;
display: flex;
flex-direction: column;
}
.slide-page {
position: relative;
height: 100%;
overflow: hidden;
}
.scroll-wrapper {
position: absolute;
width: 150rem;
height: 100%;
flex-direction: row;
}
.page-item {
height: 100%;
flex: 1;
}
.page-item .inner {
height: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
}
.page-item:nth-child(1) .inner {
background-color: orange;
}
.page-item:nth-child(2) .inner {
background-color: red;
}
.page-item:nth-child(3) .inner {
background-color: pink;
}
.page-item:nth-child(4) .inner {
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<div class="slide-page">
<div class="scroll-wrapper">
<div class="page-item">
<div class="inner">1</div>
</div>
<div class="page-item">
<div class="inner">2</div>
</div>
<div class="page-item">
<div class="inner">3</div>
</div>
<div class="page-item">
<div class="inner">4</div>
</div>
</div>
</div>
</div>
<script>
// 拿宽度
const oSlidePage = document.querySelector('.slide-page'),
pageWidth = oSlidePage.offsetWidth;
// 要滚动
const oScrollWrapper = oSlidePage.querySelector('.scroll-wrapper');
// 根据长度来判断边界
const oPageItems = oScrollWrapper.querySelectorAll('.page-item');
// 需要哪些变量来控制
const init = () => {
bindEvent
}
function bindEvent() {
oScrollWrapper.addEventListener('touchstart', handleTouchStart, false);
oScrollWrapper.addEventListener('touchmove', handleTouchMove, false);
oScrollWrapper.addEventListener('touchend', handleTouchend, false);
}
function handleTouchStart() {
}
function handleTouchMove() {
}
function handleTouchend() {
}
init();
</script>
</body>
</html>
3.2. touchstart: 拿到鼠标第一次接触的坐标
<body>
<div id="app">
<div class="slide-page">
<div class="scroll-wrapper">
<div class="page-item">
<div class="inner">1</div>
</div>
<div class="page-item">
<div class="inner">2</div>
</div>
<div class="page-item">
<div class="inner">3</div>
</div>
<div class="page-item">
<div class="inner">4</div>
</div>
</div>
</div>
</div>
<script>
// 拿宽度
const oSlidePage = document.querySelector('.slide-page'),
pageWidth = oSlidePage.offsetWidth;
// 要滚动
const oScrollWrapper = oSlidePage.querySelector('.scroll-wrapper');
// 根据长度来判断边界
const oPageItems = oScrollWrapper.querySelectorAll('.page-item');
// 需要哪些变量来控制
// startX moveX -> startX 到 moveX的距离要计算出来 也就是scroppWrapper要移动的距离
let startX = 0;
const init = () => {
bindEvent
}
function bindEvent() {
oScrollWrapper.addEventListener('touchstart', handleTouchStart, false);
oScrollWrapper.addEventListener('touchmove', handleTouchMove, false);
oScrollWrapper.addEventListener('touchend', handleTouchend, false);
}
// 拿到第一个手指坐标
function handleTouchStart(e) {
// touches单指触控 手指就是[0]
startX = e.touches[0].clienX;
}
function handleTouchMove() {
}
function handleTouchend() {
}
init();
</script>
</body>
3.3. touchmove
拿到move后的坐标moveX
根据moveX 和 startX来做边界: 右划且是第一页 || 左划且是最后一页 return
计算出移动的距离: distanceX
做页面移动过渡和动画
<body>
<div id="app">
<div class="slide-page">
<div class="scroll-wrapper">
<div class="page-item">
<div class="inner">1</div>
</div>
<div class="page-item">
<div class="inner">2</div>
</div>
<div class="page-item">
<div class="inner">3</div>
</div>
<div class="page-item">
<div class="inner">4</div>
</div>
</div>
</div>
</div>
<script>
// 拿宽度
const oSlidePage = document.querySelector('.slide-page'),
pageWidth = oSlidePage.offsetWidth;
// 要滚动
const oScrollWrapper = oSlidePage.querySelector('.scroll-wrapper');
// 根据长度来判断边界
const oPageItems = oScrollWrapper.querySelectorAll('.page-item');
// 需要哪些变量来控制
// startX moveX -> startX 到 moveX的距离要计算出来 也就是scroppWrapper要移动的距离
let startX = 0,
pageIndex = 0, // 第几个page-item的索引
distancex = 0;// 移动的距离
const init = () => {
bindEvent
}
function bindEvent() {
oScrollWrapper.addEventListener('touchstart', handleTouchStart, false);
oScrollWrapper.addEventListener('touchmove', handleTouchMove, false);
oScrollWrapper.addEventListener('touchend', handleTouchend, false);
}
// 拿到第一个手指坐标
function handleTouchStart(e) {
// touches单指触控 手指就是[0]
startX = e.touches[0].clienX;
}
function handleTouchMove() {
// 手指滑动的当前的坐标
const moveX = e.touches[0].clienX;
// 做边界
// 判断用户是左划还是右划 --> 如果moveX > startX 往右划; 否则左划
// 右划且是第一页 || 左划且是最后一页 return
if ((moveX > startX && pageIndex === 0)
||
(moveX < startX && pageIndex === oPageItems.length - 1)) {
return;
}
// 不是第一/最后一页 -> 要划动 算出移动的距离
distanceX = moveX - startX;
// 做页面移动的效果和动画 页面滑动的距离: 自己的宽度
// 第二页: - pageWidth * 1 + distanceX
setTranslateX(- pageWidth * pageIndex + distanceX)
}
// 做页面移动的效果和动画
function setTranslateX(transX) {
oScrollWrapper.style.transition = 'all .1s';
// translateX水平左右移动 正值右移 负值左移
oScrollWrapper.style.transform = `translateX(${transX}px)`;
}
function handleTouchend() {
}
init();
</script>
</body>

3.4. touchend
在move的情况下,计算移动的距离是否大于1/3 切换/回弹
距离大于1/3 -> 判断是左划还是右划 右划 -> 修改pageIndex 当前页面的索引号
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 1000px -> 100rem
document.documentElement.style.fontSize = document.documentElement.clientWidth / 37.5 + 'px';
</script>
<style>
body {
margin: 0
}
html,
body,
#app {
height: 100%;
}
div {
width: 100%;
display: flex;
flex-direction: column;
}
.slide-page {
position: relative;
height: 100%;
overflow: hidden;
}
.scroll-wrapper {
position: absolute;
width: 150rem;
height: 100%;
flex-direction: row;
}
.page-item {
height: 100%;
flex: 1;
}
.page-item .inner {
height: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
}
.page-item:nth-child(1) .inner {
background-color: orange;
}
.page-item:nth-child(2) .inner {
background-color: red;
}
.page-item:nth-child(3) .inner {
background-color: pink;
}
.page-item:nth-child(4) .inner {
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<div class="slide-page">
<div class="scroll-wrapper">
<div class="page-item">
<div class="inner">1</div>
</div>
<div class="page-item">
<div class="inner">2</div>
</div>
<div class="page-item">
<div class="inner">3</div>
</div>
<div class="page-item">
<div class="inner">4</div>
</div>
</div>
</div>
</div>
<script>
// 拿宽度
const oSlidePage = document.querySelector('.slide-page'),
pageWidth = oSlidePage.offsetWidth;
// 要滚动
const oScrollWrapper = oSlidePage.querySelector('.scroll-wrapper');
// 根据长度来判断边界
const oPageItems = oScrollWrapper.querySelectorAll('.page-item');
// 需要哪些变量来控制
// startX moveX -> startX 到 moveX的距离要计算出来 也就是scroppWrapper要移动的距离
let startX = 0,
pageIndex = 0, // 第几个page-item的索引
distancex = 0,// 移动的距离
isMove = false; // 默认不切换
const init = () => {
bindEvent
}
function bindEvent() {
oScrollWrapper.addEventListener('touchstart', handleTouchStart, false);
oScrollWrapper.addEventListener('touchmove', handleTouchMove, false);
oScrollWrapper.addEventListener('touchend', handleTouchend, false);
}
// 拿到第一个手指坐标
function handleTouchStart(e) {
// touches单指触控 手指就是[0]
startX = e.touches[0].clienX;
}
function handleTouchMove() {
// 手指滑动的当前的坐标
const moveX = e.touches[0].clienX;
// 做边界
// 判断用户是左划还是右划 --> 如果moveX > startX 往右划; 否则左划
// 右划且是第一页 || 左划且是最后一页 return
if ((moveX > startX && pageIndex === 0)
||
(moveX < startX && pageIndex === oPageItems.length - 1)) {
return;
}
// 不是第一/最后一页 -> 要划动 算出移动的距离
distanceX = moveX - startX;
// 做页面移动的效果和动画 页面滑动的距离: 自己的宽度
// 第二页: - pageWidth * 1 + distanceX
setTranslateX(- pageWidth * pageIndex + distanceX);
// 开启move
isMove = true;
}
// 做页面移动的效果和动画
function setTranslateX(transX) {
oScrollWrapper.style.transition = 'all .1s';
// translateX水平左右移动 正值右移 负值左移
oScrollWrapper.style.transform = `translateX(${transX}px)`;
}
function handleTouchend() {
// 在move的情况下,计算移动的距离是否大于1/3 切换/回弹
if(isMove){
if(Math.abs(distanceX) > pageWidth /3 ){
// 切换
// 修改pageIndex 当前页面的索引号
// 判断是左划还是右划 右划
if(moveX > startX){
pageIndex --;
}
// 左划
if(moveX < startX){
pageIndex ++;
}
}
//
setTranslateX(- pageWidth * pageIndex);
}
startX = 0;
moveX = 0;
isMove = false;
}
init();
</script>
</body>
</html>
4. 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 1000px -> 100rem
document.documentElement.style.fontSize = document.documentElement.clientWidth / 37.5 + 'px';
</script>
<style>
body {
margin: 0
}
html,
body,
#app {
height: 100%;
}
div {
width: 100%;
display: flex;
flex-direction: column;
}
.slide-page {
position: relative;
height: 100%;
overflow: hidden;
}
.scroll-wrapper {
position: absolute;
width: 150rem;
height: 100%;
flex-direction: row;
}
.page-item {
height: 100%;
flex: 1;
}
.page-item .inner {
height: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
}
.page-item:nth-child(1) .inner {
background-color: orange;
}
.page-item:nth-child(2) .inner {
background-color: red;
}
.page-item:nth-child(3) .inner {
background-color: pink;
}
.page-item:nth-child(4) .inner {
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<div class="slide-page">
<div class="scroll-wrapper">
<div class="page-item">
<div class="inner">1</div>
</div>
<div class="page-item">
<div class="inner">2</div>
</div>
<div class="page-item">
<div class="inner">3</div>
</div>
<div class="page-item">
<div class="inner">4</div>
</div>
</div>
</div>
</div>
<script>
// 拿宽度
const oSlidePage = document.querySelector('.slide-page'),
pageWidth = oSlidePage.offsetWidth;
// 要滚动
const oScrollWrapper = oSlidePage.querySelector('.scroll-wrapper');
// 根据长度来判断边界
const oPageItems = oScrollWrapper.querySelectorAll('.page-item');
// 需要哪些变量来控制
// startX moveX -> startX 到 moveX的距离要计算出来 也就是scroppWrapper要移动的距离
let startX = 0,
pageIndex = 0, // 第几个page-item的索引
distancex = 0,// 移动的距离
isMove = false; // 默认不切换
const init = () => {
bindEvent
}
function bindEvent() {
oScrollWrapper.addEventListener('touchstart', handleTouchStart, false);
oScrollWrapper.addEventListener('touchmove', handleTouchMove, false);
oScrollWrapper.addEventListener('touchend', handleTouchend, false);
}
// 拿到第一个手指坐标
function handleTouchStart(e) {
// touches单指触控 手指就是[0]
startX = e.touches[0].clienX;
}
function handleTouchMove() {
// 手指滑动的当前的坐标
const moveX = e.touches[0].clienX;
// 做边界
// 判断用户是左划还是右划 --> 如果moveX > startX 往右划; 否则左划
// 右划且是第一页 || 左划且是最后一页 return
if ((moveX > startX && pageIndex === 0)
||
(moveX < startX && pageIndex === oPageItems.length - 1)) {
return;
}
// 不是第一/最后一页 -> 要划动 算出移动的距离
distanceX = moveX - startX;
// 做手指拖移的动画效果
// 第二页: - pageWidth * 1 + distanceX
setTranslateX(- pageWidth * pageIndex + distanceX);
// 开启move
isMove = true;
}
// 做页面移动的效果和动画
function setTranslateX(transX) {
// 这个函数的作用是将 .scroll-wrapper 元素(包含多个 .page-item 页面项的滚动容器)沿X轴移动指定的像素距离 transX
oScrollWrapper.style.transition = 'all .1s';
// translateX水平左右移动 正值右移 负值左移
oScrollWrapper.style.transform = `translateX(${transX}px)`;
}
function handleTouchend() {
// 在move的情况下,计算移动的距离是否大于1/3 切换/回弹
if(isMove){
if(Math.abs(distanceX) > pageWidth /3 ){
// 切换
// 修改pageIndex 当前页面的索引号
// 判断是左划还是右划 右划
if(moveX > startX){
pageIndex --;
}
// 左划
if(moveX < startX){
pageIndex ++;
}
}
//
setTranslateX(- pageWidth * pageIndex);
}
startX = 0;
moveX = 0;
isMove = false;
}
init();
</script>
</body>
</html>
该博客围绕前端滑动切换与回弹功能展开。先进行案例分析,明确功能为切换、回弹及边界限制,页面结构为 scroll-wrapper 在 slide-page 中滑动。接着分析 Flex 布局和样式,然后阐述功能实现步骤,包括绑定事件处理函数、获取坐标、处理 touchmove 和 touchend 事件,最后给出代码。

3445

被折叠的 条评论
为什么被折叠?



