所属分类:web前端开发
不知道大家有没有遇到这样的需求或者说看到类似的效果,就是列表进去详情看轮播图的时候,当手指滚动轮播图时轮播的高度容器会自适应,这样下面的内容就向上挤,滑动的过程会计算高度,释放的时候也会滚到下一张,也会计算对应图片的高度,然后做一个缓动的动画效果。就像下面这张图的样子。
可以看到上面的图片内容文字,随着轮播的滑动高度也在变化。费话不多说直接上代码。
实现方法
可以通过监听鼠标mounse
或者手指的滑动 touch
事件来控制图片,这里本文只说一下轮播的功能实现思路,重点说的是怎么实现高度的自适应。
直接开始正文,先看 html 代码结构。
html 结构
<div class="container">
<div class="wrapper">
<div class="swiper">
<div class="item">
<img src="https://ci.xiaohongshu.com/776d1cc7-ff36-5881-ad8f-12a5cd1c3ab3?imageView2/2/w/1080/format/jpg" alt="">
</div>
<div class="item">
<img src="https://ci.xiaohongshu.com/b8e16620-66a0-79a5-8a4b-5bfee1028554?imageView2/2/w/1080/format/jpg" alt="">
</div>
<div class="item">
<img src="https://ci.xiaohongshu.com/e12013c2-3c46-a2cc-7fda-1e0b20b36f3d?imageView2/2/w/1080/format/jpg" alt="">
</div>
</div>
</div>
<div class="content">这是一段内容</div>
</div>
登录后复制
css 样式
.container {
width: 100%;
overflow: hidden;
}.wrapper {
width: 100%;
}.swiper {
font-size: 0;
white-space: nowrap;
}.item {
display: inline-block;
width: 100%;
vertical-align: top; // 一定要使用顶部对齐,不然会出现错位的情况
}.item img {
width: 100%;
height: auto;
display: block;
}.content {
position: relative;
z-index: 9;
font-size: 14px;
text-align: center;
padding-top: 20px;
background-color: #fff;
height: 200px;
}
登录后复制
值得注意的地方有几点;
white-space
时,子集元素设置 display: inline-block
会出现高度不同的排列错位,解决办法就是加上一句 vertical-align: top
,具体什么原因我也不细讲了。font-size: 0
,如果没加上的话,就会出现两个子集有空隙出现,加上之后空隙就会去掉。100%
还要加上 display: block
,没有的话底部就会出现间隙。写好上面的 html
容器部分和 样式,下面就看一下 js
上是怎么处理的。
Js 实现
开始之前我们先思考一下去怎么实现这个轮播以及高度的自适应问题,分为几步操作;
transform
位置,中间还做其他的边界处理,当然还有高度的变化。transition
过渡动画即可。按照我们试想的思路,开始正文;
const data = {
ele: null,
width: 0,
len: 0,
proportion: .3,
type: false,
heights: [500, 250, 375],
currentIndex: 0,
startOffset: 0,
clientX: 0,
distanceX: 0,
duration: 30,
touching: false
}
const wrapper = data.ele = document.querySelector('.wrapper')
const items = document.querySelectorAll('.item')
data.width = wrapper.offsetWidth
data.len = items.length - 1
wrapper.addEventListener('touchstart', onStart)
wrapper.addEventListener('mousedown', onStart)
登录后复制
注意,这里在做高度之前,我们需要等图片加载完成之后才能拿到每一个元素的高度,我这里为了省懒就没写具体代码,上面的 heights
对应的是每个图片在渲染之后的高度,一般情况下最好让后端传回来带宽高,这样就不需要用 onload
再去处理这个。
function onStart(event) {
if (event.type === 'mousedown' && event.which !== 1) return
if (event.type === 'touchstart' && event.touches.length > 1) return
data.type = event.type === 'touchstart'
const events = data.type ? event.touches[0] || event : event
data.touching = true
data.clientX = events.clientX
data.startOffset = data.currentIndex * -data.width
data.ele.style.transition = `none`
window.addEventListener(data.type ? 'touchmove' : 'mousemove', onMove, { passive: false })
window.addEventListener(data.type ? 'touchend' : 'mouseup', onEnd, false)
}
登录后复制
上面的代码里面我做了PC和移动端的兼容,跟计划的一样,保存一下 clientX
坐标和一个初始的坐标 startOffset
这个由当前索引和父级宽度计算得到,场景是当从第二张图片滚动到第三张图片时,会把之前的第一张图片的距离也要加上去,不然就计算错误,看下面滑动时的代码。
另外在做监听移动的时候加上了 passive: false
是为了在移动端兼容处理。
function onMove(event) {
event.preventDefault()
if (!data.touching) return
const events = data.type ? event.touches[0] || event : event
data.distanceX = events.clientX - data.clientX
let translatex = data.startOffset + data.distanceX
if (translatex > 0) {
translatex = translatex > 30 ? 30 : translatex
} else {
const d = -(data.len * data.width + 30)
translatex = translatex < d ? d : translatex
}
data.ele.style.transform = `translate3d(${translatex}px, 0, 0)`
data.ele.style.webkitTransform = `translate3d(${translatex}px, 0, 0)`
}
登录后复制
做了一个边界处理的,超了 30 的距离就不让继续滑动了,加上之前保存的 startOffset
的值,得到的就是具体移动的距离了。
function onEnd() {
if (!data.touching) return
data.touching = false
// 通过计算 proportion 滑动的阈值拿到释放后的索引
if (Math.abs(data.distanceX) > data.width * data.proportion) {
data.currentIndex -= data.distanceX / Math.abs(data.distanceX)
}
if (data.currentIndex < 0) {
data.currentIndex = 0
} else if (data.currentIndex > data.len) {
data.currentIndex = data.len
}
const translatex = data.currentIndex * -data.width
data.ele.style.transition = 'all .3s ease'
data.ele.style.transform = `translate3d(${translatex}px, 0, 0)`
data.ele.style.webkitTransform = `translate3d(${translatex}px, 0, 0)`
window.removeEventListener(data.type ? 'touchmove' : 'mousemove', onMove, { passive: false })
window.removeEventListener(data.type ? 'touchend' : 'mouseup', onEnd, false)
}
登录后复制
通过计算 proportion
滑动的阈值拿到释放后的索引,也就是超过父级宽度的三分之一时释放就会滚动到下一张,拿到索引之后就可以设置需要移动的最终距离,记得加上 transition
做一个缓动效果,最后也别忘记移除事件的监听。
至此上面的简单的轮播效果就大功告成了,但是还缺少一点东西,就是本篇需要讲的自适应高度,为了方便理解就单独拿出来说一下。
高度自适应
在移动时就可以在里面做相关的代码整理了, onMove
函数里加上以下代码,来获取实时的高度。
const index = data.currentIndex
const currentHeight = data.heights[index]
// 判断手指滑动的方向拿到下一张图片的高度
let nextHeight = data.distanceX > 0 ? data.heights[index - 1] : data.heights[index + 1]
let diffHeight = Math.abs((nextHeight - currentHeight) * (data.distanceX / data.width))
let realHeight = currentHeight + (nextHeight - currentHeight > 0 ? diffHeight : -diffHeight)
data.ele.style.height = `${realHeight}px`
登录后复制
这里是移动时的高度变化,另外还需要在释放时也要处理, onEnd
函数里加上以下代码。
// ... 因为上面已经拿到了下一张的索引 currentIndex
const currentHeight = data.heights[data.currentIndex]
data.ele.style.height = `${currentHeight}px`
登录后复制
因为上面已经拿到了下一张的索引 currentIndex
所以再滚动到下一张是就直接通过数据获取就可以了。
以上就是实例详解如何给轮播图做自适应的高度的详细内容,更多请关注zzsucai.com其它相关文章!