Vue组件-景深卡片轮播

2,899 阅读6分钟

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

前言

朋友的朋友做了个首页,首页用到一个卡片轮播,大概就是这个样子的:

Animation.gif

第一版他们是开发出来了,但是各种bug,希望我帮忙改一下。

看完代码以后,发现他们整合了一个缝合怪出来,各个楼层都是从其他地方 CV 过来的,而且各个楼层引用了 n 多个js 和 css 文件,看了一下效果以后,觉得改他的东西确实比重新开发一个要麻烦的多得多,所以就重新写了一下。

在此记录一下,以便于后续复用。

需求拆解

  1. 初始化渲染5个容器,用来承载图片和标题,按照景深的效果排列,越靠近自己越大。
  2. 点击中间一块的左边的整体向右旋转一格,点击右边的向左旋转一格,点击中间的没有效果。
  3. 中间一项显示 content查看更多 按钮,其他不显示。

开发思路(vue2)

  1. 景深效果可以选择使用 css3 的转换来制作,通过对每个容器进行不通比例的缩放来实现5个容器大小不一致的效果。
  2. 因为使用到了css3的转换,就需要用到过渡,动画效果就使用 transition-group 来实现。
  3. 至此就可以轮播就可以滚动起来了,只需要给5个容器循环赋值css样式,就可以实现缩放和位置转换,结合渐变,就成了一个循环动画效果。
  4. 点击事件,一开始的思路是通过获取点击事件中的鼠标的坐标,如果在左半个屏幕就走左边的逻辑,但是页面有个最小宽度 1280,当分辨率被拖动的时候就不行了,所以pass掉了。最后选择的方案是,给容器一套下标(_id)的同时全局声明一个变量用来记录当前展示的容器的下标,每次点击都要和展示项作比较,通过比较结果来确定左右逻辑。
  5. 思维发散:
    • 可以扩展为7个甚至更多容器的轮播
    • 扩展为n个容器,但是只显示5个的轮播
    • 景深效果以及动画效果改变
    • ...

开发过程

首先是html+css部分,这部分很简单,而且很普通。

  1. 写 5 个div,每个都用绝对定位的方式铺满整个屏幕,每个宽度都是 20%。
  2. 绑定各数据,并用 几个 状态值来控制 content 的显示隐藏,以及当前展示项。

代码如下:


<template>
  <div class="card-loop">
    <transition-group>
      <div
        v-for="(item, idx) in loopModules"
        :key="idx"
        class="loop-item"
        :style="item.style"
        @click="handleLoop(idx)"
      >
        <img :src="baseData[idx].cover" :alt="baseData[idx].cover" />
        <p class="title">{{ baseData[idx].title }}</p>
        <transition
          name="fade"
          enter-active-class="animate__animated animate__fadeInUp"
          leave-active-class="animate__animated animate__fadeOutDown"
        >
          <div v-show="idx === loopCenterIdx">
            <p class="content">{{ baseData[idx].desc }}</p>
            <p class="tool"><a href="#">查看更多</a></p>
          </div>
        </transition>
      </div>
    </transition-group>
  </div>
</template>

<style lang="scss" scoped>
.card-loop {
  width: 100%;
  height: 400px;

  margin-top: 170px;
  position: relative;
  .loop-item {
    width: calc(100% / 5);
    float: left;
    box-sizing: border-box;

    position: absolute;

    transition: all ease-out 0.5s;
    transform-origin: 50% 50%;

    img {
      width: 100%;
    }
    div {
      background: #fff;
      padding-bottom: 5px;
    }
    p.title {
      padding: 3px;
      color: #333;
      font-weight: bolder;
      transform: scale(0.9);
    }
    p.content {
      font-size: 6px;
      color: #999;
      overflow: hidden;
      display: -webkit-box; //将元素设为盒子伸缩模型显示
      -webkit-box-orient: vertical; //伸缩方向设为垂直方向
      -webkit-line-clamp: 2; //超出2行隐藏,并显示省略号
      transform: scale(0.9);
    }
    p.tool {
      text-align: center;
      transform: scale(0.7);
    }
  }
}
</style>

<script>
// 引入图片资源
import loop1 from '@assets/images/loop1.png';
import loop2 from '@assets/images/loop2.png';
import loop3 from '@assets/images/loop3.png';
import loop4 from '@assets/images/loop4.png';
import loop5 from '@assets/images/loop5.png';

/**
 * 根据 id 获取下标
 * @params arr object[]
 * @params id number
 * @result number n>=-1
 */
const findIdxById = (arr, id) => {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === id) {
      return i;
    }
  }
  return -1;
};

