手动实现基于vue构建移动触摸滑动组件——简易的swiper
在web或移动端开发中,有时候我们需要做一个可滚动显示的banner、轮播、滑动翻页显示内容等,常用的插件就数swiper。当然如果我不想因为一个小的页面去引入一个库,那么我们就手动写一个简易版的swiper。因为正做的项目是vue中需要用到滑动翻页效果,就用vue来实现一个垂直方向滑动翻页的效果咯!核心触摸事件:touchstart、touchmove和to...
在web或移动端开发中,有时候我们需要做一个可滚动显示的banner、轮播、滑动翻页显示内容等,常用的插件就数swiper。当然如果我不想因为一个小的页面去引入一个库,那么我们就手动写一个简易版的swiper。因为正做的项目是vue中需要用到滑动翻页效果,就用vue来实现一个垂直方向滑动翻页的效果咯!
核心触摸事件:touchstart、touchmove和touchend。(还有touchcancel事件,这里我们就不考虑了)
- touchstart事件:当手指触摸屏幕时候触发,即使已经有一个手指放在屏幕上也会触发。
- touchmove事件:当手指在屏幕上滑动的时候连续地触发。在这个事件发生期间,调用preventDefault()事件可以阻止滚动。
- touchend事件:当手指从屏幕上离开的时候触发。
核心动画效果: 使用CSS3的transition属性。
一、如何实现展示页面内容:
接下来我们要如何实现页面能向上或向下活动,我们可以把节点分为两层,第一层为页面上显示内容的视窗a,比如,这个窗口占满整个屏幕;第二层为内容容器b,容器b的大小由多页内容c组成。每页c的大小与视窗a相等,这样只要控制内容容器b的位置就可以让每一个c全部呈现在视窗a中。如下图所示(只有视窗a大小的内容是可以显示的)。
视窗a大小等于内容c 大小,容器b是内容c的整数倍。容器b采用position: absolute,控制属性top为视窗a的整数倍,就可以控制页面显示了。再用transition: top 1s 控制过渡效果,只要给一个状态显示第几页,控制top值就可以自由滚动了。如何控制呢,接下来再看看第二部分用滑动事件来如何触发的向下/向上指令状态改变。
我们构建DOM结构,.wiper-container为视窗a,.wiper-list为容器b,wiper-item为内容c。通过this.refs.wiperbox.offsetHeight获取a的大小,然后通过listStyle 和 itemStyle计算出合适的b、c的样式。
<div class="wiper-container"
ref="wiperbox"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
@touchmove="handleTouchMove">
<div class="wiper-list" :style="listStyle">
<div class="wiper-item" :style="itemStyle">第一页</div>
<div class="wiper-item" :style="itemStyle">第二页</div>
<div class="wiper-item" :style="itemStyle">第三页</div>
</div>
</div>
二、 如何触发滑动:
每个触摸事件的触发都会获取到touch对象包含的属性:
- clientX:触摸目标在视口中的x坐标。
- clientY:触摸目标在视口中的y坐标。
- identifier:标识触摸的唯一ID。
- pageX:触摸目标在页面中的x坐标。
- pageY:触摸目标在页面中的y坐标。
- screenX:触摸目标在屏幕中的x坐标。
- screenY:触摸目标在屏幕中的y坐标。
- target:触目的DOM节点目标
那么我们可以分别在touchstart 和 touchend事件中记录触摸的初始位置和结束位置,如pageY的值,两者之差我们就可以计算出手指是向上滑动还是向下滑动,这样我们就可以控制内容滑动是向上翻页还是向下翻页,我们也可以设置一个阈值才出发滑动翻页,避免小幅度触摸导致的翻页。
handleTouchStart: function(e){
this.startPoint = e.touches[0].pageY;
}
handleTouchEnd: function(e){
let end = e.changedTouches[0].pageY;
let start = this.startPoint;
let delta = start - end;
let goDown = 0;
if(delta > 10){
goDown = 1;
}else if(delta < -10){
goDown = -1;
}
this.changeItem(goDown);
}
三、提升效果
实现前面两部分内容,向下滑动,页面就向下滑动,向上滑动,页面就向上滑动。但是,如果手指不松开,页面不会跟随手指滑动,而是等手指松开后才滑动。这时我们需要前面未用到的touchmove事件来实现手指跟随效果。手指不断滑动tochmove就会不断触发,我们还是获取pageX值,计算tochmove的值与tochstart记录的初始值进行比较,并在容器b的当前位置上实时修改top值,同时禁用过渡效果,跟随效果就可以实现了。
handleTouchMove: function(e){
let poChange = e.changedTouches[0].pageY - this.startPoint;
this.pointChange = poChange
}
四、以此类推
以上原理可以实现手动触发上下滑动效果,稍加修改可以扩展更多:
- 如果我们将height切换为width,top切换为left,那么我们也就可以实现左右滑动的效果。
- 如果我们再设置定时器控制页面的切换,那么我们就可以实现轮播图跑马灯banner。
- 如果我们抽象成组件,再通过传入参数控制组件就可以拓展以上的如果,构造为可复用的组件了。
五、抽象公共组件
到第三部分已经可以满足初步的建议swiper效果的需求,有多个地方使用这样的页面,我们可继续抽象出公共组件,页面可以直接使用。
// 页面中引入滑动组件
<template>
<div class="hello">
<wiper>
<wiperitem color="#90F7EC">
<div>第一页</div>
</wiperitem>
<wiperitem color="#7CAAFF">
<span>第二页</span>
</wiperitem>
<wiperitem>
<span>第三页</span>
</wiperitem>
</wiper>
</div>
</template>
我把视窗和内容容器都定义在wiper组件中,wiperitem接收每个内容页的内容,两个组件中用到了vue中的插槽slot,wiper组件中可以根据 this.$slots.default 计算出通过slot传入的组件个数来计算有多少页、并控制容器大小,在wiper中处理页面触摸事件和动画效果;在wiperitem中获取wiper对slot传入的参数计算每个页面大小。
// weiper组件 视窗a + 容器b
<template>
<div class="wiper-container"
ref="wiperbox"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
@touchmove="handleTouchMove"
>
<div class="wiper-list" :style="listStyle" >
<slot :itemHeight="itemHeight"></slot>
</div>
</div>
</template>
//wiperitem组件 内容c
<template>
<div class="wiper-item" :style="itemStyle">
<slot></slot>
</div>
</template>
具体slot的使用可以参照vue官方文档:https://cn.vuejs.org/v2/guide/components-slots.html
完整代码见:https://github.com/chenjycm/myswiper
更多推荐
所有评论(0)