基于流式布局的轮播图实现思路

布局

这是一个流式布局下得轮播图案例,问题还比较多,可以提供一种不错的思路

通过让父元素禁止换行,每个图片子元素设置inline-block,流式排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
* {
padding: 0;
margin: 0;
}

#root {
width: 500px;
height: 280px;
margin:auto;
white-space: nowrap;
overflow: hidden;
}

#root>div {
width: 500px;
height: 280px;
display: inline-block;
transition: .5s linear;
}
1
2
3
4
5
6
<div id='root'>
<div style='background: url(./assets/1b809d9a2bdf3ecc481322d7c9223c21)'></div>
<div style='background: url(./assets/1b809d9a2bdf3ecc481322d7c9223c21)'></div>
<div style='background: url(./assets/1b809d9a2bdf3ecc481322d7c9223c21)'></div>
<div style='background: url(./assets/1b809d9a2bdf3ecc481322d7c9223c21)'></div>
</div>

开始拖动

通过CSS的transform属性来改变图片的位置,这里我们不让如父元素一起滚动,而是控制每一个元素的滚动,来看一下实现的思路

在鼠标按下的时候加一个标识,表示正在拖动,并且记录下鼠标点击的位置

1
2
3
4
root.addEventListener('mousedown', event => {
mark = true;
startX= event.clientX;
})

在鼠标移动的时候,判断是否,已经开始移动

1
2
3
document.addEventListener('mousemove', function (event) {
if (!mark) return false
});

由于是使用流式布局,每一个元素都在自己的位置上,而且偏移量为0

那么当前正在窗口中的图片的索引标记为pos,初始化为0

再滑动的时候需要知道当前窗口内的图片索引,当滑动距离的绝对值超过500,则pos切换为上一张图片,或下一张图片,

1
2
3
4
5
//错误,不能直接向下取整,因为向左滑动的时候不足500不能算作前一张,向上也同理
let current = position - Math.floor(x / 500);

// 需要去掉偏移量之后,看是否超过了500
let current = position - Math.ceil((x-x % 500) / 500);

下面,我们只关心pos左侧和右侧的图片是哪一张

如果是右边的一张图片,那索引就会 +1, 按照[1,2,3,0,1,2,3,0,...]的顺序循环,需要让他在到达最后索引的时候归零,这里可以通过取余实现

1
const right = (pos+1)%length

但是左边的一张图片,是按倒叙排列的 [3,2,1,0,3,2,1,0,...]pos===0左边一张图片的索引为3,也就是让位置[-1]和 图片索引[3]对应,这里我们还是通过取余操作,但是要先加上子元素的长度length,把它转为整数,对于右边的图片来说,对于多加的长度没有影响,因为加了一倍的长度,最终取余的时候还是可以约调

1
const index = (pos+ offset + lenth) % length

下面是如何把中间位置,和左右两张图片放到对应的位置

首先减去偏移量,把图片移动到窗口位置,类似绝对定位的效果,500为图片的宽度

1
let dx = - index * 500

在加上左右位置的偏移量,和鼠标滑动的偏移量

注意:为了学习理解,滑动的事件实在document上面的,所以滑动距离可能会超过500px,这里需要取余计算剩余偏移量

1
let dx  = -index * 500 + offset * 500 + x % 500 

最终滑动事件为

1
2
3
4
5
6
7
8
9
10
11
document.addEventListener('mousemove', function (event) {
if (!mark) return false
let x = event.clientX - startX;
let current = position - Math.ceil((x - x % 500) / 500);
for (let offset of [-1, 0, 1]) {
let pos = current + offset;
pos = (pos + children.length) % children.length
children[pos].style.transition = 'none';
children[pos].style.transform = `translateX(${- pos * 500 + offset * 500 + x % 500}px)`
}
});

结束拖动

鼠标松开的时候,需要知道偏移量是否超过组件宽度的一半,如果偏移量大于250就是下一张,如果小于-250就是前一张,有四种情况可以画数轴感受一下

1
2
3
4
if (x % 500 >= -250 && x % 500 <= 0) c = 1;
if (x % 500 <= 250 && x % 500 >= 0) c = -1
if (x % 500 < -250) c = -1
if (x % 500 > 250) c = 1

最终滚动时候开启动画,并且忽略鼠标拖动的偏移量,通过css动画让图片恢复到对应的位置上

1
2
3
4
5
6
7
8
9
10
11
document.addEventListener('mouseup', function (event) {
mark = false;
let x = event.clientX - startX;
position = position - Math.round(x / 500);
for (let offset of [0, c]) {
let pos = position + offset;
pos = (pos + children.length) % children.length
children[pos].style.transition = '';
children[pos].style.transform = `translateX(${- pos * 500 + offset * 500}px)`
}
});

上面判断前后哪一张的条件语句可以用数学的算法优化

1
2
3
4
5
6
7
8
9
10
11
document.addEventListener('mouseup', function (event) {
mark = false;
let x = event.clientX - startX;
position = position - Math.round(x / 500);
for (let offset of [0, Math.sign(x % 500 - Math.sign(x) * 250)]) {
let pos = position + offset;
pos = (pos + children.length) % children.length
children[pos].style.transition = '';
children[pos].style.transform = `translateX(${- pos * 500 + offset * 500}px)`
}
});
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2025 SunZhiqi

此时无声胜有声!

支付宝
微信