你是否有一个梦想?用JavaScript[vue.js、react.js......]开发一款自定义配置视频播放器

本文涉及的产品
视频点播 VOD,流量+存储+转码
简介: 沉寂了一周了,打算把这几天的结果呈现给大家。这几天抽空就一直在搞一个自定义视频播放器,为什么会有如此想法?是因为之前看一些学习视频网站时,看到它们做的视频播放器非常Nice!于是,就打算抽空开发一款属于自己的视频播放器。话不多说,一起来实战吧!

前言


沉寂了一周了,打算把这几天的结果呈现给大家。这几天抽空就一直在搞一个自定义视频播放器,为什么会有如此想法?是因为之前看一些学习视频网站时,看到它们做的视频播放器非常Nice!于是,就打算抽空开发一款属于自己的视频播放器。话不多说,一起来实战吧!


项目展示


微信截图_20220506131045.png


(这只是一张图片哦~)


这张图就是我们的成品,还等什么?赶紧来实战吧!


实战


我会把完整源码放在github上,欢迎来star,地址在文末。


首先,我们会使用最原生的JavaScript来实现,老大哥肯定要打头阵啊!


一、JavaScript


  1. iconfont.css:阿里字体图标文件,你可以在上面找到很多漂亮的图标。
  2. index.css:项目样式文件。
  3. index.js:项目逻辑文件。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>VamVideo(原生js版)</title>
    <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
    <link rel="stylesheet" href="./css/index.css" />
  </head>
  <body>
    <div class="video-box">
      <video class="video-player" preload="auto" poster="./img/bg.png">
        <source
          src="https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4"
        />
        <source />
      </video>
      <div class="bottom-tool">
        <div class="pv-bar">
          <div class="pv-played"></div>
          <div class="pv-dot"></div>
        </div>
        <div class="pv-controls">
          <div class="pc-con-l">
            <div class="play-btn">
              <i class="iconfont icon-bofang"></i>
              <i class="iconfont icon-zanting hide"></i>
            </div>
            <div class="pv-time">
              <span class="pv-currentTime">00:00:00</span>
              <span>/</span>
              <span class="pv-duration">00:00:00</span>
            </div>
          </div>
          <div class="pc-con-r">
            <div class="pv-listen ml">
              <div class="pv-yl">
                <p class="pv-ol"></p>
                <p class="pv-bg"></p>
              </div>
              <div class="pv-iconyl">
                <i class="iconfont icon-yinliang"></i>
                <i class="iconfont icon-jingyin hide"></i>
              </div>
            </div>
            <div class="pv-speed ml">
              <p class="pv-spnum">1x</p>
              <ul class="selectList">
                <li>0.5x</li>
                <li>1x</li>
                <li>1.25x</li>
                <li>1.5x</li>
                <li>2x</li>
              </ul>
            </div>
            <div class="pv-screen ml">
              <i class="iconfont icon-quanping"></i>
              <i class="iconfont icon-huanyuan hide"></i>
            </div>
          </div>
        </div>
      </div>
    </div>
    <script src="./js/index.js"></script>
  </body>
</html>


我们主要看下逻辑文件index.js


