动画07 展开收起小动画

需求

实现下面这样的动画:

实现

总体上实现不复杂,要值得注意的是首先使用getBoundingClientRect获取元素的位置和尺寸属性,可以将它封装为一个工具函数:

export const getRect = ele => {
  const { width, height, top, left, bottom, right } = ele.getBoundingClientRect();
  return {
    width,
    height,
    top,
    left,
    bottom,
    right,
    middleLeft: left + width / 2,
    middleTop: top + height / 2,
  }
};

实现上面的动画效果,实际上由两组动画复合而成sacle + transform,这种动画一般在CSS中使用keyframes实现,但是由于两个元素之间的移动和缩放都是需要计算的,所以使用了Web Animiate API的Element.animate()方法,这个方法让我们能够在JavaScript中使用keyframes的威力。

兼容性:

Element.animate(keyframes, keyframeOptions)

其中,keyframes代表包含所需CSS关键帧的JavaScript表示的一个对象数组,每个对象都包含一个关键帧,它们一起构成了所需的动画:

var boxframes = [{
  transform: 'translateX(0)',
  background: 'red',
  borderRadius: 0
}, {
  transform: 'translateX(200px) scale(.5)',
  background: 'orange',
  borderRadius: 0,
  offset: 0.6 /* set explicit point (60%) when frame starts */
}, {
  transform: 'translateX(400px)',
  background: 'green',
  borderRadius: '50%'
}]

keyframeOptions代表包含动画的其他设置,如easing, duration, fill-mode等:

 
属性 等效 CSS 描述
id none 给这个动画的命名以便在后面的代码中引用。
delay animation-delay 动画开始之前的延迟(整数)毫秒。默认 0s.
direction animation-direction 定义动画是否应该正常播放,反之亦然,或两者之间是否交替播放。可能的值是:
  • normal: 动画正常播放。在每个动画周期之后,动画重置为开始状态并重新开始(默认)
  • reverse: 从结束状态开始反向播放动画。在每个动画周期之后,动画重置为结束状态并重新开始。
  • alternate: 动画在正常和反向之间交替。相反,动画从结束状态开始并向后播放。动画定时功能也相反。
  • alternate-reverse: 动画在反向和正常方向之间交替,从第一次迭代开始反向。
duration animation-delay 动画的持续时间(整数),以毫秒为单位,如1000.默认为0(无动画,跳转到最后一帧)。
easing animation-timing-function S设置用于动画 @keyframe 缓动功能。可用值 "ease", "ease-in", "ease-in-out","linear", "frames(integer)" 等. 默认 "linear".
endDelay n/a 动画结束后延迟的毫秒数。当基于另一个动画的结束时间对多个动画进行排序时,这非常有用。默认为0。D默认为0
fill animation-fill-mode 定义当动画不再播放时,动画应如何将样式应用于其目标。默认为 "none"。 可能的值是:
  • none: 不播放动画时,不应将任何样式应用于目标。Default value.
  • forwards: 当动画未播放时,目标元素将保留最后关键帧中定义的计算样式(即:当关键帧处于100%时)。
  • backwards: 当动画未播放时,目标元素将保留第一个关键帧中定义的计算样式(即:当关键帧处于0%时)。
  • both: 当动画未播放时,目标元素将保留在第一个和最后一个关键帧中定义的计算样式。
iterationStart n/a 设置动画应该开始的迭代中的点。值应该是一个正数,浮点数。在迭代次数为1的动画中, iterationStart 的值为0.5会在中途开始动画。 在2次迭代的动画中,iterationStart 值为1.5,通过第二次迭代等途径开始动画。 IDefaults to 0.0.
iterations

 

 

animation-iteration-count 设置停止前动画应该运行的次数。Infinity 意味着永远。 默认为1

实际上这个API的使用和很多属性都与在CSS中使用animatekeyframes声明动画都是很相似的,但是这个API让我们有能力根据需要操作结果,例如暂停,跳过前进或挂接到动画的事件处理程序。

var animation = element.animate(keyframes, options); 

它将返回一个新建的Animation对象实例,它有着能够控制动画的属性和方法,比如通过cancel方法取消动画,通过pauseplay方法来暂停和恢复动画的播放等等,更多的属性可以参考MDN的文档

总的来说,这个API虽然很强大,但是还存在一定的兼容性的问题,希望浏览器的支持早点跟上,我们就能够想使用jQuery的animate方法一样痛快的使用原生的animate方法

代码

我是在Vue中实现的,完整的代码在这里

<template>
  <div class="main">
    <div class="modal" ref="modal" @click="toggleModal"></div>
    <div class="button" ref="button" @click="toggleModal">{{text}}</div>
  </div>
</template>

<script>
import {getRect} from '@/utils/index.js'

export default {
  name: 'demo42',
  data() {
    return {
      isExpand: true,
      modalRectInit: {},
      buttonRectInit: {},
    }
  },

  computed: {
    text() {
      return this.isExpand ? '收起' : '展开'
    }
  },

  methods: {
    toggleModal() {
      this.isExpand = !this.isExpand;
      this.animate(this.isExpand);
    },

    animate(reverse) {
      const modal = this.$refs.modal,
        button = this.$refs.button;

      modal.style.transform = 'none';

      this.buttonRectInit = getRect(button);
      this.modalRectInit = getRect(modal);

      const scaleX = this.buttonRectInit.width / this.modalRectInit.width,
        scaleY = this.buttonRectInit.height / this.modalRectInit.height;

      let transformStart = [
        'translateX(-50%)',
        'translateY(-50%)',
        'scaleX(1)',
        'scaleY(1)',
      ].join(' ');


      let transformEnd = [
        `translateX(${(this.buttonRectInit.left - this.modalRectInit.left)}px)`,
        `translateY(${(this.buttonRectInit.top - this.modalRectInit.top)}px)`,
        `scaleX(${scaleX})`,
        `scaleY(${scaleY})`,
      ].join(' ');

      if (reverse) {
        [transformStart, transformEnd] = [transformEnd, transformStart]
      }


      const keyFrames = [
        {transform: transformStart},
        {transform: transformEnd},
      ];

      modal.animate(
        keyFrames,
        {
          duration: 400,
          easing: 'ease-in',
          iterations: 1,
        }
      );

      modal.style.transform = transformEnd;
    },
  },
}
</script>

<style scoped>
  .modal {
    width: 480px;
    height: 360px;
    line-height: 360px;
    background: darkgoldenrod;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    transform-origin: top left;
    cursor: pointer;
    font-size: 14px;
    z-index: 2;
  }
  .button {
    width: 80px;
    height: 30px;
    line-height: 30px;
    background: aqua;
    cursor: pointer;
    font-size: 14px;
  }
</style>

参考

发布了382 篇原创文章 · 获赞 91 · 访问量 33万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览