征文 | 手把手教你基于 Vue 搭建 HTML 5 视频播放器

前言

相较于枯燥的文字,视频能让我们更快、更有兴趣的了解新知识,新体验。作为一个为用户提供优良体验的 IT 人,如何在网站中渲染出视频应是每个 IT 人必备的技能。

本教程技术栈基于 vue2.x,使用 video.js 在教大家如何网站中实现视频播放器功能。

通过学习本教程,你会学会 video.js 基本使用,同时会学会如何修改 video.js 的默认样式来实现播放按钮自定义形状(圆形)、居中及播放时间的显示与否。最后一部分,本教程将讲述 video 的属性、事件及方法,同时还会带领你实现一个功能齐全的视频播放器及音量记忆功能。

下面可以先来看一下实现效果:

image

基础部分

配置 vue 环境

通过 npm 安装 Vue 脚手架 vue-cli


npm install -g @vue/cli

然后创建一个 Vue 项目 kalacloud-vue-video


vue create kalacloud-video

选择合适的选项后,安装完成,通过 cd 命令进入 kalacloud-vue-video 目录(此目录为我们的主开发目录),使用 npm run serve 命令,可以将项目运行起来。

image

在 vue 中使用 videojs

首先使用 npm 安装 video.js


npm i video.js

安装完毕后,在 main.js 中进行引入


import videojs from "video.js";

import "video.js/dist/video-js.css";

Vue.prototype.$video = videojs;

为了代码复用性,我们来创建一个 PlayerVideo 组件。在组件中我们分别定义 template 部分和 script 如下

最开始我们选用默认源链接: https://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8


<!-- controls:向用户显示播放按钮控件 -->

<template>

  <video

    ref="video"

    class="video-js vjs-default-skin"

    width="600"

    height="400"

    controls

  >

    <source src="https://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8" />

  </video>

</template>

export default {

  data() {

    return {

      player: null, // 用来存储当前 video

    };

  },

  mounted() { // 渲染视频

    this.player = this.$video(this.$refs.video);

  },

};

然后删除掉初始化 vue 项目默认的 App.vue 内代码,将 PlayerVideo 组件添加到 App 中,并调整播放器至中间。


<template>

  <div id="app">

    <div class="video-content">

      <player-video :src="src"></player-video>

    </div>

  </div>

</template>

<script>

  import PlayerVideo from "./components/PlayerVideo.vue";

  export default {

    components: {

      PlayerVideo,

      data() {

        return {

          src: "http://vjs.zencdn.net/v/oceans.mp4",

        };

      },

    },

  };

</script>

<style lang="scss">

  #app {

    font-family: Avenir, Helvetica, Arial, sans-serif;

    -webkit-font-smoothing: antialiased;

    -moz-osx-font-smoothing: grayscale;

    text-align: center;

    color: #2c3e50;

    .video-content {

      display: flex;

      justify-content: center;

      flex-direction: column;

      align-items: center;

    }

  }

</style>

kalacloud-vue-video 根目录使用 npm 运行下列命令:


npm run serve

在浏览器打开 http://localhost:8080/ 就可以成功渲染视频。

我们大致的来看一下目前视频播放器拥有的功能:

  • 播放与暂停功能(目前播放按钮位于左上角)

  • 可以调节音量

  • 支持全屏与小屏播放

同样我们也可以发现一些不符合日常习惯的地方:

  • 播放按钮通常位于中间

  • 播放按钮一般为圆形

  • 暂停时会显示播放按钮

下面我们就讲述一些 tips 优化一下播放器。

播放按钮垂直居中

将播放按钮垂直居中非常容易实现,video 官方提供了 vjs-big-play-centered。给 <video> 标签添加 vjs-big-play-centered 类名就可以实现播放按钮垂直居中。


<video class="video-js vjs-default-skin vjs-big-play-centered"></video>

image

修改播放按钮为圆形

修改播放按钮为圆形需要修改对应类名的 CSS 样式。我们在 PlayerVideo 组件的 style 中添加下列样式代码。

修改时属性必须设置为 !important ,否则不会生效。


.video-js .vjs-big-play-button {

  font-size: 2.5em !important;

  line-height: 2.3em !important;

  height: 2.5em !important;

  width: 2.5em !important;

  -webkit-border-radius: 2.5em !important;

  -moz-border-radius: 2.5em !important;

  border-radius: 2.5em !important;

  background-color: #73859f;

  background-color: rgba(115, 133, 159, 0.5) !important;

  border-width: 0.15em !important;

  margin-top: -1.25em !important;

  margin-left: -1.75em !important;

}

.vjs-big-play-button .vjs-icon-placeholder {

  font-size: 1.63em !important;

}

image

暂停时显示播放按钮

这个功能同样可以通过修改 CSS 实现。在 PlayerVideo 组件的 style 中添加下列样式代码。


.vjs-paused .vjs-big-play-button,

