前端那些花里胡哨的动画效果—手势无缝轮播

前言


这次的动效属实是眼高手低了,之前在PC端也手撸过无缝轮播图,只不过已经有一段时间了,这次想着无非是移动端,换成touch事件控制轮播而已。结果没想到费了我两个晚上都在研究原理....

实际上移动端的轮播效果swiper上有很多好看的示例,这次的效果也是其中之一,只不过抱着学习的态度还是自己手动封装一个吧,而且原博客项目的轮播更加麻烦,在图片视口展示的三张图片切换时scale也是动态计算改变的,也就是说每次轮播都要动态计算三张图片的位置,通过位移比例来计算,只能说是头皮发麻一点思路都没有,最终还是放弃了,但是整体效果出来还算是不错的。


效果

手势轮播


无缝轮播



分析


这次的无缝轮播图难点主要有两个:

  1. 因为我想封装成组件库一样,通过封装swipe容器组件和swipe-item组件实现轮播,那少不了的就是插槽的使用,而这里最头疼的地方就是item组件需要访问容器组件的动态数据来设置样式
  2. 无缝轮播时由于首页展示的是三张图片而不是单独一张占满屏幕,因此原来的无缝轮播切换方案可能不会适应,因此需要采用新的方案


首先对于移动端的手势几个事件:touchStart、touchMove、touchEnd必须要提前了解,通过其传递的TouchEvent来计算位移的距离。既然封装成组件就要功能划分清楚,swipe容器组件主要用来绑定这些touch事件并控制移动,而item组件则需要改变过渡的样式,比如上面展示的scale大小改变以及无缝轮播的位移改变


实现

首先第一大难题:插槽,容器组件怎么拿到item组件的DOM实例来计算宽度?item组件怎样拿到容器组件的数据?通过getCurrentInstance API 获取到原DOM实例,instance.slots.default()[0].children能够拿到插槽内容,但是拿到的内容上面el是空的,无法拿到真正的DOM实例。打印出来instance挨个找真的头疼,但在实例上肯定是能够找的到这是毋庸置疑的,后来突然想到了Vant组件库的实现,就去扒了扒Vant swipe的源码,虽然使用tsx写的,但还是让我找到了点东西:


可以看到在swipe和swipe-item使用到了两个hook,useChildren、useParent,很明显就是通过这两个hook实现父子组件的信息共享:

源代码这里就不展示了,感兴趣的可以自己去扒扒,我这里是直接cv过来稍微改改类型就使用了,像useChildren这里,可以看到源码写了个递归实例上的一个subTree属性,这个上面就含有想要的DOM实例,再自己进行flat平铺:


第二大难题:无缝轮播,这里是参考原博主的实现,在swipe-item上巧用定位left实现,具体实现方法如下,根据当前处于位置,比如第一张,那么你的左侧需要展示最后一张来实现无缝轮播,而最后一张就需要定位到容器左侧,最后一张同理:

// 无缝轮播设置左右边界left位移,默认left为0
/**
* 第一张图片展示时,左侧则放置最后两张图片
* 最后一张图片展示时:右侧则放置前两站图片
*/
const swipeTran = () => {
const index = fsSwipe.swipe.index,
{ width } = fsSwipe.track;
if (index === 0 && [childLen.value - 1, childLen.value - 2].includes(key.value)) {
return -width;
} else if (index === childLen.value - 1 && [0, 1].includes(key.value)) {
return width;
} else {
return 0;
}
};

为什么必须要设置两张而不是一张?实践出真知,无缝切换时效果欠佳,这里就不再语言赘述了....

其次就是关于效果问题了,比如这里设置了一个过渡延迟,主要是由于无缝衔接时牵扯到left改变,这部分的过渡效果需要定时器限制

// 过渡延迟,主要处理无缝衔接的过渡效果
const onTransitionEnd = (callback: () => void) => {
setTimeout(() => {
callback();
}, 250);
};
// 设置偏移量
const setOffset = (index: number) => {
const { offsetX, width: itemWidth } = child;
touch.endX = track.offsetX = index * itemWidth - offsetX;
if (index >= childLength.value) {
swipe.syncIndex = 0;
onTransitionEnd(() => {
touch.isTouch = true;
touch.endX = track.offsetX = -offsetX;
swipe.index = 0;
});
} else if (index < 0) {
swipe.syncIndex = childLength.value - 1;
onTransitionEnd(() => {
touch.isTouch = true;
touch.endX = track.offsetX = (childLength.value - 1) * itemWidth - child.offsetX;
swipe.index = childLength.value - 1;
});
} else {
swipe.index = swipe.syncIndex = index;
}
};


其实还有很多细节,但是文字阐述比较困难,剩下的大部分就是轮播的基本操作,下面放下组件的源代码,代码比较堆屎山,将就着看吧,注意需要自己设置容器大小宽高等:

<template>
<div class="game-swipe">
<fs-swipe @change="handleChange">
<fs-swipe-item v-for="(item, index) in images">
<div class="game-swipe-item">
<img :src="item" />
</div>
</fs-swipe-item>
</fs-swipe>
</div>
</template>






0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
_Async__
作者分享
前端项目分享:Q Maker——仿问卷星低代码项目
25
2023年度总结:我们的生活没有“如果”
69
一个真实二本学院的秋招:停止内耗,接受自己的平庸
94
求职经验分享——两周的求职历程
22
#蓝桥杯# web 今年的蓝桥杯属实是上压力了,作为一名稀有的web组选手(今年我们软院名单上好像就我一个web组,去年两个[捂脸]),按照去年省赛的难度的话压根要不了4个小时,但刚拿到试卷扫了一眼,瞅到了最后两题的名字就感觉难度上来了。 不过前几道题难度确实不是很大,但很明显给人的感觉就是web组的出题更加成熟了,比如没有像去年那种写两行css就a一道题,纯css的题目像还原设计稿就没有了,不过第一道题的grid布局,确实还挺不错的,如果不是后面有个响应式的题用grid布局的代码我就g了,最后比葫芦画瓢也能写的出来。 其次就是个人感觉削弱了JQuery的比重,jQuery虽然也有使用但没有像去年题目那样带来极大的便利,这次我依旧是原生写JS,感觉使不使用差别不是特别大。 只能说有点水平的就是最后两题:markdown解析、树形结构拖拽 markdown的还好,一是看正则基础,二是对于blockquote和ul需要费点功夫,看得出来web组对于正则的要求一直都是有的。 最后一道就有点东西了,个人在上面那道题费了太多时间,最后一道时间明显不太够了只写了送分的那一问。大概看了看应该是原生手写递归组件,之前只用vue写过类似多级评论,递归菜单这样的。虽然原理都一样但是原生还是有点虚,第三问应该就是类似树形结构查找,扁平化删改这样,当时也没仔细看,一会儿扒扒原题再研究研究。 整体来讲还是比较考验基础的,像使用框架的题目分值就那么多,压轴的题永远是看你的原生JS能力。难度确实比去年省赛的题目大,但个人感觉这才是正常的比赛,去年省赛和国赛的难度差别过大反而不太适应,今年听天由命得了,也是最后一次了,可能要捐款了。。
19
下载 APP