export default {
  data() {
    return {
      // 轮播数据
      baseData: [
        {
          desc:
            '可折叠式智能移动影院Royle-X展现了惊人的高科技技术和全球首创的可折叠设计,配备了柔宇科技自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折叠计自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折',
          title: '这里是标题',
          cover: loop1,
        },
        {
          desc:
            '可折叠式智能移动影院Royle-X展现了惊人的高科技技术和全球首创的可折叠设计,配备了柔宇科技自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折叠计自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折',
          title: '这里是标题',
          cover: loop2,
        },
        {
          desc:
            '可折叠式智能移动影院Royle-X展现了惊人的高科技技术和全球首创的可折叠设计,配备了柔宇科技自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折叠计自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折',
          title: '这里是标题',
          cover: loop3,
        },
        {
          desc:
            '可折叠式智能移动影院Royle-X展现了惊人的高科技技术和全球首创的可折叠设计,配备了柔宇科技自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折叠计自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折',
          title: '这里是标题',
          cover: loop4,
        },
        {
          desc:
            '可折叠式智能移动影院Royle-X展现了惊人的高科技技术和全球首创的可折叠设计,配备了柔宇科技自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折叠计自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折自助研发的首款智能移动显示操作系统,设计非常人性化。可能会可折',
          title: '这里是标题',
          cover: loop5,
        },
      ],

      // 当前展示项
      loopCenterIdx: 2,

      // 轮播样式模板,用来循环赋值给 容器
      loopModules: [
        {
          loopIdx: 0,
          style: {
            'z-index': 7,
            background: '#fff',
            transform: `scale(${1})`,
            left: 'calc(100% / 5 * 0)',
            height: '320px',
            opacity: 0.8,
          },
        },
        {
          loopIdx: 1,
          style: {
            'z-index': 8,
            transform: `scale(${1.4})`,
            background: '#fff',
            left: 'calc(100% / 5 * .9)',
            height: '320px',
            opacity: 0.9,
          },
        },
        {
          loopIdx: 2,
          style: {
            transform: `scale(${1.7})`,
            'z-index': 9,
            background: '#fff',
            left: 'calc(100% / 5 * 2)',
            opacity: 1,
            'box-shadow': '0 0 5px #ccc',
            height: '370px',
          },
        },
        {
          loopIdx: 3,
          style: {
            transform: `scale(${1.4})`,
            'z-index': 8,
            background: '#fff',
            left: 'calc(100% / 5 * 3.1)',
            opacity: 0.9,
            height: '320px',
          },
        },
        {
          loopIdx: 4,
          style: {
            'z-index': 7,
            transform: `scale(${1})`,
            background: '#fff',
            left: 'calc(100% / 5 * 4)',
            height: '320px',
            opacity: 0.8,
          },
        },
      ],
    };
  },
  created(){
    // todo Ajax ...
  },  

  mounted() {
    // 初始化轮播
    this.initLoop();
    console.log('centerIdx = ', this.loopCenterIdx);
  },

  methods: {
    // 初始化轮播
    initLoop() {
      this.loopModules = this.loopModules.map((item, idx) => {
        item.desc = this.baseData[idx];
        return item;
      });
      console.log(this.loopModules);
    },

    // 下一张
    handleNext() {
      this.loopCenterIdx = this.loopCenterIdx + 1 > 4 ? 0 : this.loopCenterIdx + 1;
      this.loopModules = this.loopModules.map((item, idx) => {
        item.desc = this.baseData[idx];
        return item;
      });
      this.loopModules.unshift(this.loopModules.pop());
    },

    // 上一张
    handleLast() {
      this.loopCenterIdx = this.loopCenterIdx - 1 < 0 ? 4 : this.loopCenterIdx - 1;

      this.loopModules = this.loopModules.map((item, idx) => {
        item.desc = this.baseData[idx];
        return item;
      });
      this.loopModules.push(this.loopModules.shift());
    },

    // 点击容器
    handleLoop(checkId) {
      console.log('checkId = ', checkId, ', loopCenterIdx = ', this.loopCenterIdx);
      // 当前项
      if (checkId === this.loopCenterIdx) {
        return;
      }

      if (checkId > this.loopCenterIdx) {
        if (this.loopCenterIdx === 0 && checkId === 4) {
          return this.handleLast(checkId);
        } else {
          return this.handleNext(checkId);
        }
      }

      if (checkId < this.loopCenterIdx) {
        if (checkId === 0 && this.loopCenterIdx === 4) {
          return this.handleNext(checkId);
        } else {
          return this.handleLast(checkId);
        }
      }
    },
  },
};
</script>

js代码里面有两个地方需要注意的:

  1. 可以看到在上一张下一张对应的方法里面,有两个一行代码。其作用就是让5个容器转起来
this.loopModules.unshift(this.loopModules.pop());
// [1,2,3,4] => [2,3,4,1]
this.loopModules.push(this.loopModules.shift());
// [1,2,3,4] => [4,1,2,3]
  1. handleLoop 方法中,需要注意展示项为 第一个 和 最后一个 的时候,和正常判断逻辑相反。

后记

轮播作为一个日常开发中最常见的组件,想必每一个前端都在学习js的时候将他作为一个很有意思的 demo 练过手,而景深卡片轮播算是比基础轮播稍微难一点的效果了。

这个组件值得记录下来的是把数据驱动视图的思路更好的进行了一次展示,这也是基于框架开发和原生开发的一个重要的转变。如果在 jquery 中,可能会用 $(selector).animate() 去控制动画效果,原生 js 中也会封装一个类似 animate() 的方式去逐帧的实现一系列的动画,而在 vue 中,直接修改数据的顺序,或者修改数据值,就让 dom 动起来了。

数据驱动视图的思路下,我们就不必去关注视图的变化,也不用频繁的去操作dom元素,只需考虑清除一组可以控制视图变化的链路就好了。例如:分页组件只需要关注当前页码和每页显示数量,表格组件只需要关注列数据的变化等等。其他的部分框架已经做了很多。

路还很长,每走一步都有新感悟!