双轨视频动画方案

693 阅读4分钟

前言

在一个阳光明媚的早晨,小明收到了一个做邀请函的需求,产品要求要有很酷炫的动画,而且还要支持交互。

小明去调用了各种乱七八糟的动画方案,找到了个pag的东西,但是由于小明所要动工的项目上一个taro去实现小程序和h5双端的工程,兼容性有问题,很难同时兼容两边,后面就用回视频了。

但是用视频做到交互触发时候无缝切换,发现也是一个很坑的事情。一切换由于手机性能和网络等原因,总会造成卡顿,所以小明想封装一个高级的video组件,当video的src切换的时候,先预加载新的video,加载完成再播放

什么样的组件是小明想要的呢

小明觉得,对于这个需求,好的组件应该是封装完,也是一个video,就是属性和调方法和video保持一致,让业务方使用起来没有什么压力,不用看一堆使用说明,直接当video去使用就好。

这样封装的话,其它地方已有的视频如果也需要使用的话,直接替换标签就好了,就不会引起额外的改动。

整体方案

核心思路

要实现动画的流畅切换,核心就是得把后面的视频准备好,当用户发生交互,触发交换时候,把后面的视频播放,之后监听后面视频播放了的事件,把后面的视频切换的前面

组件的职责

封装完的组件应该是绝大多数的属性和回调方法和普通的video组件保持一致,可能会多几个关于额外配置属性而已

doubleTrackVideo组件的属性

属性名称属性描述
src当前播放视频的url
preVideoSource下一个视频url,起预请求准备作用,可不传
其它video的属性,这里省略

doubleTrackVideo组件的主要职责

  • 对父组件传入的属性传递到各个video组件
    • 大部分属性都直接透传下去就好
    • src组件需要特殊处理,显示在前面的video传src,在背后的video传preVideoSource
  • 将video的事件回传给父组件
    • 判断来自前面的video的事件就往父组件emit,不是直接不操作
  • 控制两个video前后切换的逻辑

image.png

整体流程

具体实现

对父组件传入的属性传递到各个video组件

大部分属性直接透传下去就好,除了视频资源,需要整个状态处理一下,在前面的video就传人src的资源,在后面的video就传入preVideoSource的地址

备注: 这里src不能直接判断video在前面src就用父组件src,在后面就用preVideoSource,因为当父组件传入的src发生变化时候,要先切背后的video的src,让视频加载后触发播放条件,我们再把前后的视频调换,所以不是一个简单的判断,而需要额外的状态去储存表示video应该用哪个资源地址

<video {...props} src={videoSource1.value}>
    {slots.default?.()}
</video>
<video {...props} src={videoSource2.value}>
    {slots.default?.()}
</video>
将video的事件回传给父组件
    const createEventHandles = (targetVideoIdx: 1 | 2) => {
      // videoEmitList 包括['ended', 'error', 'play', 'timeupdate', 'pause', 'fullscreenchange']等事件
      const result = videoEmitList.reduce(
        (pv, cv) =>
          Object.assign(pv, {
            ['on' + upperCaseFirst(cv)]: (...params) => {
              // frontVideo是位于前面的video,如果不是处于前面,则直接返回
              if (frontVideo.value !== targetVideoIdx) return
              emit(cv, ...params)
            }
          }),
        {} as any
      )
      return result
    }

    const emitHandles1 = createEventHandles(1)
    const emitHandles2 = createEventHandles(2)
    // 后面将这两个事件集合传人video标签 
    // <video {...emitHandles1} /> 
    // <video {...emitHandles2} />
控制两个video前后切换的逻辑
样式控制视频的前后

视频的前面后面直接根据样式进行控制,active-video会把z-index设置高一点,让视频处于前方

<video class={{ 'active-video': frontVideo.value === 1, 'front-one': true }}>{slots.default?.()}</video>
<video class={{'active-video': frontVideo.value === 2, 'back-one': true }}>{slots.default?.()}</video>
控制video切换的逻辑

如果没有preVideoSource时候,将会是下面的流程

  • 挂载时
    • 将组件传进来的src赋值给前面的video组件
  • 更新时
    • 监听组件传进来的src
    • 将src赋值到背后的video的src,并调用其play方法
    • 监听到背后的video开始play的时候
    • 将前后的video调换位置,并让后面的video暂停播放

image.png

加上preVideoSource后,会稍微复杂一些,但也大差不差

总结

经过一番封装,小明得到一个和video组件属性基本一致的组件,之后支持视频src切换后资源加载完再切换视频,甚至可以传入preVideoSource预加载视频,让视频无缝切换,以实现假动画

不足

这里preVideoSource只有一个,就是要那种预测下一次操作只有一个分支情况,如果有多种情况,这里可以换成一个数组,以更好地准备资源,但是这个相当于多开几个video组件去准备视频,对网络还有对手机性能都是不小的考验