前端 Vue3 音视频播放技术实践分享

14,928 阅读6分钟

在Vue和TypeScript框架的项目中,使用HTML5的video标签或者流行的开源库如西瓜播放器等可以实现视频和音频的播放功能。西瓜播放器支持视频和音频播放,具有高度化的自定义UI和非常灵活的事件响应API。在Vue3中,可以将播放器封装成一个Vue组件,但需要注意的是,初始化和加载视频会占用网络资源,因此建议在点击视频页面时才初始化和播放视频,以避免所有视频同时请求加载,占用网络资源,导致显示卡顿和加载慢。

前言

在基本 Vue3.4、TypeScript 框架的项目中,现在产品需要开发视频、音频的播放功能,产品需求:

  • 鼠标移入视频区域时,播放视频
  • 鼠标移出视频区域时,暂定播放视频

视频播放器

最简单的方式,就是直接用 HTML5 video 标签,简单实现:

<!DOCTYPE html>
<html>
<head>
  <title>视频播放示例</title>
</head>
<body>
  <video id="video" controls>
    <source 
      src="https://www.apple.com/newsroom/videos/vision-pro-visionos/large_2x.mp4" 
      type="video/mp4">
    Your browser does not support HTML5 video.
  </video>

  <script>
    var video = document.getElementById('video');

    // 鼠标移动到视频时播放
    video.addEventListener('mouseover', function() {
      video.play();
    });

    // 鼠标移出时暂停
    video.addEventListener('mouseout', function() {
      video.pause();
    });
  </script>
</body>
</html>

最原始的方式,也是最简单的方式,但是过于简单,就需要自己迭代功能,比如加载失败重试、页面全屏显示,HUD交互等,所以,还是用流行的开源库才是最明智的选择,那么应该选择什么库呢?

10 款用于Web的流行HTML5视频播放器

经过多方测试,西瓜播放器 是最好的选择,原因:

  • 支持视频和音频播放
  • 支持高度化的自定义UI
  • 非常灵活和事件响应的 API
  • ...

西瓜播放器 快速上手

我们项目中使用 v3.0.13,在 package.json 中添加:

  "dependencies": {
        "xgplayer": "^3.0.13"
  }

这里重点说明一下,v3 版本!!! 如果你在网上搜索,会出现很多的教程,但是可能是 v2 的 API,而2个版本的 API 有非常大的差异,导致你配置的字段不一定生效!

安装:

$ npm install xgplayer

在页面提供占位 DOM:

<div id="mse"></div>

实例化

let player = new Player({
  id: 'mse',
  url: '//abc.com/**/*.mp4'
});

西瓜播放器 & Vue3

在 vue3 中,需要先引入库:

import Player from "xgplayer";
import "xgplayer/dist/index.min.css";

注意,一定要引入 import "xgplayer/dist/index.min.css",否则播放器 UI 显示会异常,因为找不到 css 样式,如果需要自定义 css,则引入自己定义的样式就可以。

按照我们的需求,我们把播放器,封装成一个 vue 组件:

videoPlayer.vue

<template>
  <div :width="props.width" :height="props.height" :id="id" @mouseover="play" @mouseout="pause">
    <img
      v-if="showImage"
      style="background-color: black; object-fit: cover"
      :width="props.width"
      :height="props.height"
      :src="props.poster"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import Player from "xgplayer";
import "xgplayer/dist/index.min.css";

const props = defineProps({
  id: {
    type: String,
    required: true
  },
  videoUrl: {
    type: String,
    default: () => ""
  },
  poster: {
    type: String,
    default: () => ""
  },
  playsinline: {
    type: Boolean,
    default: true
  },
  width: {
    type: String,
    default: "100%"
  },
  height: {
    type: String,
    default: "100%"
  }
});

const showImage = ref(true);
// 定义一个变量来存储 player 实例
let player: Player;

// 播放视频
const play = () => {
  if (player == null) {
    initPlayer();
    showImage.value = false;
  }
  player.play();
};

// 暂停视频
const pause = () => {
  player.pause();
};

// 初始化西瓜视频
const initPlayer = () => {
  player = new Player({
    lang: "zh",
    volume: 0.3,
    id: props.id,
    url: props.videoUrl,
    poster: props.poster,
    playsinline: props.playsinline,
    height: props.height,
    width: props.width,
    // closeVideoClick: true, //单击暂停/播放
    // closeVideoDblclick: true, //双击全屏
    // cssFullscreen: false, //显示样式全屏
    // download: true, //显示下载按钮
    // controls: false,
    // marginControls: true,
    controls: {
      mode: "bottom"
    },
    // icons: {
    //   startPlay: `<div></div>`,
    //   startPause: `<div></div>`
    // },
    ignores: ["start", "progresspreview"],
    commonStyle: {
      // 进度条底色
      progressColor: "",
      // 播放完成部分进度条底色
      playedColor: "#ff9700",
      // 缓存部分进度条底色
      cachedColor: "",
      // 进度条滑块样式
      sliderBtnStyle: {},
      // 音量颜色
      volumeColor: "#ff9700"
    },
    playbackRate: [0.25, 0.5, 1, 1.5, 2, 3],
    inactive: 1500, //播放器focus状态自动消失延迟时长,单位为ms
    leavePlayerTime: 1500, //鼠标移出播放器区域就隐藏时间
    autoplay: true,
    whitelist: [""]
  });
  player.play();
};
</script>

