html轮播图片效果代码(轮播图怎么实现的原理细节详解)
来,开整
先来看看啥是无缝滚动:
这看着只有四张图片,但是不管往前还是往后拖动,都可以无限拖,感觉就是把四张图片无限循环了一样,这种效果在页面中算是最常见的交互效果之一了,都1202年了,基本功要扎实哦~
其实实现的基本原理非常简单
第一步,先把图片复制一份,四张变八张
如果图片数量更多的话,可以做成别的方案,几张图片的话就无所谓了
如果是几十张图片,那就直接做成一个3D的圆环,还是那句话,有解决思路最重要。
这张图要这样来理解:在鼠标按下的瞬间,先看看当前显示的是第几张图,如果是第0张,则按下的瞬间把整体位置调整到第二组的第0张去,第二组也就是复制出来的那一组,这样的话不管是往左拖动还是往右拖动都是没有问题的
然后,如果当前显示的是第二组的最后一张,在鼠标按下的瞬间,直接把整体位置调整到第一组的最后一张,这样依然是可以往左和往右拖动的,来看示意图:
把其他图片遮住,只关注中间显示的那一张,你就可以看到无缝滚动了:
想看代码的,继续往后看,代码量其实很少,连着css一起也才一百多行而已,所以其实一点都不复杂,重点还是在原理上。
这里是结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | < div class = "wrap" > < ul class = "list" > <!--这里是图片列表--> < li class = "item" >< img src = "img/img01.png" ></ li > < li class = "item" >< img src = "img/img02.png" ></ li > < li class = "item" >< img src = "img/img03.png" ></ li > < li class = "item" >< img src = "img/img04.png" ></ li > </ ul > < ul class = "dot" > <!--这里是下面的小点--> < li class = "active" ></ li > < li ></ li > < li ></ li > < li ></ li > </ ul > </ div > |
可以看到在原列表中只有四张图片,直接获取到 list 之后把内容复制一遍就可以得到八张图片了。
第二步,获取元素,设置变量
1 2 3 4 5 6 7 8 | let wrap = document.querySelector( '.wrap' ); //获取外框,以便设置图片滚动的宽度 let list = wrap.querySelector( '.list' ); //图片列表 let dot = wrap.querySelectorAll( '.dot li' ); //小点 let startpoint = {}; //鼠标按下的起始点 let distaince = {}; //移动的距离 list.innerHTML += list.innerHTML; //复制图片列表 |
这里其实没什么可说的,用原生去写交互,无非就是获取元素然后添加事件操作元素,所以接下来要做什么你们就知道了,在这里我写的是移动端版本的,所以用的是touch事件
第三步,添加事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | wrap.addEventListener( 'touchstart' ,(ev)=>{ //手指按下 let touch = ev.changedTouches[0]; startpoint = { //这里记录手指按下的位置 x:touch.pageX, y:touch.pageY } }) //在手指按下的时候要做的事情其实很简单,只需要记录手指按下的位置就好了,先让图片能拖动,之后再考虑无缝滚动的事情 wrap.addEventListener( 'touchmove' ,(ev)=>{ //手指移动 let touch = ev.changedTouches[0]; distaince = { //移动的时候计算出移动了多少距离 x:touch.pageX - startpoint.x, y:touch.pageY - startpoint.y } translatex = startOffset + distaince.x; list.style.transform = `translateX(${translatex}px)`; //把计算出来的移动距离附加给图片列表 }) //到这一步,图片就已经可以被拖动了 |
这里,已经实现了图片可以被拖动了,接下来,判断什么时候要切换到下一张。
这里的话,如果你没有过多的研究,完全可以一拍脑袋来决定,但是如果要注重用户体验,那就听我的用一个比例来控制。 比如:当图片被拖动的距离超过外框的30%,那么这时候松开手会自动切换到下一张或者上一张,所以这时候我们需要一个变量来控制这个比例,以便于我们去反复调整
于是,当手指抬起的时候,应该要去做这一系列的判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 | let now = 0; //当前在那一张 let imgW = wrap.offsetWidth; //获取一下外框的宽度 let proportion = 0.3; //当图片拖动超过整体宽度的比例时,跳转到下一张或者上一张 wrap.addEventListener( 'touchend' ,()=>{ //手指抬起 if (Math.abs(distaince.x) > imgW * proportion){ //这里判断什么时候切换 now -= distaince.x/Math.abs(distaince.x); //这里判断是切换上一张还是下一张 } translatex = now * -imgW; //判断好了之后计算图片列表需要移动的距离 list.style.transition = '0.3s' ; list.style.transform = `translateX(${translatex}px)`; }) |
到这里,我们已经可以拖动图片,然后切换上一张下一张了。 但是如果想要无缝,还是需要进一步操作的,在原理解析那里有说,可以再看看这张原理分析图:
可以看到,当点下去的瞬间,如果图片在第一张,那么瞬间跳转到第二组的第一张,这样无论是往左拖动还是往右拖动,都是有图片的,来看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | wrap.addEventListener( 'touchstart' ,(ev)=>{ let touch = ev.changedTouches[0]; startpoint = { x:touch.pageX, y:touch.pageY } if (now == 0){ //如果当前是第0张,则直接跳转到第二组的第0张 now = dot.length; } else if (now == dot.length*2 -1){ //如果是最后一张,则跳转到第一组的最后一张 now = dot.length -1; } translatex = now * -imgW; //计算好之后再挪动图片列表 startOffset = translatex; list.style.transition = 'none' ; list.style.transform = `translateX(${translatex}px)`; }) |
到这里,无缝滚动其实也就没有问题了,来看看完整的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | <!DOCTYPE html> < html > < head > < meta charset = "UTF-8" > < title >Title</ title > < meta name = "viewport" content = "width=device-width,initial-scale=1" > < style > body{ margin: 0; background: #fefefe; } .wrap{ width: 100vw; overflow: hidden; box-shadow: 0 0 5px 0 rgba(0,0,0,0.1); position: relative; background: #000; } ul{ margin: 0; padding: 0; list-style: none; } .list{ display: flex; float: left; } .list img{ width: 100vw; vertical-align: top; filter: saturate(2); } .dot{ width: 100vw; display: flex; justify-content: center; position: absolute; bottom: 20px; } .dot li{ width: 10px; height: 10px; border-radius: 10px; background: #fff; margin: 0 5px; transition: 0.2s; box-shadow: 0 0 3px 0 rgba(0,0,0,0.3); } .dot li.active{ width: 20px; } </ style > </ head > < body > < div > < ul > <!--这里是图片列表--> < li >< img src = "img/img01.png" ></ li > < li >< img src = "img/img02.png" ></ li > < li >< img src = "img/img03.png" ></ li > < li >< img src = "img/img04.png" ></ li > </ ul > < ul > <!--这里是下面的小点--> < li ></ li > < li ></ li > < li ></ li > < li ></ li > </ ul > </ div > < script > { let wrap = document.querySelector('.wrap');//获取外框,以便设置图片滚动的宽度 let list = wrap.querySelector('.list');//图片列表 let dot = wrap.querySelectorAll('.dot li');//小点 let startpoint = {};//鼠标按下的起始点 let distaince = {};//距离 let startOffset = 0;//记录鼠标按下时图片列表的位置 let translatex = 0;//移动的距离 let now = 0;//当前在那一张 let imgW = wrap.offsetWidth;//获取一下外框的宽度 let proportion = 0.3;//当图片拖动超过整体宽度的比例时,跳转到下一张或者上一章 let isMove = false; list.innerHTML += list.innerHTML;//复制图片列表 wrap.addEventListener('touchstart',(ev)=>{ let touch = ev.changedTouches[0]; startpoint = { x:touch.pageX, y:touch.pageY } if(now == 0){ now = dot.length; }else if(now == dot.length*2 -1){ now = dot.length -1; } translatex = now * -imgW; startOffset = translatex; list.style.transition = 'none'; list.style.transform = `translateX(${translatex}px)`; }) wrap.addEventListener('touchmove',(ev)=>{ let touch = ev.changedTouches[0]; distaince = { x:touch.pageX - startpoint.x, y:touch.pageY - startpoint.y } if(Math.abs(distaince.x) - Math.abs(distaince.y) > 5){ isMove = true; ev.preventDefault(); }else if(Math.abs(distaince.x) - Math.abs(distaince.y) < 5){ isMove = false; } if(isMove){ translatex = startOffset + distaince.x; list.style.transform = `translateX(${translatex}px)`; } }) wrap.addEventListener('touchend',()=>{ if(Math.abs(distaince.x) > imgW * proportion){ now -= distaince.x/Math.abs(distaince.x); } Array.from(dot).forEach((item,index)=>{ item.classList.remove('active'); if(index === (now%dot.length)){ item.classList.add('active'); } }); if(isMove){ translatex = now * -imgW; list.style.transition = '0.3s'; list.style.transform = `translateX(${translatex}px)`; } }) } </ script > </ body > </ html > |
锦上添花
按道理来讲,代码写到这里已经差不多了,但是我还是想给小按钮也加上点击事件,点击小按钮能跳转到对应的图片上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Array.from(dot).forEach((item,index)=>{ item.onclick = function (ev){ Array.from(dot).forEach((item)=>{ item.classList.remove( 'active' ); }) now = index; this .classList.add( 'active' ); translatex = now * -imgW; list.style.transition = '0.3s' ; list.style.transform = `translateX(${translatex}px)`; ev.preventDefault(); } }) |
这段代码应该没什么可讲的吧,不过接下来,神奇的事情就发生了:
懂了么? 原理都是通用的,懂了就分享给别人...
原文地址:https://tangjiusheng.cn/html/875.html