Vue3官网-过渡&动画(十)gsap、列表过渡(\<transition-group>、tag、FLIP技术、v-move、动态过度、可复用过渡)、状态过渡(第三方库gsap、把过渡放在子组件)

Vue3官网-过渡&动画(十)gsap、列表过渡(<transition-group>、tag、FLIP技术、v-move、动态过度、可复用过渡)、状态过渡(第三方库gsap、把过渡放在子组件)

总结:

  • 补充

    • Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

    • truthy(真值):在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除 false0""nullundefinedNaN 以外皆为真值)。括号内都是假值falsy。

    • .prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

    • ==Event.preventDefault方法取消浏览器对当前事件的默认行为。==比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。

    • vue:用组件(app.component)构建一个模板(template),并反复使用模板

    • 父组件、子组件

      • const app = Vue.createApp({
          components: {
            'component-a': ComponentA,
            'component-b': ComponentB
          }
        })
        
      • 上面代码中app为父组件,ComponentA和ComponentB为子组件

    • Vue中美元$符号的意思

      • 除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。
      • vue中$refs的作用?
        • 当我们需要获取一个dom元素进行操作的时候 可以利用原生js的getElementByXxx 而在vue中可以设置refs来获取这个dom元素
    • 第三方网站

      • greensock API(GSAP):是一套用于所有主流浏览器中制作高性能html5动画的工具。他们有一个很棒的 ease visualizer,帮助你建立精心制作的画架。
        • GSAP的全名是==GreenSock Animation Platform,这个名字是有些怪异(官网还在各种安利你加入它的Club),但它的确是一个从flash时代一直发展到今天的专业动画库。==参照CSDN:https://blog.csdn.net/weixin_39939012/article/details/80833073
      • animate.css :集成第三方 CSS 动画库
      • CSS Triggers :来查看哪些属性会在动画时触发重绘。这个网站是用来告诉开发者不同内核浏览器对css属性修改的重绘/回流情况,开发者知道了这些细节可以提高页面性能。
    • JavaScript钩子

      • 添加 :css="false",也会让 Vue 会跳过 CSS 的检测,除了性能略高之外,这可以避免过渡过程中 CSS 规则的影响。
  • 列表过渡

    • 概述

      • 默认情况下,它不会渲染一个包裹元素,但是你可以通过 tag attribute 指定渲染一个元素。(例如<p>指段落,<ul>指网络链接)
      • 过渡模式(out-in,in-out)不可用,因为我们不再相互切换特有的元素。
      • 内部元素总是需要提供唯一的 key attribute 值。
      • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
    • 列表的进入/离开过渡

      • 补充

        • splice()方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。

          arr.splice(start, count, addElement1, addElement2, ...);
          

          splice的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。

        • 列表:使用v-for

        • 列表过渡:使用 <transition-group> 组件

    • 列表的移动过渡

      • FLIP技术

        • Vue 内部使用了一个叫 FLIP 的动画技术,它使用 transform 将元素从之前的位置平滑过渡新的位置。
        • 使用 FLIP 过渡的元素不能设置为 display: inline。作为替代方案,可以设置为 display: inline-block 或者将元素放置于 flex 布局中。
        • FLIP 动画不仅可以实现单维度的过渡,多维网格种的元素也同样可以过渡
      • v-move

        • 它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name attribute 来自定义前缀,也可以通过 move-class attribute 手动设置。

        • .flip-list-move {
            transition: transform 0.8s ease;
          }
          
    • 列表的交错过渡

      • 通过 data attribute 与 JavaScript 通信,就可以实现列表的交错过渡:
      • 数据属性指:el.dataset.index之类
    • 可复用的过渡

      • <transition> 或者 <transition-group> 作为根组件,然后将任何子组件放置在其中就可以了。
      • 使用 template
    • 动态过度

      • 首先,在 Vue 中即使是过渡也是数据驱动的!动态过渡最基础的例子是通过 name attribute 来绑定动态的值

        <transition :name="transitionName">
          <!-- ... -->
        </transition>
        
      • 其次,尽管所有过渡 attribute 都可以动态绑定,我们可用的不仅有 attribute。因为事件钩子是方法,它们可以访问任何上下文中的数据,这意味着根据组件的状态不同,你的 JavaScript 过渡可以有不同的表现。

      • 最后创建动态过渡的最终方案是组件通过接受 prop 来动态修改之前的过渡。

  • 状态过渡

    • 数据元素本身动效
      • 数据元素
        • 数字和运算
        • 颜色的显示
        • SVG 节点的位置
        • 元素的大小和其他的 property
      • 这些数据要么本身就以数值形式存储,要么可以转换为数值。有了这些数值后,我们就可以结合 Vue 的响应性和组件系统,使用第三方库(gsap)来实现切换元素的过渡状态。
  • 把过渡放在组件里

    • 管理太多的状态过渡会迅速增加组件实例的复杂度,幸好很多的动画可以提取到专用的子组件中。

    • 如下面所示,所有的数值过渡都自动存放在’animated-integer’组件中

      app.component('animated-integer', {
      

1. 列表过渡

目前为止,关于过渡我们已经讲到:

  • 单个节点
  • 多个节点,每次只渲染一个

那么怎么同时渲染整个列表,比如使用 v-for?在这种场景下,我们会使用 <transition-group> 组件。在我们深入例子之前,先了解关于这个组件的几个特点:

  • 默认情况下,它不会渲染一个包裹元素,但是你可以通过 tag attribute 指定渲染一个元素。
  • 过渡模式不可用,因为我们不再相互切换特有的元素。
  • 内部元素总是需要提供唯一的 key attribute 值。
  • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

列表的进入/离开过渡

现在让我们由一个简单的例子深入,进入和离开的过渡使用之前一样的 CSS 类名。

<div id="list-demo">
  <button @click="add">Add</button>
  <button @click="remove">Remove</button>
  <transition-group name="list" tag="p">
    <span v-for="item in items" :key="item" class="list-item">
      {{ item }}
    </span>
  </transition-group>
</div>
const Demo = {
  data() {
    return {
      items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      nextNum: 10
    }
  },
  methods: {
    randomIndex() {
      return Math.floor(Math.random() * this.items.length)
    },
    add() {
      this.items.splice(this.randomIndex(), 0, this.nextNum++)
    },
    remove() {
      this.items.splice(this.randomIndex(), 1)
    }
  }
}

Vue.createApp(Demo).mount('#list-demo')
.list-item {
  display: inline-block;
  margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
  transition: all 1s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateY(30px);
}

在这里插入图片描述

这个例子有个问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,我们下面会解决这个问题。

列表的移动过渡

<transition-group> 组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name attribute 来自定义前缀,也可以通过 move-class attribute 手动设置。

这个类主要用于指定过渡 timing 和 easing 曲线,如下所示:

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

<div id="flip-list-demo">
  <button @click="shuffle">Shuffle</button>
  <transition-group name="flip-list" tag="ul">
    <li v-for="item in items" :key="item">
      {{ item }}
    </li>
  </transition-group>
</div>
const Demo = {
  data() {
    return {
      items: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
  },
  methods: {
    shuffle() {
      this.items = _.shuffle(this.items)
    }
  }
}

Vue.createApp(Demo).mount('#flip-list-demo')
.flip-list-move {
  transition: transform 0.8s ease;
}

在这里插入图片描述

这个看起来很神奇,其实 Vue 内部使用了一个叫 FLIP 的动画技术,它使用 transform 将元素从之前的位置平滑过渡新的位置。

我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡。

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>

<div id="list-complete-demo" class="demo">
  <button @click="shuffle">Shuffle</button>
  <button @click="add">Add</button>
  <button @click="remove">Remove</button>
  <transition-group name="list-complete" tag="p">
    <span v-for="item in items" :key="item" class="list-complete-item">
      {{ item }}
    </span>
  </transition-group>
</div>
const Demo = {
  data() {
    return {
      items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      nextNum: 10
    }
  },
  methods: {
    randomIndex() {
      return Math.floor(Math.random() * this.items.length)
    },
    add() {
      this.items.splice(this.randomIndex(), 0, this.nextNum++)
    },
    remove() {
      this.items.splice(this.randomIndex(), 1)
    },
    shuffle() {
      this.items = _.shuffle(this.items)
    }
  }
}

Vue.createApp(Demo).mount('#list-complete-demo')
.list-complete-item {
  transition: all 0.8s ease;
  display: inline-block;
  margin-right: 10px;
}

.list-complete-enter-from,
.list-complete-leave-to {
  opacity: 0;
  transform: translateY(30px);
}

.list-complete-leave-active {
  position: absolute;
}

在这里插入图片描述

TIP

需要注意的是使用 FLIP 过渡的元素不能设置为 display: inline。作为替代方案,可以设置为 display: inline-block 或者将元素放置于 flex 布局中。

FLIP 动画不仅可以实现单维度的过渡,多维网格种的元素也同样可以过渡

TODO:示例

列表的交错过渡

通过 data attribute 与 JavaScript 通信,就可以实现列表的交错过渡:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/gsap.min.js"></script>

<div id="demo">
  <input v-model="query" />
  <transition-group
    name="staggered-fade"
    tag="ul"
    :css="false"
    @before-enter="beforeEnter"
    @enter="enter"
    @leave="leave"
  >
    <li
      v-for="(item, index) in computedList"
      :key="item.msg"
      :data-index="index"
    >
      {{ item.msg }}
    </li>
  </transition-group>
</div>
const Demo = {
  data() {
    return {
      query: '',
      list: [
        { msg: 'Bruce Lee' },
        { msg: 'Jackie Chan' },
        { msg: 'Chuck Norris' },
        { msg: 'Jet Li' },
        { msg: 'Kung Fury' }
      ]
    }
  },
  computed: {
    computedList() {
      var vm = this
      return this.list.filter(item => {
        return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
      })
    }
  },
  methods: {
    beforeEnter(el) {
      el.style.opacity = 0
      el.style.height = 0
    },
    enter(el, done) {
      gsap.to(el, {
        opacity: 1,
        height: '1.6em',
        delay: el.dataset.index * 0.15,
        onComplete: done
      })
    },
    leave(el, done) {
      gsap.to(el, {
        opacity: 0,
        height: 0,
        delay: el.dataset.index * 0.15,
        onComplete: done
      })
    }
  }
}

Vue.createApp(Demo).mount('#demo')

在这里插入图片描述
在这里插入图片描述

可复用的过渡

过渡可以通过 Vue 的组件系统实现复用。要创建一个可复用过渡组件,你需要做的就是将 <transition> 或者 <transition-group> 作为根组件,然后将任何子组件放置在其中就可以了。

TODO:使用 Vue3 重构

使用 template 的简单例子:

Vue.component('my-special-transition', {
  template: '\
    <transition\
      name="very-special-transition"\
      mode="out-in"\
      @before-enter="beforeEnter"\
      @after-enter="afterEnter"\
    >\
      <slot></slot>\
    </transition>\
  ',
  methods: {
    beforeEnter(el) {
      // ...
    },
    afterEnter(el) {
      // ...
    }
  }
})

函数式组件更适合完成这个任务:

Vue.component('my-special-transition', {
  functional: true,
  render: function(createElement, context) {
    var data = {
      props: {
        name: 'very-special-transition',
        mode: 'out-in'
      },
      on: {
        beforeEnter(el) {
          // ...
        },
        afterEnter(el) {
          // ...
        }
      }
    }
    return createElement('transition', data, context.children)
  }
})

动态过渡

在 Vue 中即使是过渡也是数据驱动的!动态过渡最基础的例子是通过 name attribute 来绑定动态的值

<transition :name="transitionName">
  <!-- ... -->
</transition>

当你已经定义了 Vue 的过渡类约定,并希望可以快速切换它们的时候,这会非常有用。

尽管所有过渡 attribute 都可以动态绑定,我们可用的不仅有 attribute。因为事件钩子是方法,它们可以访问任何上下文中的数据,这意味着根据组件的状态不同,你的 JavaScript 过渡可以有不同的表现。

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

<div id="dynamic-fade-demo" class="demo">
  Fade In:
  <input type="range" v-model="fadeInDuration" min="0" :max="maxFadeDuration" />
  Fade Out:
  <input
    type="range"
    v-model="fadeOutDuration"
    min="0"
    :max="maxFadeDuration"
  />
  <transition
    :css="false"
    @before-enter="beforeEnter"
    @enter="enter"
    @leave="leave"
  >
    <p v-if="show">hello</p>
  </transition>
  <button v-if="stop" @click="stop = false; show = false">
    Start animating
  </button>
  <button v-else @click="stop = true">Stop it!</button>
</div>
const app = Vue.createApp({
  data() {
    return {
      show: true,
      fadeInDuration: 1000,
      fadeOutDuration: 1000,
      maxFadeDuration: 1500,
      stop: true
    }
  },
  mounted() {
    this.show = false
  },
  methods: {
    beforeEnter(el) {
      el.style.opacity = 0
    },
    enter(el, done) {
      var vm = this
      Velocity(
        el,
        { opacity: 1 },
        {
          duration: this.fadeInDuration,
          complete: function() {
            done()
            if (!vm.stop) vm.show = false
          }
        }
      )
    },
    leave(el, done) {
      var vm = this
      Velocity(
        el,
        { opacity: 0 },
        {
          duration: this.fadeOutDuration,
          complete: function() {
            done()
            vm.show = true
          }
        }
      )
    }
  }
})

app.mount('#dynamic-fade-demo')

TODO:示例

最后,创建动态过渡的最终方案是组件通过接受 prop 来动态修改之前的过渡。一句老话,唯一的限制是你的想象力。

2. 状态过渡

Vue 的过渡系统提供了非常多简单的方法设置进入、离开和列表的动效。那么对于数据元素本身的动效呢,比如:

  • 数字和运算
  • 颜色的显示
  • SVG 节点的位置
  • 元素的大小和其他的 property

这些数据要么本身就以数值形式存储,要么可以转换为数值。有了这些数值后,我们就可以结合 Vue 的响应性和组件系统,使用第三方库(gsap)来实现切换元素的过渡状态

状态动画与侦听器

通过侦听器我们能监听到任何数值 property 的数值更新。可能听起来很抽象,所以让我们先来看看使用 GreenSock 一个例子:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>

<div id="animated-number-demo">
  <input v-model.number="number" type="number" step="20" />
  <p>{{ animatedNumber }}</p>
</div>
const Demo = {
  data() {
    return {
      number: 0,
      tweenedNumber: 0
    }
  },
  computed: {
    animatedNumber() {
      return this.tweenedNumber.toFixed(0)
    }
  },
  watch: {
    number(newValue) {
      gsap.to(this.$data, { duration: 0.5, tweenedNumber: newValue })
    }
  }
}

Vue.createApp(Demo).mount('#animated-number-demo')

在这里插入图片描述

更新数字时,输入框下方会对更改设置动画效果。

动态状态过渡

就像 Vue 的过渡组件一样,数据背后状态过渡会实时更新,这对于原型设计十分有用。当你修改一些变量,即使是一个简单的 SVG 多边形也可实现很多难以想象的效果。

在这里插入图片描述

把过渡放到组件里

管理太多的状态过渡会迅速增加组件实例的复杂度,幸好很多的动画可以提取到专用的子组件中。我们把之前的示例改写一下:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>

<div id="app">
  <input v-model.number="firstNumber" type="number" step="20" /> +
  <input v-model.number="secondNumber" type="number" step="20" /> = {{ result }}
  <p>
    <animated-integer :value="firstNumber"></animated-integer> +
    <animated-integer :value="secondNumber"></animated-integer> =
    <animated-integer :value="result"></animated-integer>
  </p>
</div>
const app = Vue.createApp({
  data() {
    return {
      firstNumber: 20,
      secondNumber: 40
    }
  },
  computed: {
    result() {
      return this.firstNumber + this.secondNumber
    }
  }
})

app.component('animated-integer', {
  template: '<span>{{ fullValue }}</span>',
  props: {
    value: {
      type: Number,
      required: true
    }
  },
  data() {
    return {
      tweeningValue: 0
    }
  },
  computed: {
    fullValue() {
      return Math.floor(this.tweeningValue)
    }
  },
  methods: {
    tween(newValue, oldValue) {
      gsap.to(this.$data, {
        duration: 0.5,
        tweeningValue: newValue,
        ease: 'sine'
      })
    }
  },
  watch: {
    value(newValue, oldValue) {
      this.tween(newValue, oldValue)
    }
  },
  mounted() {
    this.tween(this.value, 0)
  }
})

app.mount('#app')

在这里插入图片描述

我们能在组件中结合使用这一节讲到各种过渡策略和 Vue 内建的过渡系统。总之,对于完成各种过渡动效几乎没有阻碍。

你可以看到我们如何使用它进行数据可视化、物理效果、角色动画和交互,一切都没有限制。

赋予设计以生命

动画效果可以栩栩如生。不幸的是,设计师创建图标、logo 和吉祥物的时候,他们交付的通常都是图片或静态的 SVG。所以,虽然 GitHub 的章鱼猫、Twitter 的小鸟以及其它许多 logo 类似于生灵,它们看上去实际上并不是活着的。

Vue 可以帮到你。因为 SVG 的本质是数据,我们只需要准备好这些生物兴奋、思考或警戒的样例。然后 Vue 就可以辅助完成这几种状态之间的过渡动画,来制作你的欢迎页面、加载指示和更加带有情感的通知提示。

Sarah Drasner 展示了下面这个根据时间和交互变化发生状态改变的 demo:

img

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChrisP3616

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值