iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 28
1
Modern Web

前端影片與直播筆記系列 第 28

Day28 Video.js 浮水印套件

Day28 Video.js 浮水印套件

今天的內容一樣是 Video.js

我們來看看如何替播放器加上浮水印。

這裡的浮水印是添加在播放器上,而不是添加在影片上。所以如果有人寫爬蟲抓走影片片源,影片本身還是不會有水印的。

前端能做的不是改變影片(片源)本身的內容,而是從<vidoe>和其周遭的 HTML 標籤著手,想辦法把水印圖像疊在播放器介面上。

將播放器添加浮水印圖片不外乎是三種方式:

  1. 用 HTML 的<img>圖片疊上去
  2. 用 HTML 的空 div 和 CSS 的 background-img 搭配疊上去
  3. 承上,改用偽元素來完成

採上面任一種作法後,用CSS 絕對定位 去對齊播放器位置。

我們可以暴力用 JS 添加水印疊在播放器上,也可以用 Videojs plugins 的方式來添加浮水印,關於 plugins ,videojs 的 文件 也有開發說明,不過看完大概還是有點問號不知道該怎麼做。

幸運的是,網路上仍然找的到非官方的 Video.js 浮水印套件,先來看看此套件原本的用法:

安裝 Video.js 浮水印套件

下載這個 浮水印套件 到本地端,記得先在 HTML 引入(或者 NPM 安裝後 import ) Video.js 的 JS 和 CSS 檔案。確認 Video.js 本身 CSS 和 JS 有引入後才引入浮水印的 JS 和 CSS

<!-- CSS -->
<link href="路徑/videojs.watermark.css" rel="stylesheet">

<!-- JS -->
<script src='路徑/videojs.watermark.js'></script>

範例

HTML

<video id="my-video" class="video-js"><video>

JS

const player = videojs('my-video',{
    sources:[{ src: "http://www.html5videoplayer.net/videos/toystory.mp4"}],
    loop:true,
    muted:true,
    width:"600",
    height:"300",
    controls:true
});

player.watermark({
    file: 'http://literreviewstaffing.com/wp-content/uploads/2015/02/google-1088004_960_720-150x150.png',
    xpos: 100,
    ypos: 100,
    opacity: 0.5,
});

說明

使用videojs()建立videojs物件後,再用.watermark()去建立浮水印相關的設定,.watermark()內可以傳入一個物件,浮水印即會是按照傳入的物件屬性、值做對應的設置。

videojs物件.watermark({
    設定屬性:值
});

當然囉,所謂.watermark() 並不是 Videojs 原本定義好的 Method,而是這個 plugins 添加的。

以下是這個 Library 套件.watermark() 可傳入物件屬性、值說明:

屬性 描述
file 字串 ( URL 路徑 ) 圖片位置
xpos 數值 水印圖在播放器上的位置參數
ypos 數值 水印圖在播放器上的位置參數
opacity 數值 ( 0.1 ~ 1 ) 透明度
className 字串 CSS的class名稱(添加在水印上)
url 字串 ( URL 路徑 ) 浮水印超連結網址
clickable 布林值 設置浮水印超連結是否可點選連結
debug 布林值 顯示 console.log() 內容
xrepeat 布林值 設置圖片是否重複

其中末三項 clickablede、bug、xrepeat 其實沒什麼幫助,之後會解釋。
至此便完成浮水印

問題

雖然完成浮水印功能了,但這個浮水印套件也衍生了一些問題:

1. xpos? ypos?

首先,這個 xpos 和 ypos 其實不是所謂 X 座標 和 Y 座標

videojs物件.watermark({
    file: 'watermarks.png',
    xpos: 50,
    ypos: 50,
    opacity: 0.5,
});

按照這個套件文件寫的內容描述來看

Center: xpos: 50 ypos: 50
Bottom right: xpos: 100 ypos: 100
Top left: xpos: 0 ypos: 0

意思是

xpos ypos 位置
0 0 左上角
0 50 右上角
50 50 中央
0 100 左下角
100 100 右下角

但是這種設置參數的方式很容易讓人誤會,當輸入0、50、100以外的數值,水印就會出現在左上角,這是因為這個套件預設水印位置在左上角。

2. 圖片覆蓋控制列

當我們讓浮水印出現在左下角或右下角時

點擊全螢幕的按鈕會沒反應,用開發者工具來檢查

原來是因為圖片蓋到全螢幕按扭了,這是因為


z-index設置了2000

想解決這問題很簡單,因為圖片在右下角,按照 CSS 的絕對定位來看,我們可以假定套件原始碼是設置了 bottom:0;righr:0;

而 videojs 下方控制條 UI 只有高30px,只要把 bottom:0; 改成 bottom:30px; 就能解決這問題。

檢查原始碼

為了解決這兩個問題,檢查這個套件的 JS

defaults 參數

從上面這段可以看到,原來可以傳入 .watermark() 物件的屬性和值,是在 defaults 這邊定義的。

extend、videojs.plugin()