.vjs-paused.vjs-has-started .vjs-big-play-button {

  display: block !important;

}

image

设置显示当前播放时间

通过修改两个类的状态可以实现显示播放时间的功能,在 PlayerVideo 组件中设置下列样式代码:


.video-js .vjs-time-control {

  display: block !important;

}

.video-js .vjs-remaining-time {

  display: none !important;

}

image

进阶使用

基础部分我们使用 this.$video(this.$refs.video) 渲染出播放器,但 this.$video 本质上是 video.js 提供的 videojs 函数,videojs 函数共有三个参数,第一个参数是绑定播放器的元素,第二参数为 options 对象,提供播放器的配置项,第三个参数为播放器渲染后的回调函数。

我们给 PlayerVideo 组件的 data 添加 options 对象,并设置 controlsfalse,同时设定一个简单的回调函数。

controls 属性是用来控制播放器是否具有与用户交互的控件——播放按钮等。如果设置为 false ,播放器将不显示播放控件,那么视频只能通过 Player API 或者 autoplay 控制播放。


export default {

  data() {

    return {

      player: null,

      options: { controls: false },

    };

  },

  mounted() {

    this.player = this.$video(this.$refs.video, this.options, () => {

      alert("播放器渲染完成");

    });

  },

};

我们可以发现,播放器渲染完成后,浏览器发出了通知,并且播放器上没有控件出现。

更多的配置项链接: video-options

常用事件

video 提供了很多常用事件,我们可以通过监测事件来处理不同的逻辑。

例如监测 play/pause 事件,给用户发送提醒

修改 PlayerVideo 组件中的 mounted 方法:


mounted() {

  this.player = this.$video(this.$refs.video, this.options, function () {

    this.on("play", () => {

      alert("视频已经开始播放,祝你有好的体验");

    });

    this.on("pause", () => {

      alert("视频已经暂停");

    });

  });

}

image

音量记忆功能

为了让大家更能理解监听事件的用处,我们举一下实际的案例: 音量记忆功能。

为了更好的用户体验,用户每次调整音量后,我们应该帮其记住当前音量。当用户刷新页面或者重新进入页面后,无需再次调整音量。

这个功能其实不难实现:

  • 监听 volumechange 事件,当用户修改音量时,把此音量存储到 localStorage 中(如果音量功能会有多个组件使用,建议同时存放在 Vuex 中)

  • 当页面刷新或进入页面后,从 localStorage 中取出音量值,同步设置播放器音量。

我们修改一下 PlayerVideo 组件,使其可以接受属性 volume 音量值,同时添加事件 volumechangeplay 事件的监听。

volumechange 触发时,将当前音量值存储到 localStorage 中;当 play 事件触发时,更新音量值。


<template>

  <video

    ref="video"

    controls

    class="video-js vjs-default-skin vjs-big-play-centered"

    width="600"

    height="400"

  >

    <source src="https://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8" />

  </video>

</template>

<script>

  export default {

    // 接受App传值

    props: ["volume"],

    data() {

      return {

        player: null,

        // volumnVideo 记录音量

        volumeVideo: this.volume,

      };

    },

    mounted() {

      const _this = this;

      this.player = this.$video(this.$refs.video, this.options, function () {

        this.on("volumechange", () => {

          // 存储音量

          _this.volumeVideo = this.volume();

          window.localStorage.volume = this.volume();

        });

        this.on("play", () => {

          this.volume(this.volumeVideo);

        });

      });

    },

  };

</script>

同时在 App.vue 中使用全局钩子函数 beforeCreate 获取到 localStorage 中存取的 volume


beforeCreate() {

  this.volume = window.localStorage.volume;

},

通过上述步骤,就可以成功实现音量记忆功能。

image

简单视频播放器

下面我带大家实现一下播放器的各种控制方法: 开始、暂停、重新加载、快进、后退、增大音量、降低音量以及换台功能。

video.js 对于这些控制方法都对应提供了方法。我们只需对提供的方法略作封装,即可使用。

下面我们就利用 video.js 提供的方法实现一个简单的播放器功能。

我们分别修改 PlayerVideo 组件和 App 组件如下:

PlayerVideo.vue 代码如下:


<template>

  <video

    ref="video"

    controls

    class="video-js vjs-default-skin vjs-big-play-centered"

    width="600"

    height="400"

  >

    <source :src="src" />

  </video>

</template>

