✨✨做一个伪3D效果的卡片列表来复习一下CSS动画吧~

简介: ✨✨做一个伪3D效果的卡片列表来复习一下CSS动画吧~

前言


本来是准备接着前面做的那个 使用SVG实现动态分布的圆环发散路径动画 的效果,希望通过 纯 Div + CSS 的方式来实现。但是目前看起来进度比较缓慢,虽然做出来了大致样式,但是动画还没加上,所以得后面再继续弄。


今天主要是通过写一个 “伪3D” 的卡片组效果,顺道也复习一下基础的 CSS 动画。


因为作者平时都是写的后台管理的项目,所以接触 CSS 动画之类的东西也比较少,实现方式和效果看起来可能没有那么好,希望大家多多包涵。


开始


本身这里最初的设想是希望 像打牌那样的初始效果,然后 通过点击卡片抽出单张选中卡片预览,再加上一些简单的过渡动画。不过后来又觉得 直接做成平铺列表然后选中卡片移动到最右侧 的效果也行,所以干脆都加上算了。


当然为了保证正确的层级关系,还是使用了 定位配合 zIndex 来确定每个卡片的 层级顺序和位置


卡片的排列模式也是通过一个变量来确定的,然后增加了一个切换按钮,后面如果有需要的话改成 props 配置也行。


话不多说,直接开干吧。


模拟数据


当然,在开始之前肯定要 模拟一个卡片数组 cards 与一个排列模式变量 clutter


data() {
  return {
    activeIndex: -1,
    clutter: true, // 杂乱
    cards: []
  };
},
created() {
  this.initData();
},
methods: {
  initData() {
    const arr = new Array(12).fill(1);
    this.cards = arr.map((_, index) => {
      return this.computedStyle(index, 12);
    });
  },
  resetData() {
    this.clutter = !this.clutter;
    this.initData();
  },
  computedStyle(index, length) {
    const clutter = this.clutter;
    const defaultStyles = {
        "--max-index": length + 1,
        "--bg-color": randomRgbColor(),
        "--card-index": index
    };
    return defaultStyles;
  }
}


这里的 initDataresetData 肯定就不需要介绍啦,就是重置和切换排列模式,当然因为改变排列模式后样式也有改动,所以在resetData 中也重新调用了initData来重新生成卡片数组。


至于computedStyle则是 计算每个卡片的样式并返回样式结果的,不管什么排列模式下 这几个 CSS 变量都是必须且固定的,所以就先放上来了。


data 中的activeIndex 则是代表当前激活的卡片下标的。


键盘事件


这里的初衷是为了方便 快速切换激活卡片、在卡片太多时也可以减少误触


这里唯一注意的一点就是,在执行键盘监听事件的时候,需要判断一下当前的 focus 元素,如果是在一些可聚集的元素中则不能触发翻页


mounted() {
  let addIndex = () => {
    if (this.activeIndex < this.cards.length - 1) {
      this.activeIndex++;
    } else {
      this.activeIndex = 0;
    }
  };
  let lessIndex = () => {
    if (this.activeIndex > 0) {
      this.activeIndex--;
    } else {
      this.activeIndex = this.cards.length - 1;
    }
  };
  let keyboardDeal = (e) => {
    if (document.activeElement !== document.body) return;
    // 方向键--上
    if (e.keyCode === 38) {
      addIndex();
    }
    // 方向键--下
    if (e.keyCode === 40) {
      lessIndex();
    }
    // 方向键--左
    if (e.keyCode === 37) {
      lessIndex();
    }
    // 方向键--右
    if (e.keyCode === 39) {
      addIndex();
    }
  };
  window.addEventListener("keyup", keyboardDeal);
  this.$on("hook:beforeDestroy", () => {
    window.removeEventListener("keyup", keyboardDeal);
  });
},


另外,这里通过 this.$on("hook:beforeDestroy") 来注册组件 在销毁之前需要执行的回调函数功能与选项式 API 中的 beforeDestroy 配置项功能一致


在组件即将销毁时,会清除掉组件中的键盘监听事件。


通过document.activeElement 是否等于 body 元素,来确定当前焦点位置。一般只有在该属性等于body的时候,才代表当前页面中没有其他聚焦元素,可以正常执行我们定义的按键翻页。


模板


