1 布局 布局的包裹器:占满视口的宽度 滑屏元素(动态生成,根据图片的数量) 注意:1、querySelector获取的是静态列表,动态添加元素后要重新获取一遍 2、有时候绘制跟不上js引擎的绘制,解决方法:定时器 2 基本滑屏 拿到元素和手指一开始(点击到布局包裹器上时)的位置; 拿到元素的实时位置,计算手指实时的偏移量,将偏移量实时同步给滑屏元素; 3 版本 定位版(调整滑屏元素的left偏移量) offsetLeft:累加的过程 -- 抽象图片的下标 -- 抽象滑屏元素的位置 2d变换版(单独的图层,触发的重绘重排代价较小,利用transform,translate) translate的变换不会同步到offsetLeft 解决办法: -- 变量,设置一个变量专门去同步translate,但是这会让业务逻辑变得复杂 -- 定义css函数,一定一个css函数,专门处理transform中设置的各种变换的值 利用节点的属性来管理变换类型,及其所对应的值 2个参数: 读取操作 3个参数: 设置操作 循环节点的属性 4 无缝自动滑屏 无缝:复制一组图片,当点击第一组的第一张图片的时候,瞬间跳到第二组的第一张 当点击第二组的最后一张时,瞬间跳转到第一组的最后一张 自动滑屏:循环定时器 函数包裹(重启定时器) 清定时器(循环定时器的逻辑没有必要开启多次,暂停逻辑) 自动滑屏和无缝的冲突: 使用同一个index变量就可以 html文件中js代码如下:其中引入的cl.js文件是将动态生成结构、自动滑屏和同步translate(css函数)代码抽取出来的js文件。
<script type="text/javascript" src="js/cl.js"></script>
<script type="text/javascript">
window.onload = function () {
document.addEventListener('touchstart',function (ev) {
ev = ev || event
ev.preventDefault()
})
var arr = ["img/01.jpg","img/02.jpg","img/03.jpg","img/04.jpg","img/05.jpg"]
cl.carousel(arr)
}
</script>
cl.js文件代码:
!(function (w) {
// 将函数绑给window的cl属性上
w.cl = {};
w.cl.css = function(node,type,val) {
// 判断节点transform对象是否存在,不存在则添加
if (typeof node === "object" && typeof node["transform"] == "undefined") {
node["transform"] = {};
}
if(arguments.length >= 3){// 写操作
//设置
var text = "";
// 加入两次传入同样属性的变换,则会被覆盖
// 只会更改改变的值,之前没有改变的值则不会改变
node["transform"][type] = val;
// in操作符,会查找对象实例本身及原型链上所有的属性,可能会对后续操作产生影响
for (item in node["transform"]) {
if(node["transform"].hasOwnProperty(item)){
switch (item){
case "translateX":
case "translateY":
text += item + "(" + node["transform"][item] + "px)";
break;
case "scale":
text += item + "(" + node["transform"][item] + ")";
break;
case "roate":
text += item + "(" + node["transform"][item] + "deg)";
break;
}
}
}
// 兼容性问题
node.style.transform = node.style.webkitTransform = text;
}else if(arguments.length == 2){
// 读操作
// 此时只传入了2个参数,第3个参数并没有用上,所以可以利用未使用的第3个参数返回结果
val = node["transform"][type];
if (typeof val === 'undefined'){
switch (type){
case "translateX":
case "translateY":
case "rotate":
val = 0;
break;
case "scale":
val = 1;
break;
}
}
return val;
}
}
w.cl.carousel = function (arr) {
// 布局
var carouselWrap = document.querySelector(".carousel-wrap");
if(carouselWrap){
var pointsLength = arr.length;
// 无缝
var needCarousel = carouselWrap.getAttribute('needCarousel');
needCarousel = needCarousel == null? false:true;
//console.log(needCarousel);
if(needCarousel){
arr = arr.concat(arr);
}
var ulNode = document.createElement('ul');
var styleNode = document.createElement('style')
ulNode.classList.add('list');
for(var i=0;i<arr.length;i++){
ulNode.innerHTML += "<li><a href='javascript:;'><img src="+ arr[i] + " alt='picture'></a></li>"
}
styleNode.innerHTML = ".carousel-wrap > .list{width:"+ (arr.length*100) +"%;}.carousel-wrap > .list > li{width:"+ (1/arr.length*100) +"%;}"
carouselWrap.appendChild(ulNode);
document.head.appendChild(styleNode);
var imgNode = document.querySelector('.carousel-wrap > .list > li > a > img')
// 如果不设置定时器,可能获取到img时,还没有渲染完,此时无法获取其高度
setTimeout(function () {
carouselWrap.style.height = imgNode.offsetHeight + 'px';
},100)
var pointsWrap = document.querySelector('.carousel-wrap > .points-wrap');
if(pointsWrap) {
for (var i = 0; i < pointsLength; i++) {
if(i == 0){
pointsWrap.innerHTML += "<span class='active'></span>";
} else {
pointsWrap.innerHTML += "<span></span>";
}
}
var pointsSpan = document.querySelectorAll('.carousel-wrap > .points-wrap > span');
}
// 滑屏
/*
* 1 拿到元素一开始的位置
* 2 拿到手指一开始点击的位置
* 3 拿到手指move的实时距离
* 4 将手指移动的距离加给元素
*/
var index = 0;
// 手指的位置
var startX = 0;
// 元素的位置
var elementX = 0;
// 保存实时偏移量
//var translateX = 0;
carouselWrap.addEventListener('touchstart',function (ev) {
ev = ev || event;
// changedTouches是列表,取一个就够了
var touchC = ev.changedTouches[0];
ulNode.style.transition = 'none';
// 无缝逻辑,点击第一组的第一张是瞬间跳到第二组的第一张
//点击第二组的最后一张时,瞬间跳到第一组的最后一张
// index代表ul的位置
// 无缝
if(needCarousel){
var index = cl.css(ulNode,"translateX")/document.documentElement.clientWidth;
if(-index === 0){
index = -pointsLength;
} else if (-index === arr.length-1) {
index = -pointsLength + 1;
}
cl.css(ulNode,"translateX",index*document.documentElement.clientWidth);
}
startX = touchC.clientX;
//elementX = ulNode.offsetLeft;
//elementX = translateX;
elementX = cl.css(ulNode,"translateX")
// 停止自动轮播
clearInterval(timer)
})
carouselWrap.addEventListener('touchmove',function (ev) {
ev = ev || event;
var touchC = ev.changedTouches[0]
// 获取滑动时手指的位置
var nowX = touchC.clientX;
// 手指移动的距离
var disX = nowX - startX;
//translateX = elementX + disX
//ulNode.style.left = elementX + disX + 'px';
cl.css(ulNode,"translateX",elementX+disX);
})
carouselWrap.addEventListener('touchend',function (ev) {
ev = ev || event;
//var index = ulNode.offsetLeft/document.documentElement.clientWidth;
//var index = translateX/document.documentElement.clientWidth;
index = cl.css(ulNode,"translateX")/document.documentElement.clientWidth;
index = Math.round(index)
if(index > 0){
index = 0
} else if (index < -arr.length+1) {
index = -arr.length+1
}
syncPoints(index)
ulNode.style.transition = '.5s left';
//ulNode.style.left = index * (document.documentElement.clientWidth) + 'px'
// translate造成的元素偏移并不会同步到offsetLeft上,因为两者不在同个一个图层
//translateX = index * (document.documentElement.clientWidth)
//ulNode.style.transform = "translateX("+ translateX + "px)" ;
cl.css(ulNode,"translateX",index * (document.documentElement.clientWidth));
// 开启自动轮播
if(needAuto){
auto();
}
})
// 自动轮播
var timer = 0;
var needAuto = carouselWrap.getAttribute('needAuto')
needAuto = needAuto == null ? false:true;
//console.log(needAuto);
if(needAuto){
auto();
}
function auto() {
clearInterval(timer)
timer = setInterval(function () {
if(index === -arr.length+1){
ulNode.style.transition = "none";
index = -arr.length/2+1;
cl.css(ulNode,"translateX",index*document.documentElement.clientWidth)
}
setTimeout(function () {
index--;
ulNode.style.transition = "1s transform"
syncPoints(index)
cl.css(ulNode,"translateX",index*document.documentElement.clientWidth)
},50)
},2000)
}
// 同步小圆点的函数
function syncPoints(index) {
if(!pointsWrap){
return;
}
// 同步小圆点
for (var i = 0; i < pointsSpan.length; i++) {
pointsSpan[i].classList.remove('active');
}
pointsSpan[-index%pointsLength].classList.add('active');
}
}
}
})(window)