【没错!前端也能做动画】前端实现复杂动画方案

动画播放

开发 h5 时,可能会碰到很多复杂的动效(例如一些炫酷的开场动画),这些动效可以通过 webp、gif、apng 等来实现。

但是通过图片文件的格式播放出来的效果可能会出现模糊,达不到预期的效果。

提供以下几种方案

Vap

VAP 是企鹅电竞实现融合礼物特效的组件,将图片/文字与原始 mp4 视频融合在一起,支持透明度,项目详细介绍请参考 VAP

1. 安装

bash 复制代码
npm i video-animation-player

2. 创建实例

js 复制代码
import Vap from "video-animation-player";
// init
let vap = new Vap(options);

3. 实例方法

js 复制代码
// 实例方法
1、on(): 绑定h5 video事件或者自定义事件(frame: 接收当前帧和播放时间戳)  如on('playering', function() {// do some thing})
2、destroy():销毁实例,清除video、canvas等
3、pause():暂停播放
4、play():继续播放
5、setTime(s):设置播放时间点(单位秒)

4. 实例参数

参数名 含义 默认值
container dom 容器 null
src mp4 视频地址 ''
config 配置 json 对象(需要提供视频的同事提供该 json 文件) null
width 宽度 375
height 高度 375
fps 动画帧数(生成素材时在工具中填写的 fps 值) 20
mute 是否对视频静音 false
loop 是否循环播放 false
type 组件基于 type 字段做了实例化缓存,不同的 VAP 实例应该使用不同的 type 值(如 0、1、2 等) undefined
beginPoint 起始播放时间点(单位秒),在一些浏览器中可能无效 0
accurate 是否启用精准模式(使用 requestVideoFrameCallback 提升融合效果,浏览器不兼容时自动降级) false
precache 是否预加载视频资源(默认关闭,即边下边播) false
onDestory 组件销毁时回调 undefined
onLoadError 加载失败回调 undefined
ext(无固定名) 融合参数(和 json 配置文件中保持一致) ''

5. 实现原理

使用 webgl texture 获取 video 和图片/文字的纹理,并在 shader 中进行自定义融合

6. 例子

js 复制代码
import Vap from "video-animation-player";
import config from "./demo.json";

export default {
  name: "vap",
  data() {
    return {
      url: require("./demo.mp4"),
      vap: null,
    };
  },
  methods: {
    play(flag) {
      const that = this;
      this.vap = new Vap()
        .play(
          Object.assign(
            {},
            {
              container: this.$refs.anim,
              // 素材视频链接
              src: this.url,
              // 素材配置json对象
              config: config,
              width: 900,
              height: 400,
              // 同素材生成工具中配置的保持一致
              fps: 20,
              // 是否循环
              loop: false,
              // 起始播放时间点
              beginPoint: 0,
              // 精准模式
              accurate: true,
              // 播放起始时间点(秒)
            },
            {
              // 融合信息(图片/文字),同素材生成工具生成的配置文件中的srcTag所对应,比如[imgUser] => imgUser
              imgUser:
                "//shp.qlogo.cn/pghead/Q3auHgzwzM6TmnCKHzBcyxVPEJ5t4Ria7H18tYJyM40c/0",
              imgAnchor:
                "//shp.qlogo.cn/pghead/PiajxSqBRaEKRa1v87G8wh37GibiaosmfU334GBWgk7aC8/140",
              textUser: "user1",
              textAnchor: "user2",
              type: 2,
            },
            { type: 1 }
          )
        )
        .on("playing", () => {
          console.log("playing");
        })
        .on("ended", () => {
          this.vap = null;
          console.log("play ended");
        })
        .on("frame", (frame, timestamp) => {
          // frame: 当前帧(从0开始)  timestamp: (播放时间戳)
          if (frame === 50) {
            // do something
          }
          console.log(frame, "-------", timestamp);
        });
      window.vap = this.vap;
    },
    pause() {
      this.vap.pause();
    },
    playContinue() {
      this.vap.play();
    },
  },
};

HTML video

使用 HTML 原始的 video 标签,但是该标签直接使用在手机上面会出现自动全屏或者出现工具条白边等情况

所以我们需要处理 video 标签所带来的问题,让其在手机上面可以正常播放即可

1. video 添加属性

html 复制代码
<template>
  <video
    v-if="show"
    id="VideoDom"
    poster="../assets/001.png"
    preload="auto"
    webkit-playsinline="webkit-playsinline"
    playsinline="true"
    x5-playsinline="x5-playsinline"
    x5-video-player-type="h5"
    x5-video-player-fullscreen="x5-video-player-fullscreen"
    muted="true"
    src="../assets/entry.mp4"
  ></video>
</template>

<script setup>
const show = ref(false);
onMounted(() => {
  show.value = true;
  nextTick(() => {
    // 获取实例
    const VideoDom = document.getElementById("VideoDom");
    // 300ms播放
    setTimeout(() => VideoDom.play(), 300);
    // 播放结束事件
    VideoDom.onended = endVideo;
  });
});
const endVideo = () => {
  show.value = false;
};
</script>

<style>
#VideoDom {
  width: 100%;
  height: 100%;
  object-fit: cover; // 铺满
  background-color: #000;
}
</style>

2. 属性介绍

属性 含义
autoplay 如果出现该属性,则视频在就绪后马上播放。
controls 如果出现该属性,则向用户显示控件,比如播放按钮
loop 循环播放
poster="url" 视频封面图
preload="auto" 视频在页面加载时进行加载
muted="true" 静音播放属性
webkit-playsinline="true" 防止默认转到 HTML5 视频的全屏行为
playsinline="true" 防止微信浏览器默认转到 HTML5 视频的全屏行为
x-webkit-airplay="allow" 使此视频支持 ios 的 AirPlay 功能
x5-video-player-type="h5" 启用 H5 播放器,是 wechat 安卓版特性
x5-video-player-fullscreen="true" 全屏设置,设置为 true 是防止横屏
x5-video-orientation="portraint" 播放器支付的方向,landscape 横屏,portraint 竖屏,默认值为竖屏

canvas 绘制视频

移动端视频会自动全屏可以参考上面给 video 标签设置属性禁止视频全屏播放,再把视频隐藏即可

html 复制代码
<!DOCTYPE html>
<html>
  <body>
    <p>要使用的视频:</p>

    <video id="video1" controls width="270" autoplay>
      <source src="/example/html5/mov_bbb.mp4" type="video/mp4" />
      <source src="/example/html5/mov_bbb.ogg" type="video/ogg" />
      <source src="/example/html5/mov_bbb.webm" type="video/webm" />
    </video>

    <p>画布(每 20 毫秒,代码就会绘制视频的当前帧):</p>

    <canvas
      id="myCanvas"
      width="270"
      height="135"
      style="border:1px solid #d3d3d3;"
    >
      Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script>
      var v = document.getElementById("video1");
      var c = document.getElementById("myCanvas");
      ctx = c.getContext("2d");
      var rafId;
      function renderVideo() {
        ctx.drawImage(v, 0, 0, 270, 135);
        rafId = requestAnimationFrame(renderVideo);
      }
      v.addEventListener(
        "play",
        function () {
          renderVideo();
        },
        false
      );
      v.addEventListener(
        "pause",
        function () {
          cancelAnimationFrame(rafId);
        },
        false
      );
      v.addEventListener(
        "ended",
        function () {
          cancelAnimationFrame(rafId);
        },
        false
      );
    </script>
  </body>
</html>

css 逐帧动画

使用逐帧图片,做好使用雪碧图,只需要加载一次即可

配合css动画事件 @animationend和steps属性 使用

总结

  1. Vap 生成的视频在部分机型上面可能会有点卡顿问题(严重的情况可能视频无法播放),这或许和视频本身帧率有关联。
  2. 使用 video 标签,支持 video 标签的浏览器都可以播放,视频帧率也需要调试,不可太大,否则会出现视频卡顿或者无法播放等错误。
  3. canvas 绘制视频会比使用第三方库好一些,但是绘制视频可能会出现模糊,网上也有解决方案,比如通过 dpr 按照原始尺寸绘制,在进行缩放等。
  4. css 逐帧动画实现起来还是比较方便的,且不复杂,不过在部分机型(安卓)上面闪烁比较严重,即使做了图片预加载也无法缓解。
  5. h5 推荐还是使用 video 标签比较好,使用方法简单,而且视频播放比较流畅清晰
  • 使用video方案的一些注意事项
  1. 如果视频出现资源问题,那么可能是视频太大导致,可以使用一倍视频进行尝试
  2. 根据手机不同,视频可能会出现一点卡顿情况
  3. 苹果手机低版本机型可能会出现视频卡第一帧现象,需要缓冲等待一会才可以正常播放,不过不影响整体效果(暂无完美解决方案)
  4. 如视频出现卡顿,模糊或者部分机型无法正常播放,大概率是视频无法兼容问题,可以和建模(或 UI)共同配合调试,基本都可以无障碍播放
  5. 使用 video 标签时,视频无法填充满可以使用 object-fit: cover; 属性,记得设置poster封面图片,少用 v-ifv-show 防止元素替换闪烁问题,更换了 videosrc 可以使用 video.load() 方法重新加载一下
相关推荐
掘金一周3 分钟前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队21 分钟前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei1 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
猿榜1 小时前
js逆向-喜某拉雅Xm-Sign参数解密
javascript
转转技术团队1 小时前
代码变更暗藏危机?代码影响范围分析为你保驾护航
前端·javascript·node.js
Mintopia1 小时前
Node.js高级实战:自定义流与Pipeline的高效数据处理 ——从字母生成器到文件管道的深度解析
前端·javascript·node.js
Mintopia1 小时前
Three.js深度解析:InstancedBufferGeometry实现动态星空特效 ——高效渲染十万粒子的底层奥秘
前端·javascript·three.js
北凉温华1 小时前
强大的 Vue 标签输入组件:基于 Element Plus 的 ElTagInput 详解
前端
随笔记1 小时前
Flex布局下,label标签设置宽度依旧对不齐,完美解决(flex-shrink属性)
javascript·css·vue.js