至于 html 部分,倒是没有太多细节的内容,仅仅只是 Vue 的遍历语法和动态样式两个知识点,相信大部分同学都很清楚。


<template>
  <div class="AnimationCards">
    <h1>AnimationCards Page</h1>
    <p>
      <el-button @click="resetData">乱序</el-button>
    </p>
    <div class="demo-content">
      <div class="animation-cards-box">
        <div
          v-for="(styles, index) in cards"
          :class="[
            'animation-card',
            { 'is-active': activeIndex === index, 'is-clutter': clutter, 'is-list': !clutter }
          ]"
          :key="index"
          :style="styles"
          @click="activeIndex = activeIndex === index ? -1 : index"
        >
          <span>Card {{ index }}</span>
        </div>
      </div>
    </div>
  </div>
</template>


这里动态绑定的 is-clutter 和 is-list 其实也可以放到父级 animation-cards-box 中


至于每个 card 元素动态绑定的 style,里面有定义好的 CSS 变量,提供给每个卡片的动画和默认样式使用。这个用法我在之前的文章中也提到过,标签的内联样式也可以直接声明 CSS 变量


样式与动画


因为采用了两种排列方式,每种排列方式的动画都不一样,所以样式和动画都有不同

但是因为共同都是采用的定位来确定层级的,所以也有一样的地方。


下面这部分是一样的样式部分:


.animation-cards-box {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 20px;
  position: relative;
  .animation-card {
    width: 200px;
    background-color: var(--bg-color);
    border-radius: 8px;
    cursor: pointer;
    position: absolute;
    top: 20px;
    bottom: 20px;
    box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.8);
    transition: all ease-in-out 0.4s;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 32px;
    font-weight: bold;
    color: #ffffff;
    z-index: var(--card-index);
  }
}


通过上面模板部分绑定的 CSS 变量来确定具体样式。


列表状态


列表分布的状态下,所有卡片 依次向右排布,后面的卡片覆盖前面的卡片;在卡片被激活后,会移动到最右侧最顶层显示,大致效果如下:


网络异常,图片无法展示
|


1. 所以,首先是正常情况下的分布样式


.animation-card.is-list {
  &:hover {
    & ~ .animation-card {
      transform: translateX(24px);
    }
  }
  &.is-active {
    z-index: var(--max-index) !important;
    transform: translateX(calc(var(--max-index) * 20px - var(--card-index) * 20px - 40px));
  }
}


在 hover 时,所有 后面的兄弟节点会向右移动 20px;激活后(is-active),被激活的卡片会通过 zIndex 置顶,并通过 transform 向右平移到 最右侧


2. 然后,是 js 中的 computedStyle 样式计算,按顺序每个卡片向右移动 16 px(后面可以根据需求修改):


computedStyle(index, length) {
    const clutter = this.clutter;
    const defaultStyles = {
        "--max-index": length + 1,
        "--bg-color": randomRgbColor(),
        "--card-index": index
    };
    if (!clutter) defaultStyles["left"] = `${16 * ++index}px`;
    return defaultStyles;
}


3. 最后,是列表横向排布时的动画帧。


@keyframes eject {
  50% {
    transform: translateX(calc(-100% - 20px)) rotate(-20deg);
  }
}
@keyframes reject {
  50% {
    transform: translateX(calc(100% + 20px)) rotate(10deg);
  }
}


这里区分了 非激活 -> 被激活 的 eject从被激活 -> 非激活状态的 reject 两个动画:


  • eject :动画中间状态是 整体 向左偏移 整个卡片宽度加上 20px 的距离,并向左稍微旋转 20deg


  • reject :动画中间状态是 整体 向右偏移 整个卡片宽度加上 20px 的距离,并向右稍微旋转 10deg


另外eject的动画时间比reject 多一倍,也是为了把注意力集中在被激活卡片。


本身我是希望最后才把 zIndex 设置成最大值的,但是因为用了CSS变量的关系,动画帧定义不知道咋写了。如果有大佬知道也请告诉我一下,非常感谢~~


扇形乱序分布


这也是我里边说“乱序”状态吧,因为最初的一版卡片分布是按下标依次一左一右排列的,所以顺序有一点问题;不过后面也加了一个完整扇形的效果。


基础样式与列表状态一样,有区别的只是 激活/非激活的样式计算和动画定义部分


这个效果大概像这样:


网络异常,图片无法展示
|