let timer = null;
let disX = 0;
let disL = 0;
function $(el) {
  return document.querySelector(el);
}
function showEl(el) {
  $(el).style.display = "block";
}
function hideEl(el) {
  $(el).style.display = "none";
}
function setVp(w, h) {
  $(".video-player").style.width = w + "px";
  $(".video-player").style.height = h + "px";
  $(".video-box").style.width = w + "px";
  $(".video-box").style.height = h + "px";
  $(".pv-bar").style.width = w + "px";
}
// 时间格式化
function changeTime(iNum) {
  let iN = parseInt(iNum);
  const iH = toZero(Math.floor(iN / 3600));
  const iM = toZero(Math.floor((iN % 3600) / 60));
  const iS = toZero(Math.floor(iN % 60));
  return iH + ":" + iM + ":" + iS;
}
// 整0处理
function toZero(num) {
  if (num <= 9) {
    return "0" + num;
  } else {
    return "" + num;
  }
}
// 底部控制栏
$(".video-box").onmouseenter = function () {
  $(".bottom-tool").style.bottom = "0px";
};
$(".video-box").onmouseleave = function () {
  $(".bottom-tool").style.bottom = "-45px";
};
// 倍速播放栏(显示/隐藏)
$(".pv-spnum").onmouseover = function () {
  showEl(".selectList");
};
$(".pv-controls").onmouseleave = function () {
  hideEl(".selectList");
};
// 播放/暂停
$(".play-btn").onclick = function () {
  if ($(".video-player").paused) {
    $(".video-player").play();
    hideEl(".icon-bofang");
    showEl(".icon-zanting");
    nowTime();
    timer = setInterval(nowTime, 1000);
  } else {
    $(".video-player").pause();
    showEl(".icon-bofang");
    hideEl(".icon-zanting");
    clearInterval(timer);
  }
};
// 总时长
$(".video-player").oncanplay = function () {
  $(".pv-duration").innerHTML = changeTime($(".video-player").duration);
};
// 播放结束
$(".video-player").onended = function (params) {
  showEl(".icon-bofang");
  hideEl(".icon-zanting");
};
// 播放时长
function nowTime() {
  $(".pv-currentTime").innerHTML = changeTime($(".video-player").currentTime);
  let scale = $(".video-player").currentTime / $(".video-player").duration;
  let w = $(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth;
  $(".pv-dot").style.left = scale * w + "px";
  $(".pv-played").style.width = scale * w + "px";
}
// 静音/取消静音
$(".pv-iconyl").onclick = function () {
  if ($(".video-player").muted) {
    $(".video-player").volume = 1;
    hideEl(".icon-jingyin");
    showEl(".icon-yinliang");
    $(".video-player").muted = false;
  } else {
    $(".video-player").volume = 0;
    showEl(".icon-jingyin");
    hideEl(".icon-yinliang");
    $(".video-player").muted = true;
  }
};
let isfullScreen = false;
// 全屏
$(".pv-screen").onclick = function () {
  const w = document.documentElement.clientWidth || document.body.clientWidth;
  const h = document.documentElement.clientHeight || document.body.clientHeight;
  isfullScreen = !isfullScreen;
  if (isfullScreen) {
    setVp(w, h);
    hideEl(".icon-quanping");
    showEl(".icon-huanyuan");
  } else {
    setVp(900, 480);
    showEl(".icon-quanping");
    hideEl(".icon-huanyuan");
  }
};
// 播放进度条
$(".pv-dot").onmousedown = function (ev) {
  let ev1 = ev || window.event;
  disX = ev1.clientX - $(".pv-dot").offsetLeft;
  document.onmousemove = function (ev) {
    let ev2 = ev || window.event;
    let L = ev2.clientX - disX;
    if (L < 0) {
      L = 0;
    } else if (L > $(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth) {
      L = $(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth;
    }
    $(".pv-dot").style.left = L + "px";
    let scale = L / ($(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth);
    $(".video-player").currentTime = scale * $(".video-player").duration;
    nowTime();
  };
  document.onmouseup = function () {
    document.onmousemove = null;
  };
  return false;
};
// 音量控制
$(".pv-ol").onmousedown = function (ev) {
  let ev1 = ev || window.event;
  disL = ev1.clientX - $(".pv-ol").offsetLeft;
  document.onmousemove = function (ev) {
    let ev2 = ev || window.event;
    let L = ev2.clientX - disL;
    if (L < 0) {
      L = 0;
    } else if (L > $(".pv-yl").offsetWidth - $(".pv-ol").offsetWidth) {
      L = $(".pv-yl").offsetWidth - $(".pv-ol").offsetWidth;
    }
    $(".pv-ol").style.left = L + "px";
    let scale = L / ($(".pv-yl").offsetWidth - $(".pv-ol").offsetWidth);
    $(".pv-bg").style.width = $(".pv-ol").offsetLeft + "px";
    if ($(".pv-ol").offsetLeft !== 0) {
      showEl(".icon-yinliang");
      hideEl(".icon-jingyin");
    } else {
      showEl(".icon-jingyin");
      hideEl(".icon-yinliang");
    }
    $(".video-player").volume = scale;
  };
  document.onmouseup = function () {
    document.onmousemove = null;
  };
  return false;
};
// 播放速度
$(".selectList").onclick = function (e) {
  let ev = e || window.event;
  hideEl(".selectList");
  $(".pv-spnum").innerText = ev.target.innerText;
  const value = ev.target.innerText.replace("x", "");
  $(".video-player").playbackRate = value;
};


这样写是可以实现一个视频播放器,你可以通过改样式文件还有部分逻辑文件来实现一个自定义配置视频播放器,但是这种效果不太好,所以我们将通过使用Es6中的Class类来重写这个自定义配置视频播放器。


二、Class类


  1. vp.js:class类逻辑文件


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>VamVideo(Class类版)</title>
    <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
    <link rel="stylesheet" href="./css/index.css" />
  </head>
  <body>
    <div class="video-box" onmouseenter="vp.bottomTup()" onmouseleave="vp.bottomTdow()">
      <video class="video-player" oncanplay="vp.useOnplay()" onended="vp.useEnd()"></video>
      <div class="bottom-tool">
        <div class="pv-bar">
          <div class="pv-played"></div>
          <div class="pv-dot" onmousedown="vp.useTime()"></div>
        </div>
        <div class="pv-controls" onmouseleave="vp.selectListHide()">
          <div class="pc-con-l">
            <div class="play-btn" onclick="vp.usePlay()">
              <i class="iconfont icon-bofang"></i>
              <i class="iconfont icon-zanting hide"></i>
            </div>
            <div class="pv-time">
              <span class="pv-currentTime">00:00:00</span>
              <span>/</span>
              <span class="pv-duration">00:00:00</span>
            </div>
          </div>
          <div class="pc-con-r">
            <div class="pv-listen ml">
              <div class="pv-yl">
                <p class="pv-ol" onmousedown="vp.useListen()"></p>
                <p class="pv-bg"></p>
              </div>
              <div class="pv-iconyl" onclick="vp.useVolume()">
                <i class="iconfont icon-yinliang"></i>
                <i class="iconfont icon-jingyin hide"></i>
              </div>
            </div>
            <div class="pv-speed ml">
              <p class="pv-spnum" onmouseover="vp.selectListShow()">1x</p>
              <ul class="selectList" onclick="vp.useSpnum()">
                <li>0.5x</li>
                <li>1x</li>
                <li>1.25x</li>
                <li>1.5x</li>
                <li>2x</li>
              </ul>
            </div>
            <div class="pv-screen ml" onclick="vp.fullScreen()">
              <i class="iconfont icon-quanping"></i>
              <i class="iconfont icon-huanyuan hide"></i>
            </div>
          </div>
        </div>
      </div>
    </div>
    <script src="./js/vp.js"></script>
    <script>
      const vp = new VamVideo(
        document.querySelector(".video-box"), // 挂载父节点
        { // 视频属性
          poster:"./img/bg.png",
          src:"https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4",
          preload:"auto",
          // loop:"loop",
          // autoplay:"autoplay"
        },
        { // 视频样式
          width:"1200px",
          height:"600px"
        }
      );
    </script>
  </body>
</html>


看到上面的代码,已经发现我们可以配置视频播放器了,那么这个vp.js到底是何方神圣呢?我们来看下。


class VamVideo {
  constructor(vp, attrObj, styleObj) {
    this.timer = null;
    this.disX = 0;
    this.disL = 0;
    this.isfullScreen = false;
    for (const key in attrObj) {
      if (Object.hasOwnProperty.call(attrObj, key)) {
        this.$(".video-player").setAttribute(key, attrObj[key]);
      }
    }
    for (const key in styleObj) {
      if (Object.hasOwnProperty.call(styleObj, key)) {
        this.$(".video-box").style[`${key}`] = styleObj[key];
        key === "width"
          ? (this.vbw = styleObj.width)
          : (this.vbw = vp.offsetWidth);
        key === "height"
          ? (this.vbh = styleObj.height)
          : (this.vbh = vp.offsetHeight);
      }
    }
  }
  $ = (el) => document.querySelector(el);
  showEl = (el) => {
    this.$(el).style.display = "block";
  };
  hideEl = (el) => {
    this.$(el).style.display = "none";
  };
  setVp = (w, h) => {
    const _w = String(w).indexOf("px") != -1 ? w : w + "px";
    const _h = String(h).indexOf("px") != -1 ? h : h + "px";
    this.$(".video-player").style.width = _w;
    this.$(".video-player").style.height = _h;
    this.$(".video-box").style.width = _w;
    this.$(".video-box").style.height = _h;
    this.$(".pv-bar").style.width = _w;
  };
  nowTime = () => {
    this.$(".pv-currentTime").innerHTML = this.changeTime(
      this.$(".video-player").currentTime
    );
    let scale =
      this.$(".video-player").currentTime / this.$(".video-player").duration;
    let w = this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth;
    this.$(".pv-dot").style.left = scale * w + "px";
    this.$(".pv-played").style.width = scale * w + "px";
  };
  changeTime = (iNum) => {
    let iN = parseInt(iNum);
    const iH = this.toZero(Math.floor(iN / 3600));
    const iM = this.toZero(Math.floor((iN % 3600) / 60));
    const iS = this.toZero(Math.floor(iN % 60));
    return iH + ":" + iM + ":" + iS;
  };
  toZero = (num) => {
    if (num <= 9) {
      return "0" + num;
    } else {
      return "" + num;
    }
  };
  // 底部控制栏(显示/隐藏)
  bottomTup = () => {
    this.$(".bottom-tool").style.bottom = "0px";
  };
  bottomTdow = () => {
    this.$(".bottom-tool").style.bottom = "-45px";
  };
  // 倍速播放栏(显示/隐藏)
  selectListShow = () => {
    this.showEl(".selectList");
  };
  selectListHide = () => {
    this.hideEl(".selectList");
  };
  // 播放/暂停
  usePlay = () => {
    if (this.$(".video-player").paused) {
      this.$(".video-player").play();
      this.hideEl(".icon-bofang");
      this.showEl(".icon-zanting");
      this.nowTime();
      this.timer = setInterval(this.nowTime, 1000);
    } else {
      this.$(".video-player").pause();
      this.showEl(".icon-bofang");
      this.hideEl(".icon-zanting");
      clearInterval(this.timer);
    }
  };
  // 总时长
  useOnplay = () => {
    this.$(".pv-duration").innerHTML = this.changeTime(
      this.$(".video-player").duration
    );
  };
  // 播放结束
  useEnd = () => {
    this.showEl(".icon-bofang");
    this.hideEl(".icon-zanting");
  };
  // 静音
  useVolume = () => {
    if (this.$(".video-player").muted) {
      this.$(".video-player").volume = 1;
      this.hideEl(".icon-jingyin");
      this.showEl(".icon-yinliang");
      this.$(".video-player").muted = false;
    } else {
      this.$(".video-player").volume = 0;
      this.showEl(".icon-jingyin");
      this.hideEl(".icon-yinliang");
      this.$(".video-player").muted = true;
    }
  };
  // 全屏
  fullScreen = () => {
    const w = document.documentElement.clientWidth || document.body.clientWidth;
    const h =
      document.documentElement.clientHeight || document.body.clientHeight;
    this.isfullScreen = !this.isfullScreen;
    if (this.isfullScreen) {
      this.setVp(w, h);
      this.hideEl(".icon-quanping");
      this.showEl(".icon-huanyuan");
    } else {
      console.log("w" + this.vbw, "h" + this.vbh);
      this.setVp(this.vbw, this.vbh);
      this.showEl(".icon-quanping");
      this.hideEl(".icon-huanyuan");
    }
  };
  // 播放进度条
  useTime = (ev) => {
    let ev1 = ev || window.event;
    this.disX = ev1.clientX - this.$(".pv-dot").offsetLeft;
    document.onmousemove = (ev) => {
      let ev2 = ev || window.event;
      let L = ev2.clientX - this.disX;
      if (L < 0) {
        L = 0;
      } else if (
        L >
        this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth
      ) {
        L = this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth;
      }
      this.$(".pv-dot").style.left = L + "px";
      let scale =
        L / (this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth);
      this.$(".video-player").currentTime =
        scale * this.$(".video-player").duration;
      this.nowTime();
    };
    document.onmouseup = function () {
      document.onmousemove = null;
    };
    return false;
  };
  // 音量控制
  useListen = (ev) => {
    let ev1 = ev || window.event;
    this.disL = ev1.clientX - this.$(".pv-ol").offsetLeft;
    document.onmousemove = (ev) => {
      let ev2 = ev || window.event;
      let L = ev2.clientX - this.disL;
      if (L < 0) {
        L = 0;
      } else if (
        L >
        this.$(".pv-yl").offsetWidth - this.$(".pv-ol").offsetWidth
      ) {
        L = this.$(".pv-yl").offsetWidth - this.$(".pv-ol").offsetWidth;
      }
      this.$(".pv-ol").style.left = L + "px";
      let scale =
        L / (this.$(".pv-yl").offsetWidth - this.$(".pv-ol").offsetWidth);
      this.$(".pv-bg").style.width = this.$(".pv-ol").offsetLeft + "px";
      if (this.$(".pv-ol").offsetLeft !== 0) {
        this.showEl(".icon-yinliang");
        this.hideEl(".icon-jingyin");
      } else {
        this.showEl(".icon-jingyin");
        this.hideEl(".icon-yinliang");
      }
      this.$(".video-player").volume = scale;
    };
    document.onmouseup = function () {
      document.onmousemove = null;
    };
    return false;
  };
  // 播放速度
  useSpnum = (e) => {
    let ev = e || window.event;
    this.hideEl(".selectList");
    this.$(".pv-spnum").innerText = ev.target.innerText;
    const value = ev.target.innerText.replace("x", "");
    this.$(".video-player").playbackRate = value;
  };
}


这样不仅可以自定义配置一个视频播放器,逻辑文件中的每一个方法函数还非常的简单明了,可以说是达到我们要求的目的了。但是我们可以更简洁。


三、模板字符串


  1. strvp.js:把标签语句放在了模板字符串中。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>VamVideo(模板字符版)</title>
    <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
    <link rel="stylesheet" href="./css/index.css" />
  </head>
  <body>
    <!-- 挂载点 -->
    <div id="app"></div>
    <script src="./js/strvp.js"></script>
    <script src="./js/vp.js"></script>
    <script>
      const node = document.querySelector("#app");
      node.insertAdjacentHTML("beforeEnd", strHtml);
      const vp = new VamVideo(
        document.querySelector(".video-box"), // 挂载父节点
        { // 视频属性
          poster:"./img/bg.png",
          src:"https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4",
          preload:"auto",
          // loop:"loop",
          // autoplay:"autoplay"
        },
        { // 视频样式
          width:"1200px",
          height:"600px"
        }
      );
    </script>
  </body>
</html>


可以看到上面的代码,我直接把标签语句转换为字符串直接挂载到父节点上,这样就更加简洁了。下面的代码就是一堆标签语句。


const strHtml = `
<div class="video-box" onmouseenter="vp.bottomTup()" onmouseleave="vp.bottomTdow()">
      <video class="video-player" oncanplay="vp.useOnplay()" onended="vp.useEnd()"></video>
      <div class="bottom-tool">
        <div class="pv-bar">
          <div class="pv-played"></div>
          <div class="pv-dot" onmousedown="vp.useTime()"></div>
        </div>
        <div class="pv-controls" onmouseleave="vp.selectListHide()">
          <div class="pc-con-l">
            <div class="play-btn" onclick="vp.usePlay()">
              <i class="iconfont icon-bofang"></i>
              <i class="iconfont icon-zanting hide"></i>
            </div>
            <div class="pv-time">
              <span class="pv-currentTime">00:00:00</span>
              <span>/</span>
              <span class="pv-duration">00:00:00</span>
            </div>
          </div>
          <div class="pc-con-r">
            <div class="pv-listen ml">
              <div class="pv-yl">
                <p class="pv-ol" onmousedown="vp.useListen()"></p>
                <p class="pv-bg"></p>
              </div>
              <div class="pv-iconyl" onclick="vp.useVolume()">
                <i class="iconfont icon-yinliang"></i>
                <i class="iconfont icon-jingyin hide"></i>
              </div>
            </div>
            <div class="pv-speed ml">
              <p class="pv-spnum" onmouseover="vp.selectListShow()">1x</p>
              <ul class="selectList" onclick="vp.useSpnum()">
                <li>0.5x</li>
                <li>1x</li>
                <li>1.25x</li>
                <li>1.5x</li>
                <li>2x</li>
              </ul>
            </div>
            <div class="pv-screen ml" onclick="vp.fullScreen()">
              <i class="iconfont icon-quanping"></i>
              <i class="iconfont icon-huanyuan hide"></i>
            </div>
          </div>
        </div>
      </div>
    </div>
`;


我们再进一步,使用Vue.js、React.js分别实现一波。


四、Vue.js


  1. vue@2.6.12:引入Vue.js,这里我们使用@2.6.12。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>VamVideo(Vue.js版)</title>
    <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
    <link rel="stylesheet" href="./css/index.css" />
  </head>
  <body>
    <div id="app">
      <vam-video></vam-video>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="./js/vp.js"></script>
    <script>
      Vue.component("vam-video", {
        template: `
    <div class="video-box" @mouseenter="vp.bottomTup()" @mouseleave="vp.bottomTdow()">
      <video class="video-player" @canplay="vp.useOnplay()" @ended="vp.useEnd()"></video>
      <div class="bottom-tool">
        <div class="pv-bar">
          <div class="pv-played"></div>
          <div class="pv-dot" @mousedown="vp.useTime()"></div>
        </div>
        <div class="pv-controls" @mouseleave="vp.selectListHide()">
          <div class="pc-con-l">
            <div class="play-btn" @click="vp.usePlay()">
              <i class="iconfont icon-bofang"></i>
              <i class="iconfont icon-zanting hide"></i>
            </div>
            <div class="pv-time">
              <span class="pv-currentTime">00:00:00</span>
              <span>/</span>
              <span class="pv-duration">00:00:00</span>
            </div>
          </div>
          <div class="pc-con-r">
            <div class="pv-listen ml">
              <div class="pv-yl">
                <p class="pv-ol" @mousedown="vp.useListen()"></p>
                <p class="pv-bg"></p>
              </div>
              <div class="pv-iconyl" @click="vp.useVolume()">
                <i class="iconfont icon-yinliang"></i>
                <i class="iconfont icon-jingyin hide"></i>
              </div>
            </div>
            <div class="pv-speed ml">
              <p class="pv-spnum" @mouseover="vp.selectListShow()">1x</p>
              <ul class="selectList" @click="vp.useSpnum()">
                <li>0.5x</li>
                <li>1x</li>
                <li>1.25x</li>
                <li>1.5x</li>
                <li>2x</li>
              </ul>
            </div>
            <div class="pv-screen ml" @click="vp.fullScreen()">
              <i class="iconfont icon-quanping"></i>
              <i class="iconfont icon-huanyuan hide"></i>
            </div>
          </div>
        </div>
      </div>
    </div>
          `,
      });
      const vm = new Vue({
        el: "#app",
      });
      const vp = new VamVideo(
        document.querySelector(".video-box"), // 挂载父节点
        {
          // 视频属性
          poster: "./img/bg.png",
          src:
            "https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4",
          preload: "auto",
          // loop:"loop",
          // autoplay:"autoplay"
        },
        {
          // 视频样式
          width: "1200px",
          height: "600px",
        }
      );
    </script>
  </body>
</html>


从上面的代码中可以看到,可以直接在全局实例化一个对象,可以根据自己的需要进行配置。


五、React.js


  1. react.development.js - React 的核心库。
  2. react-dom.development - 提供与 DOM 相关的功能。
  3. babel.min.js - Babel 可以将 ES6 代码转为 ES5 代码,这样我们就能在目前不支持 ES6 浏览器上执行 React 代码。Babel 内嵌了对 JSX 的支持。通过将 Babel 和 babel-sublime 包(package)一同使用可以让源码的语法渲染上升到一个全新的水平。


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>VamVideo(React.js版)</title>
    <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
    <link rel="stylesheet" href="./css/index.css" />
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script src="./js/vp.js"></script>
    <script type="text/babel">
      class VamVideoPlayer extends React.Component {
        constructor(props) {
          super(props);
          this.vper = null;
        }
        vp(v) {
          this.vper = v;
        }
        componentDidMount() {
          let vps = new VamVideo(
            document.querySelector(".video-box"), // 挂载父节点
            {
              // 视频属性
              poster: "./img/bg.png",
              src:
                "https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4",
              preload: "auto",
              // loop:"loop",
              // autoplay:"autoplay"
            },
            {
              // 视频样式
              width: "1200px",
              height: "600px",
            }
          );
          this.vp(vps);
        }
        render() {
          return (
            <div
              className="video-box"
              onMouseEnter={() => this.vper.bottomTup()}
              onMouseLeave={() => this.vper.bottomTdow()}
            >
              <video
                className="video-player"
                onCanPlay={() => this.vper.useOnplay()}
                onEnded={() => this.vper.useEnd()}
              ></video>
              <div className="bottom-tool">
                <div className="pv-bar">
                  <div className="pv-played"></div>
                  <div
                    className="pv-dot"
                    onMouseDown={(e) => this.vper.useTime(e)}
                  ></div>
                </div>
                <div
                  className="pv-controls"
                  onMouseLeave={() => this.vper.selectListHide()}
                >
                  <div className="pc-con-l">
                    <div
                      className="play-btn"
                      onClick={() => this.vper.usePlay()}
                    >
                      <i className="iconfont icon-bofang"></i>
                      <i className="iconfont icon-zanting hide"></i>
                    </div>
                    <div className="pv-time">
                      <span className="pv-currentTime">00:00:00</span>
                      <span>/</span>
                      <span className="pv-duration">00:00:00</span>
                    </div>
                  </div>
                  <div className="pc-con-r">
                    <div className="pv-listen ml">
                      <div className="pv-yl">
                        <p
                          className="pv-ol"
                          onMouseDown={(e) => this.vper.useListen(e)}
                        ></p>
                        <p className="pv-bg"></p>
                      </div>
                      <div
                        className="pv-iconyl"
                        onClick={() => this.vper.useVolume()}
                      >
                        <i className="iconfont icon-yinliang"></i>
                        <i className="iconfont icon-jingyin hide"></i>
                      </div>
                    </div>
                    <div className="pv-speed ml">
                      <p
                        className="pv-spnum"
                        onMouseOver={(e) => this.vper.selectListShow(e)}
                      >
                        1x
                      </p>
                      <ul
                        className="selectList"
                        onClick={(e) => this.vper.useSpnum(e)}
                      >
                        <li>0.5x</li>
                        <li>1x</li>
                        <li>1.25x</li>
                        <li>1.5x</li>
                        <li>2x</li>
                      </ul>
                    </div>
                    <div
                      className="pv-screen ml"
                      onClick={() => this.vper.fullScreen()}
                    >
                      <i className="iconfont icon-quanping"></i>
                      <i className="iconfont icon-huanyuan hide"></i>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          );
        }
      }
      ReactDOM.render(<VamVideoPlayer />, document.getElementById("app"));
    </script>
  </body>
</html>


上面React版本可能有点老,但是逻辑不会变。大家可以使用最新版本或者脚手架来开发一个视频播放器组件,这样一切都是自己说了算。


结语


到这里,我们使用五种方法来实践一个自定义配置视频播放器。梦想就这么简单地实现了!你可以查看完整源码到我的github上,地址在这https://github.com/maomincoding/vamPlayer


项目中主要难点在于拖拽那块,大家可以先自己尝试着去理解,我将会在下一篇主要讲述本项目所遇到的一些问题以及解决方法。欢迎及时关注我的动态哦~谢谢



相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
163 2
|
1月前
|
JavaScript 前端开发 开发者
VUE 开发——Node.js学习(一)
VUE 开发——Node.js学习(一)
64 3
|
20天前
|
监控 前端开发 JavaScript
React 静态网站生成工具 Next.js 入门指南
【10月更文挑战第20天】Next.js 是一个基于 React 的服务器端渲染框架,由 Vercel 开发。本文从基础概念出发,逐步探讨 Next.js 的常见问题、易错点及解决方法,并通过具体代码示例进行说明,帮助开发者快速构建高性能的 Web 应用。
52 10
|
18天前
|
资源调度 前端开发 数据可视化
构建高效的数据可视化仪表板:D3.js与React的融合之道
【10月更文挑战第25天】在数据驱动的时代,将复杂的数据集转换为直观、互动式的可视化表示已成为一项至关重要的技能。本文深入探讨了如何结合D3.js的强大可视化功能和React框架的响应式特性来构建高效、动态的数据可视化仪表板。文章首先介绍了D3.js和React的基础知识,然后通过一个实际的项目案例,详细阐述了如何将两者结合使用,并提供了实用的代码示例。无论你是数据科学家、前端开发者还是可视化爱好者,这篇文章都将为你提供宝贵的洞见和实用技能。
42 5
|
18天前
|
JavaScript 前端开发 开发者
如何在 Visual Studio Code (VSCode) 中使用 ESLint 和 Prettier 检查代码规范并自动格式化 Vue.js 代码,包括安装插件、配置 ESLint 和 Prettier 以及 VSCode 设置的具体步骤
随着前端开发技术的快速发展,代码规范和格式化工具变得尤为重要。本文介绍了如何在 Visual Studio Code (VSCode) 中使用 ESLint 和 Prettier 检查代码规范并自动格式化 Vue.js 代码,包括安装插件、配置 ESLint 和 Prettier 以及 VSCode 设置的具体步骤。通过这些工具,可以显著提升编码效率和代码质量。
180 4
|
1月前
|
人工智能 JavaScript 前端开发
使用Node.js模拟执行JavaScript
使用Node.js模拟执行JavaScript
|
1月前
|
消息中间件 JavaScript 前端开发
用于全栈数据流的 JavaScript、Node.js 和 Apache Kafka
用于全栈数据流的 JavaScript、Node.js 和 Apache Kafka
44 1
|
1月前
|
JavaScript 前端开发
电话号码正则表达式 代码 javascript+html,JS正则表达式判断11位手机号码
电话号码正则表达式 代码 javascript+html,JS正则表达式判断11位手机号码
99 1
|
1月前
|
开发框架 前端开发 JavaScript
React、Vue.js 和 Angular主流前端框架和选择指南
在当今的前端开发领域,选择合适的框架对于项目的成功至关重要。本文将介绍几个主流的前端框架——React、Vue.js 和 Angular,探讨它们各自的特点、开发场景、优缺点,并提供选择框架的建议。
42 6
|
2月前
|
JavaScript 前端开发 API
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示