在 defaults 下方段落的 extendvideojs.plugin 把這個 plugin 綁到 videojs上 (其實是新增到 videojs 的 prototypal 上,關於何謂原型可以參考我 去年的筆記 )

xpos、ypos

參數 xpos 和 ypos ,其實只是用來在裏頭做 if 判定,並不是真的依數值多少來賦值給絕對定位。既然這個套件的定位 ( 左上、右上、左下、右下、中央 ) 是寫死的,用 xpos 和 ypos 反而容易讓人誤會。

clickable

基本上只是和 url 參數在 if 搭配判斷用,但是要判斷是否產生連結用 url 判斷是否空值就好,多比較這個參數沒什麼意義。

debug

只是用來打開 console.log 的內容,其實也沒什麼意義。

xrepeat

這個參數相關程式碼,在 JS 裏頭被註解了,那這參數也沒什麼意義。

修改、自定義

以下是我修改過後的版本: ITMAN_watermark

.ITMAN_watermark() 可傳物件屬性、值

屬性 描述 預設
file 字串 ( URL 路徑 ) 圖片位置
position 字串 水印圖在播放器上的位置 "bottom-right"
opacity 數值 ( 0.1 ~ 1 ) 透明度 1
className 字串 CSS的class名稱(添加在水印上)
url 字串 ( URL 路徑 ) 浮水印超連結網址

postiion

postiion 會決定浮水印圖片的位置, 我以 postiion 取代原本的 xpos、ypos ,其可設置的值為:

浮水印位置
"top-left" 或 "left-top" 左上
"bottom-left" 或 "left-bottom" 左下
"top-right" 或 "right-top" 右上
"bottom-right" 或 "right-bottom" 右下

className

className 的設定為,當使用者傳入自己的 class 樣式名稱,會接續在 ITMAN.watermanmark.css 的樣式後面變成

<div class="itman-watermark 你傳入的樣式名稱">
    <img>
</div>

完整程式碼

或見 github ITMAN_watermark

JavaScript

(function() {

  const defaults = {
    file: "",
    position: "bottom-right",
    opacity: 1,
    url: "",
    className: "itman-watermark",
  };
  
  const extend = function() {
    let args, target, i, object, property;
    args = Array.prototype.slice.call(arguments);
    target = args.shift() || {};
    for (i in args) {
      object = args[i];
      for (property in object) {
        if (object.hasOwnProperty(property)) {
          if (typeof object[property] === "object") {
            target[property] = extend(target[property], object[property]);
          } else {
            target[property] = object[property];
          }
        }
      }
    }
    return target;
  };

  let div;

  // 使用 .registerPlugin 和 extend 把自訂內容綁進videojs
  // .registerPlugin() 第一個參數名稱會對應之後新建進videojs原形鏈上的方法名稱
  videojs.registerPlugin("watermark", function(settings) {

    let options, player, video, img, link;
    options = extend(defaults, settings);

    
    player = this.el();
    video = this.el().getElementsByTagName("video")[0];

    // 創建浮水印相關元素的 div
    if (!div) {
      div = document.createElement("div");
      options.className === "vjs-watermark"
        ? (div.className = options.className)
        : (div.className = "vjs-watermark " + options.className);
    } else {
      // 如果 div 已存在 就清空 div 內容
      div.innerHTML = "";
    }

    // 圖片設定
    if (options.file) {
      img = document.createElement("img");
      div.appendChild(img);
      img.src = options.file;
    }

    // 浮水印位置設定
    if (options.position === "top-left" || options.position === "left-top") {
      // 左上
      div.style.top = "0";
      div.style.left = "0";
    } else if (
      options.position === "top-right" ||
      options.position === "right-top"
    ) {
      // 右上
      div.style.top = "0";
      div.style.right = "0";
    } else if (
      options.position === "bottom-right" ||
      options.position === "right-bottom"
    ) {
      // 右下 ( 30px 是為了避開與導覽列UI重疊 )
      div.style.bottom = "30px";
      div.style.right = "0";
    } else if (
      options.position === "bottom-left" ||
      options.position === "left-bottom"
    ) {
      // 左下 ( 30px 是為了避開與導覽列UI重疊 )
      div.style.bottom = "30px";
      div.style.left = "0";
    } else if (options.position === "center") {
      // Center
      div.style.top = this.height() / 2 + "px";
      div.style.left = this.width() / 2 + "px";
    }

    // 透明度設定
    div.style.opacity = options.opacity;

    // url參數有值(連結) 就替浮水印添加 a 標籤
    if (options.url !== "") {
      link = document.createElement("a");
      link.href = options.url;
      link.target = "_blank";
      link.appendChild(div);
      player.appendChild(link);
    } else {
      player.appendChild(div);
    }
  });
})();


CSS

.itman-watermark {
  position: absolute;
  display: inline-block;
  z-index: 1;
  width: 20%;
}
.itman-watermark img {
  width: 100%;
  height: auto;
}

 
 
 
 
 
 

參考

videojs 文件 plugins
videojs 浮水印 Library


上一篇
Day27 Video.js + React 播放器整合
下一篇
Day29 Video.js 影片標題套件
系列文
前端影片與直播筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言