1. 首先,一样是两个状态的样式定义:


.animation-card.is-clutter {
  transform: translateX(0%) rotate(var(--rotate-deg));
  transform-origin: bottom center;
  &.is-active {
    animation: rotation ease-in-out 0.8s;
    transform: translateX(calc(120%)) rotate(0deg);
    z-index: var(--max-index) !important;
  }
}


因为是 类似扇形的效果,需要保证旋转轴的位置在底部,所以需要设置 transform-origin


  • 正常状态 下,卡片只是稍微旋转


  • 被激活状态 下,卡片会平移到最后侧并取消旋转角度,同时置顶


2. 然后,是样式计算部分


computedStyle(index, length) {
  const clutter = this.clutter;
  const defaultStyles = {
    "--max-index": length + 1,
    "--bg-color": randomRgbColor(),
    "--card-index": index
  };
  if (clutter) {
    let rotate = 0;
    if (index % 2 === 1) {
      rotate = length - index;
    } else {
      rotate = index - length;
    }
    defaultStyles["--rotate-deg"] = rotate + "deg";
  }
  return defaultStyles;
}


根据下标的奇偶性,分别向左、向右旋转特定角度。


3. 最后,就是动画定义


这里为了方便,只定义了 从 非激活 -> 激活状态 的动画,取消激活时则是直接通过 transition 定义的一个过渡效果。


@keyframes rotation {
  0% {
    transform: translateX(0%) rotate(var(--rotate-deg));
  }
  60% {
    transform: translateX(calc(130%)) rotate(2deg);
  }
  70% {
    transform: translateX(calc(110%)) rotate(-2deg);
  }
  80% {
    transform: translateX(calc(125%)) rotate(1deg);
  }
  90% {
    transform: translateX(calc(115%)) rotate(-1deg);
  }
  100% {
    transform: translateX(calc(120%)) rotate(0deg);
  }
}


这里动画 初期依旧保持正常状态,到 60% 时到达目标区域,后面的过程就是一个轻微晃动的效果,在100% 时停留在目标位置。


扇形正序


早上突然发现,正序其实修改不大,只需要调整 computedStyle 方法即可。


computedStyle(index, length) {
  const clutter = this.clutter;
  const defaultStyles = {
    "--max-index": length + 1,
    "--bg-color": randomRgbColor(),
    "--card-index": index
  };
  const tangle = 48;
  const unitArc = tangle / length;
  if (clutter) {
    let rotate = unitArc * index - 48 / 2;
    defaultStyles["--rotate-deg"] = rotate + "deg";
  return defaultStyles;
}


这里的 tangle 是两边的最大旋转角度,可以根据需要自定义。unitArc 则是单位旋转角度,然后 初始角度 则是在左边 二分之一 tangle 的位置。


此时,显示效果就是这样了:


网络异常,图片无法展示
|


最后


当然本文个人感觉干货不是很多,只是在平时突然想到的一点儿小点子,但是动画效果其实不是很完美。只希望能给大家带来一点小小的灵感吧~~~

目录
相关文章
|
1月前
|
前端开发 内存技术
CSS动画示例(上一篇是CSS过渡…)
CSS动画示例(上一篇是CSS过渡…)
22 1
|
1月前
CSS3自动旋转正方体3D特效
CSS3自动旋转正方体3D特效
25 3
CSS3自动旋转正方体3D特效
|
1月前
|
资源调度 前端开发 CDN
纯css动画库animate.css的用法
纯css动画库animate.css的用法
46 0
|
2月前
CSS3滑动轮播动画
CSS3滑动轮播动画
33 8
|
18天前
|
前端开发
css3动画
css3动画
20 0
|
1月前
|
前端开发
CSS3中的动画示例
CSS3中的动画示例
12 1
|
1月前
|
前端开发
纯css实现的3D立体鸡蛋动画视觉效果
纯css实现的3D立体鸡蛋动画视觉效果
29 6
纯css实现的3D立体鸡蛋动画视觉效果
|
1月前
|
JavaScript
JS+CSS3点击粒子烟花动画js特效
JS+CSS3点击粒子烟花动画js特效
15 0
JS+CSS3点击粒子烟花动画js特效
|
1月前
|
前端开发 UED
深入理解CSS过渡效果(Transition):提升网页动画体验
深入理解CSS过渡效果(Transition):提升网页动画体验
29 1