工具-巧用数组滚动实现复杂轮播
我又来做轮播了,感觉对轮播有着很强的执念。
在上次的一个轮播中使用到了一个很小的单行方法,并没有将之转换为一个独立的函数,后来在评论里面回答小伙伴的评论的时候又关注到了这里,对这个单行方法又多看了一下,整理了成了一个常用的基础工具函数。
数组滚动
首先介绍一下我所说的数组滚动,数组滚动的就是让数组滚动起来,不知道大家有没有遇到这样的需求:
[1,2,3] => [2,3,1] => [3,1,2]
一般来说在做游戏或者做动画效果的时候会用到。
let array = [0, 1, 2, 3, 4];
/**
* 数组滚动
* @param {any[]} arr 需要被操作的数组
* @param {*} times
* @returns
*/
const rollTimes = (arr = [], times = 1) => {
// 异常参数处理
if (!Array.isArray(arr)) {
console.error(
"rollTimes(any[], number) 入参错误! 应该是 array 类型,但是收到一个",
typeof arr,
"类型。"
);
return;
}
// 深拷贝数据,避免对源数据造成影响
let result = JSON.parse(JSON.stringify(arr));
/* 如果滚动次数为0,返回源数据的拷贝体,即本方法可以实现单层数据的深拷贝 */
if (times === 0) {
result = result;
/* 如果滚动次数小于0,则向左滚动,即第1个变成最后一个,其他依次向左移动一个 */
} else if (times < 0) {
for (let i = 0; i > times; i--) {
result.push(result.shift());
}
/* 如果滚动次数大于0,则向右滚动,即最后一个变成第一个,其他依次向左移动一个 */
} else {
for (let i = 0; i < times; i++) {
result.unshift(result.pop());
}
}
return result;
};
console.log(rollTimes(123));
console.log(rollTimes(array, 1));
// [ 4, 0, 1, 2, 3 ]
console.log(rollTimes(array, -1));
//[ 1, 2, 3, 4, 0 ]
如代码所示,方法的核心是 result.push(result.shift())
和 result.unshift(result.pop())
这两个单行代码。
需要确认的是 result.shift() 和 result.pop() 方法会返回被移除的项,然后被 push 和 unshift 接收,因为push 和 unshift 方法会修改原始数据,所以这里进行了简单的深拷贝数据。
最终返回滚动对应次数的结果。
实际使用
分析以后感觉这个方法和轮播很搭,一些复杂的轮播,可以结合这个滚动函数很容易的就实现。
效果图如下:
源码奉上:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轮播页面</title>
<style>
* {
margin: 0;
padding: 0;
}
#wrap {
height: 500px;
width: 1260px;
border: 1px solid #eee;
margin: 0 auto;
position: relative;
perspective: 800px; /* 景深 */
transform-style: preserve-3d; /* 让我的元素成3D在舞台上呈现 */
}
.wrap-item {
position: absolute;
height: 250px;
width: 20%;
/* border: 1px solid #000; */
top: 5%;
-webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left
bottom, from(transparent), to(rgba(250, 250, 250, 0.5)));
background-size: auto 100%;
background-repeat: no-repeat;
background-position: center;
transform-origin: 50% 50% -1000px;
transition: linear 0.5s;
left: 40%;
}
</style>
</head>
<body>
<div id="wrap"></div>
</body>
<script>
/**
*
* @param {any[]} arr 需要被操作的数组
* @param {*} times
* @returns
*/
const rollTimes = (arr = [], times = 1) => {
// 异常参数处理
if (!Array.isArray(arr)) {
console.error(
'rollTimes(any[], number) 入参错误! 应该是 array 类型,但是收到一个',
typeof arr,
'类型。'
)
return
}
// 深拷贝数据,避免对源数据造成影响
let result = JSON.parse(JSON.stringify(arr))
/* 如果滚动次数为0,返回源数据的拷贝体,即本方法可以实现单层数据的深拷贝 */
if (times === 0) {
result = result
/* 如果滚动次数小于0,则向左滚动,即第1个变成最后一个,其他依次向左移动一个 */
} else if (times < 0) {
for (let i = 0; i > times; i--) {
result.push(result.shift())
}
/* 如果滚动次数大于0,则向右滚动,即最后一个变成第一个,其他依次向左移动一个 */
} else {
for (let i = 0; i < times; i++) {
result.unshift(result.pop())
}
}
return result
}
class LoopEvent {
constructor(id, arr, styleList) {
this.$wrap = document.getElementById(id)
this.dataList = arr
this.styleList = styleList
this.wrapList = []
this.initWrap()
}
initWrap() {
this.initItems()
this.setStyles()
}
setStyles() {
this.wrapList.forEach((item, i) => {
Object.keys(this.styleList[0]).forEach((s) => {
item.style[s] = this.styleList[i][s]
})
})
}
initItems() {
console.log(this.dataList)
this.dataList.forEach((ele) => {
const item = document.createElement('div')
item.className = 'wrap-item'
item.style.backgroundImage = 'url("' + ele.image + '")'
this.$wrap.appendChild(item)
this.wrapList.push(item)
})
}
animate(type) {
this.styleList = type
? rollTimes(this.styleList, -1)
: rollTimes(this.styleList, 1)
this.setStyles()
}
}
const wrapLoop = new LoopEvent(
'wrap',
[1, 2, 3, 4, 5, 6, 7].map((item) => ({
id: item,
image: `./static/img/${item}.jpg`,
})),
[
{
transform: 'rotateY(-45deg)',
},
{
transform: 'rotateY(-30deg)',
},
{
transform: 'rotateY(-15deg)',
},
{
transform: 'rotateY(0deg)',
},
{
transform: 'rotateY(15deg)',
},
{
transform: 'rotateY(30deg)',
},
{
transform: 'rotateY(45deg)',
},
]
)
const timer = setInterval(() => {
wrapLoop.animate(false)
}, 2000)
</script>
</html>
通过查看 html 不难看出,动画核心是 LoopEvent 的第三个 参数 styleList,通过滚动 styleList 然后重新赋值给每个滚动项,然后经由 transition 过渡,就可以实现复杂的轮播啦!
首先要把公共的样式在 style 里面写好,然后在数组里面只保留 每个的特殊样式,直接组成复杂轮播的基础结构,然后就滚动样式数组。
当然这个方案也有弊端,就是每一帧的动画效果完全由 transition 来控制,我们是无法去逐帧操作的。如果需要用到这么细的操作则需要在滚动的方法里面进行计算了。
后记
通过这样设计,就实现了动画和数据的解耦。当然,这个只是个demo,真实场景会增加一些复杂的元素和交互的按钮。但是随着网上的资源越来越丰富,已经很少有人会一行一行的去写轮播了,唉!
现在的工作也是,一直纠结于 curd ,一些css3的属性都不会用了,本文里面用到的景深、3D 转换都不会玩了。
以后有时间再搞一下3D动画方面的小玩意。
转载自:https://juejin.cn/post/7180283018559356985