经过昨天对移动端基础的了解,今天就来用原生JS实现一下我们的幻灯片。
因为是用原生实现,所以本文篇幅较长,各位看官只需理解思路即可,代码部分可以粗略看看。
毕竟我们有better-scroll这样封装好的框架能更快速实现效果。b( ̄▽ ̄)d
首先根据我们昨天的滑屏操作,先将幻灯片的滑屏效果做出来。这里大家将照片地址更换成自己的就能得到效果。
案例要在客户端才有效果哦,如果在PC端,网页中右键点审查,控制器旁边有个手机图标,点击下图这个也能有效果。
Document
html {
font-size: 10vw;
}
body {
margin: 0;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
#wrap {
position: relative;
width: 100vw;
overflow: hidden;
}
#list {
float: left;
display: flex;
display: -webkit-box;
}
#list li {
flex: none;
width: 100vw;
}
#list img {
width: 100%;
display: block;
}
.nav {
position: absolute;
left: 0;
bottom: .2rem;
width: 100%//代码效果参考:http://hnjlyzjd.com/hw/wz_25260.html
;text-align: center;
vertical-align: top;
}
.nav a {
display: inline-block;
width: .3rem;
height: .3rem;
background: #fff;
margin: 0 .1rem;
border-radius: .15rem;
transition: .3s;
}
.nav .active {
width: .6rem;
color: #fff; //代码效果参考:http://hnjlyzjd.com/hw/wz_25258.html
}
// 幻灯片效果
{
let wrap = document.querySelector("#wrap");
let list = document.querySelector("#list");
let navs = document.querySelectorAll(".nav a");
let translateX = 0; //元素的移动位置
let startPoint = {}; //摁下时手指坐标
let disPoint = {};//手指移动距离
let startX = 0;//摁下时元素坐标
wrap.addEventListener("touchstart",({changedTouches})=>{
startPoint = {
x: changedTouches【0】.pageX,
y: changedTouches【0】.pageY
};
startX = translateX;
});
wrap.addEventListener("touchmove",(e)=>{
let touch = e.changedTouches【0】;
let nowPoint = {
x: touch.pageX,
y: touch.pageY
};
disPoint = {
x: nowPoint.x - startPoint.x,
y: nowPoint.y - startPoint.y
};
translateX = disPoint.x + startX;
list.style.transform = translateX(${translateX}px)
;
});
}
滑屏效果做出来以后,我们给它添加动画,接下来为了大家方便看,就只写JavaScript的代码。红色的为添加的新代码。
// 幻灯片效果
{
let wrap = document.querySelector("#wrap");
let list = document.querySelector("#list");
let navs = document.querySelectorAll(".nav a");
let translateX = 0; //元素的移动位置
let startPoint = {}; //摁下时手指坐标
let startX = 0;//摁下时元素坐标
let disPoint = {};//手指移动距离
let now = 0; // 记录当前在第几张
const Range = .3 wrap.clientWidth; // 移动超过屏幕 30% 时,抬起切换到下一张
wrap.addEventListener("touchstart",({changedTouches})=>{
list.style.transition = "none";
startPoint = {
x: changedTouches【0】.pageX,
y: changedTouches【0】.pageY
};
startX = translateX;
});
wrap.addEventListener("touchmove",(e)=>{
let touch = e.changedTouches【0】;
let nowPoint = {
x: touch.pageX,
y: touch.pageY
};
disPoint = {
x: nowPoint.x - startPoint.x,
y: nowPoint.y - startPoint.y
};
translateX = disPoint.x + startX;
list.style.transform = translateX(${translateX}px)
;
});
wrap.addEventListener("touchend",()=>{
if(Math.abs(disPoint.x)>Range){ //切换到下一张
//console.log(disPoint.x/Math.abs(disPoint.x));
now -= disPoint.x/Math.abs(disPoint.x);
// 求当前要看第几张
}
translateX = -nowwrap.clientWidth;
list.style.transition = ".3s";
list.style.transform = translateX(${translateX}px)
;
//console.log(now);
});
}
这一步我们将每次手指离开屏幕后进行判断,判断手指移动的距离是否超过30%,抬起则切换到下一张。我们在每次切换时添加了0.3s的延迟动画。因为这延迟动画在手指触屏时就会生效,所以我们在touchstart的时候将它清除。
我们实现上面效果后,可以发现会有划出去的风险,接下来我们要实现无缝滚动
在此之前,我们要了解实现无缝滚动的原理,下面我做了个图,一目了然,如果有不明白可以评论联系我。
原理就是:当用户往第一组的第一张滑动时,我们将他移动到第二组的第一张。
当用户网第二组最后一张滑动时,我们将他移动到第一组的最后一张。
// 幻灯片效果
{
let wrap = document.querySelector("#wrap");
let list = document.querySelector("#list");
let navs = document.querySelectorAll(".nav a");
let translateX = 0; //元素的移动位置
let startPoint = {}; //摁下时手指坐标
let startX = 0;//摁下时元素坐标
let disPoint = {};//手指移动距离
let now = 0; // 记录当前在第几张
const Range = .3 wrap.clientWidth; // 移动超过屏幕 30% 时,抬起切换到下一张
list.innerHTML += list.innerHTML; // 把 list 图片复制一份
wrap.addEventListener("touchstart",({changedTouches})=>{
list.style.transition = "none";
startPoint = {
x: changedTouches【0】.pageX,
y: changedTouches【0】.pageY
};
if(now === 0){
now = navs.length;
} else if(now === navs.length2-1){
now = navs.length - 1;
}
translateX = -nowwrap.clientWidth;
list.style.transform = translateX(${translateX}px)
;
startX = translateX;
});
wrap.addEventListener("touchmove",(e)=>{
let touch = e.changedTouches【0】;
let nowPoint = {
x: touch.pageX,
y: touch.pageY
};
disPoint = {
x: nowPoint.x - startPoint.x,
y: nowPoint.y - startPoint.y
};
translateX = disPoint.x + startX;
list.style.transform = translateX(${translateX}px)
;
});
wrap.addEventListener("touchend",()=>{
if(Math.abs(disPoint.x)>Range){ //切换到下一张
//console.log(disPoint.x/Math.abs(disPoint.x));
now -= disPoint.x/Math.abs(disPoint.x);
// 求当前要看第几张
}
translateX = -nowwrap.clientWidth;
list.style.transition = ".3s";
list.style.transform = translateX(${translateX}px)
;
});
}
实现这一效果,首先我们将图片复制多一份,然后在手指点击屏幕时,
判断现在是否是第一组的第一张,是则跳转到第第二组的第一张
或者是否为第二组的最后一张,是则跳转到第一组的最后一张。
对其style里的translateX进行进行更改就能实现无缝滚动。
接着我们对下面的白点进行同步,只用在touchend下添加以下代码即可
// 同步 nav
navs.forEach(item=>{
item.classList.remove("active");
});
navs【now%navs.length】.classList.add("active");
至此我们已经完成一个比较简单的移动端轮播。
但是
我们还有bug,就是当用户斜向上滑动时,轮播与下面内容皆会滑动。
因此我们需要做:
1. 判断用户想要滑动的是幻灯片,还是想要滚动滚动条
2. 如果想要滑动幻灯片,就阻止滚动条
3. 如果想要滚动滚动条,就阻止幻灯片
4. 注意 一旦在滑动的过程中判断到用户的滑动方向之后,就不在做方向修改
以下便是所有效果
Document
html {
font-size: 10vw;
}
body {
margin: 0;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
#wrap {
position: relative;
width: 100vw;
overflow: hidden;
}
#list {
float: left;
display: flex;
display: -webkit-box;
}
#list li {
flex: none;
width: 100vw;
}
#list img {
width: 100%;
display: block;
}
.nav {
position: absolute;
left: 0;
bottom: .2rem;
width: 100%;
text-align: center;
vertical-align: top;
}
.nav a {
display: inline-block;
width: .3rem;
height: .3rem;
background: #fff;
margin: 0 .1rem;
border-radius: .15rem;
transition: .3s;
}
.nav .active {
width: .6rem;
color: #fff;
}
.textList {
margin: 0;
padding: 0;
list-style: none;
}
.textList li {
font: 14px/40px "宋体";
padding-left: 20px;
border-bottom: 1px solid #000;
}
"use strict";
// 补充列表内容
{
var txtList = document.querySelector(".textList");
txtList.innerHTML = 【...(".".repeat(100))】.map(function (item, index) {
return "\u8FD9\u662F\u7B2C" + index + "\u4E2Ali";
}).join("");
} // 幻灯片
/
判断用户滑动方向时:
一旦判断到用户的滑动方向,就认定该次操作用户想要进行上下或左右滑动,中间不再修改,一直到用户下一次再执行 start
/
{
var wrap = document.querySelector("#wrap");
var list = document.querySelector("#list");
list.innerHTML += list.innerHTML; // 把图片复制一份用来处理无缝
var startPoint = {}; // 摁下时手指位置
var startX = 0; // 摁下时元素的位置
var translateX = 0; // 元素的 tranlateX 值
var now = 0; //记录当前在第几张
var navs = document.querySelectorAll(".nav a");
var RANGE = wrap.clientWidth .3; //超过该幅度切换上一张下一张
var isMove = false; // 是否需要滑动幻灯片
var isDir = true; // 记录是否已经判断到了方向 true 还没有判断到方向,false已经判断到了方向
wrap.addEventListener("touchstart", function (_ref) {
var changedTouches = _ref.changedTouches;
list.style.transition = "none";
var touch = changedTouches【0】;
startPoint = {
x: touch.pageX,
y: touch.pageY
};
if (now == 0) {
//第1组第0张会有划出去的风险
now = navs.length;
} else if (now == navs.length 2 - 1) {
// 第2组最后一张,有划出去的风险
now = navs.length - 1;
}
translateX = -now wrap.clientWidth;
list.style.WebkitTransform = list.style.transform = "translateX(" + translateX + "px)";
startX = translateX;
isMove = false;
isDir = true;
});
wrap.addEventListener("touchmove", function (e) {
var touch = e.changedTouches【0】;
var nowPoint = {
x: touch.pageX,
y: touch.pageY
};
var dis = {
x: nowPoint.x - startPoint.x,
y: nowPoint.y - startPoint.y
}; // 判断方向根据需求来阻止默认事件
if (isDir) {
if (Math.abs(dis.x) - Math.abs(dis.y) > 5) {
// 左右滑动
isMove = true;
isDir = false;
} else if (Math.abs(dis.y) - Math.abs(dis.x) > 5) {
// 上下滑动
isMove = false;
isDir = false;
}
e.preventDefault();
}
console.log(isMove, isDir);
if (isMove) {
translateX = startX + dis.x;
list.style.WebkitTransform = list.style.transform = "translateX(" + translateX + "px)";
e.preventDefault();
}
});
wrap.addEventListener("touchend", function (_ref2) {
var changedTouches = _ref2.changedTouches;
var touch = changedTouches【0】;
var nowPoint = {
x: touch.pageX,
y: touch.pageY
};
var dis = {
x: nowPoint.x - startPoint.x,
y: nowPoint.y - startPoint.y
}; // 当移动的距离超过图片宽度的 30% 时 切换至下一张或上一张,否则回到当前张
if (Math.abs(dis.x) >= RANGE && isMove) {
// 切换上一张下一张
//console.log(dis.x,dis.x/Math.abs(dis.x));
now -= dis.x / Math.abs(dis.x);
} //console.log(-nowwrap.clientWidth);
translateX = -now * wrap.clientWidth;
list.style.transition = ".3s";
list.style.WebkitTransform = list.style.transform = "translateX(" + translateX + "px)";
navs.forEach(function (nav) {
nav.classList.remove("active");
});
navs【now % navs.length】.classList.add("active");
});
}