Vue + AnimeJS 实现3d轮播图

    轮播图在开发中是经常用到的,3D轮播图是其中最常用的一种,这里使用Vue3+animejs来实现,效果如下:
Vue + AnimeJS 实现3d轮播图

基础布局

    首先需要设置轮播容器中的子元素位于3D容器中,这是需要设置transform-style。transform-style有两个值,flat:设置元素的子元素位于该元素的平面中、preserve-3d:设置元素的子元素位于3D空间中。
    与之对应的还需要设置perspective,指与z=0平面的距离
    实现3d效果的轮播图主要需要动态修改transform中translateX,rotateY,scale以及每个图片的层级、透明度。
Vue + AnimeJS 实现3d轮播图
    由上图可知,缩放比例、层级、透明度三个属性中间值最大,然后两边的值逐级递减。在X轴上的偏移值是以中间图片为基准向左(逐级递减)、向右偏移(逐级递增)。Y轴上的旋转以中间图片为基准左边顺时针旋转,右边逆时针旋转。
    这里轮播图为无限轮播效果,处理方式是在初始化的时候保存上述五个参数,因为怎么循环,固定位置对应的五个参数是相同的,在切换的时候就不需要重新计算。
    因为使用的是vite,获取静态文件资源的时候需要使用new URL()方法来实现资源的引用。初始化代码如下:

<div style="display: flex; align-items: center; justify-content: center">
  <el-icon size="30" @click="clickArrow('left')"><ArrowLeft /></el-icon>
  <div class="carousel-wrapper" ref="carouselRef">
    <div
      v-for="(carousel, index) in carouselList"
      :key="carousel.id"
      :data-base-id="carousel.id"
      class="item"
      :style="computedStyle(index)"
    >
      <img :src="carousel.url" />
    </div>
  </div>
  <el-icon size="30" @click="clickArrow('right')"><ArrowRight /></el-icon>
</div>
const carouselList = ref([
  {
    id: 1,
    url: new URL('../../assets/images/1.jpg', import.meta.url).href
  },
  {
    id: 2,
    url: new URL('../../assets/images/2.jpg', import.meta.url).href
  },
  {
    id: 3,
    url: new URL('../../assets/images/3.jpg', import.meta.url).href
  },
  {
    id: 4,
    url: new URL('../../assets/images/4.jpg', import.meta.url).href
  },
  {
    id: 5,
    url: new URL('../../assets/images/5.jpg', import.meta.url).href
  }
])

const carouselRef = ref()
type CarouseInfo = {
  x: number[]
  y: number[]
  z: number[]
  opacity: number[]
}
const carouselInfo = reactive<CarouseInfo>({
  x: [],
  y: [],
  z: [],
  opacity: []
})

const baseParams = {
  x: 150,
  xOffset: -300,
  y: -25,
  yOffset: 50,
  opacity: 0.6,
  opacityOffset: 0.2
}

const computedStyle = (i: number) => {
  const { opacity, opacityOffset, xOffset, yOffset } = baseParams
  let z = 0
  let opacityNum = 0
  let x = i * baseParams.x + xOffset
  let y = i * baseParams.y + yOffset
  const centerIndex = (carouselList.value.length - 1) / 2

  if (i <= centerIndex) {
    z = i * 5
    opacityNum = i * opacityOffset + opacity
  } else {
    const baseIndex = i - centerIndex * (i - centerIndex)
    z = baseIndex * 5
    opacityNum = baseIndex * opacityOffset + opacity
  }
  carouselInfo.x.push(x)
  carouselInfo.y.push(y)
  carouselInfo.z.push(z)
  carouselInfo.opacity.push(opacityNum)
  return {
    transform: `translateX(${x}px) rotateY(${y}deg) scale(${opacityNum})`,
    zIndex: z,
    opacity: opacityNum
  }
}
.carousel-wrapper {
  width: 900px;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  transform-style: preserve-3d;
  overflow: hidden;
  perspective: 1000px;
}
.item {
  position: absolute;
  user-select: none;
}

轮播

    在项目初始化时保存了初始化参数,点击左侧按钮时,分别获取保存的五个参数中的第一个,然后放到数组中的末尾,点击右侧按钮时,分别获取五个参数中的最后一个,放到数组中的最前面,代码如下:

const switchPosition = (val: number[], type = 'right') => {
  const target = type === 'right' ? val.shift() : val.pop()
  type === 'right' ? val.push(target!) : val.unshift(target!)
  return val
}

    为了更好的处理动画,这里使用animejs处理,根据点击之后的参数,重新遍历dom节点,修改每个dom的偏移值、旋转角度、缩放比例、透明度,因为没有在animejs中找到设置层级的属性,所以这里使用原生设置层级。代码如下:

const clickArrow = (arrowType: string) => {
  const x = switchPosition(carouselInfo.x, arrowType)

  const y = switchPosition(carouselInfo.y, arrowType)
  const z = switchPosition(carouselInfo.z, arrowType)
  const opacity = switchPosition(carouselInfo.opacity, arrowType)

  Array.from(carouselRef.value.children).forEach((item, index) => {
    ;(item as HTMLDivElement).style.zIndex = z[index].toString()
    anime({
      targets: item as HTMLDivElement,
      translateX: x[index],
      rotateY: y[index],
      scale: opacity[index],
      opacity: opacity[index],
      easing: 'linear'
    })
  })
}

    至此,一个能够循环的3D轮播图就完成了。代码示例如下:
stackblitz.com/edit/vitejs… (图片太大了 大家可以自行替换)

原文链接:https://juejin.cn/post/7327547320832278582 作者:Kakarotto

(0)
上一篇 2024年1月25日 上午10:05
下一篇 2024年1月25日 上午10:16

相关推荐

发表回复

登录后才能评论