image1.png

这样实现,导致鼠标移动页面时,视频就会初始化和加载,导致全部视频都请求加载,从而相互占用网络资源,导致显示卡和慢!

播放器优化

最后产品还是妥协,点击视频页面时,才初始化和播放视频,修改后的代码:

videoPlayer.vue

<template>
  <div :width="props.width" :height="props.height" :id="id">
    <img
      v-if="showImage"
      style="background-color: black; object-fit: cover"
      :width="props.width"
      :height="props.height"
      :src="props.poster"
      @click="clickImage"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import Player from "xgplayer";
import "xgplayer/dist/index.min.css";

const props = defineProps({
  id: {
    type: String,
    required: true
  },
  videoUrl: {
    type: String,
    default: () => ""
  },
  poster: {
    type: String,
    default: () => ""
  },
  playsinline: {
    type: Boolean,
    default: true
  },
  width: {
    type: String,
    default: "100%"
  },
  height: {
    type: String,
    default: "100%"
  }
});

const showImage = ref(true);
// 定义一个变量来存储 player 实例
let player: Player;

const clickImage = () => {
  if (player == null) {
    initPlayer();
    showImage.value = false;
  }
};

// 初始化西瓜视频
const initPlayer = () => {
  player = new Player({
    lang: "zh",
    volume: 0.3,
    id: props.id,
    url: props.videoUrl,
    poster: props.poster,
    playsinline: props.playsinline,
    height: props.height,
    width: props.width,
    // closeVideoClick: true, //单击暂停/播放
    // closeVideoDblclick: true, //双击全屏
    // cssFullscreen: false, //显示样式全屏
    // download: true, //显示下载按钮
    // controls: false,
    // marginControls: true,
    controls: {
      mode: "bottom"
    },
    // icons: {
    //   startPlay: `<div></div>`,
    //   startPause: `<div></div>`
    // },
    // ignores: ["start", "progresspreview"],
    commonStyle: {
      // 进度条底色
      progressColor: "",
      // 播放完成部分进度条底色
      playedColor: "#ff9700",
      // 缓存部分进度条底色
      cachedColor: "",
      // 进度条滑块样式
      sliderBtnStyle: {},
      // 音量颜色
      volumeColor: "#ff9700"
    },
    playbackRate: [0.25, 0.5, 1, 1.5, 2, 3],
    // inactive: 1500, //播放器focus状态自动消失延迟时长,单位为ms
    // leavePlayerTime: 1500, //鼠标移出播放器区域就隐藏时间
    autoplay: true,
    whitelist: [""]
  });
  player.play();
};
</script>

播放器组件使用

import VideoPlayer from "@/components/xgPlayer/videoPlayer.vue";
    <VideoPlayer
      width="260px"
      height="140px"
      :video-url="data.url"
      :poster="data.cover"
      :id="'videoPlayer' + index + data.id"
    />    

image2.png

西瓜播放器有丰富的 API 和错误处理,播放器出现播放错误会抛出错误事件,也有默认的处理逻辑。详细的 API 属性 可以查看文档。

总结

在基于Vue3.4和TypeScript的项目中,我们可以通过HTML5的video标签实现视频和音频的播放功能,这种方法的实现过程相对简单直接,但是它的功能相对有限,例如,我们需要自己编写代码来实现加载失败重试、全屏显示等附加功能。因此,考虑到实际操作的方便性和效率,使用一些流行的开源库,如西瓜播放器,无疑是更明智的选择。西瓜播放器不仅支持视频和音频播放,还具有高度自定义的用户界面和灵活的应用程序接口,可以满足各种复杂的需求。在Vue3中,我们可以将这样的播放器封装成一个Vue组件,这样就可以根据特定的业务需求进行定制和扩展。最后,如果页面同时展示多个视频的列表,为了优化用户体验和保护网络资源,产品决定在用户点击视频页面时才初始化和播放视频,这样可以避免所有视频同时请求加载,占用大量的网络资源,从而导致页面显示卡顿和加载速度变慢。这种方法旨在提供更流畅、更优质的用户体验。

最后,有什么问题欢迎留言或交流~

参考引用