<script>

  export default {

    props: ["volume", "src"],

    data() {

      return {

        player: null,

        volumeVideo: this.volume,

      };

    },

    methods: {

      // 封装播放器方法

      play() {

        this.player.src({ src: this.src });

        this.player.load(this.src);

        this.player.play(this.volumeVideo);

      },

      stop() {

        this.player.pause();

      },

      reload() {

        this.stop();

        this.player.load({});

        this.play();

      },

      forward() {

        const currentTime = this.player.currentTime();

        this.player.currentTime(currentTime + 5);

      },

      back() {

        const currentTime = this.player.currentTime();

        this.player.currentTime(currentTime - 5);

      },

      volumeUp() {

        this.player.volume(this.volumeVideo + 0.1);

      },

      volumeDown() {

        this.player.volume(this.volumeVideo - 0.1);

      },

      toggleTv(obj) {

        this.player.src(obj.src);

        this.player.load(obj.load);

        this.player.play(this.volumeVideo);

      },

    },

    mounted() {

      const _this = this;

      this.player = this.$video(this.$refs.video, this.options, function () {

        this.on("volumechange", () => {

          // 存储音量

          _this.volumeVideo = this.volume();

          window.localStorage.volume = this.volume();

        });

        this.on("play", () => {

          this.volume(this.volumeVideo);

        });

      });

    },

  };

</script>

<style>

  .video-js .vjs-time-control {

    display: block !important;

  }

  .video-js .vjs-remaining-time {

    display: none !important;

  }

  .video-js .vjs-big-play-button {

    font-size: 2.5em !important;

    line-height: 2.3em !important;

    height: 2.5em !important;

    width: 2.5em !important;

    -webkit-border-radius: 2.5em !important;

    -moz-border-radius: 2.5em !important;

    border-radius: 2.5em !important;

    background-color: #73859f;

    background-color: rgba(115, 133, 159, 0.5) !important;

    border-width: 0.15em !important;

    margin-top: -1.25em !important;

    margin-left: -1.25em !important;

  }

  .vjs-big-play-button .vjs-icon-placeholder {

    font-size: 1.63em !important;

  }

  .vjs-paused .vjs-big-play-button,

  .vjs-paused.vjs-has-started .vjs-big-play-button {

    display: block !important;

  }

</style>

App.vue 代码如下:


<template>

  <div id="app">

    <div class="video-content">

      <h2>使用video.js 在网站中搭建视频</h2>

      <h3>卡拉云——低代码开发工具,1 秒搭建上传后台</h3>

      <player-video :volume="volume" ref="video" :src="src"></player-video>

    </div>

    <div class="button-group">

      <el-button class="primary" @click="playVideo">开始视频</el-button>

      <el-button class="primary" @click="stopVideo">暂停视频</el-button>

      <el-button class="primary" @click="reloadVideo">重新加载</el-button>

      <el-button class="primary" @click="forwardVideo">视频快进</el-button>

      <el-button class="primary" @click="backVideo">视频后退</el-button>

      <el-button class="primary" @click="volumeUpVideo">增大音量</el-button>

      <el-button class="primary" @click="volumeDownVideo">降低音量</el-button>

      <el-button class="primary" @click="toggleToFenghuangwang"

        >凤凰卫视</el-button

      >

      <el-button class="primary" @click="toggleToDefault">默认频道</el-button>

    </div>

  </div>

</template>

<script>

  import PlayerVideo from "./components/PlayerVideo.vue";

  export default {

    components: {

      PlayerVideo,

    },

    data() {

      return {

        volume: 0.5,

        src: "http://vjs.zencdn.net/v/oceans.mp4",

      };

    },

    computed: {

      video() {

        return this.$refs.video;

      },

    },

    methods: {

      // 父类组件调用子组件方法,触发播放器功能

      stopVideo() {

        this.video.stop();

      },

      playVideo() {

        this.video.play();

      },

      reloadVideo() {

        this.video.reload();

      },

      forwardVideo() {

        this.video.forward();

      },

      backVideo() {

        this.video.back();

      },

      fullScreenVideo() {

        this.video.fullScreen();

      },

      screenVideo() {

        this.video.exitScreen();

      },

      volumeUpVideo() {

        this.video.volumeUp();

      },

      volumeDownVideo() {

        this.video.volumeDown();

      },

      toggleToFenghuangwang() {

        this.video.toggleTv({

          src: {

            type: "application/x-mpegURL",

            src: "https://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8",

          },

          load: "https://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8",

        });

      },

      toggleToDefault() {

        this.video.toggleTv({

          src: {

            type: "video/mp4",

            src: "http://vjs.zencdn.net/v/oceans.mp4",

          },

          load: "http://vjs.zencdn.net/v/oceans.mp4",

        });

      },

    },

    beforeCreate() {

      this.volume = window.localStorage.volume;

    },

  };

</script>

<style lang="scss">

  #app {

    font-family: Avenir, Helvetica, Arial, sans-serif;

    -webkit-font-smoothing: antialiased;

    -moz-osx-font-smoothing: grayscale;

    text-align: center;

    color: #2c3e50;

    display: flex;

    .video-content {

      display: flex;

      justify-content: center;

      flex-direction: column;

      align-items: center;

      margin-right: 20px;

    }

    .button-group {

      margin-top: 20px;

      display: flex;

      flex: 0 0 100px;

      flex-direction: column;

      justify-content: space-between;

      // align-items: center;

    }

  }

</style>

image

推荐阅读
相关专栏
前端与跨平台
90 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。