7

SkeyeWebPlayer.js H5播放器开发之播放器控制栏部分功能的实现(四)

 1 year ago
source link: https://blog.51cto.com/openskeye/5851790
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

SkeyeWebPlayer.js H5播放器开发之播放器控制栏部分功能的实现(四)

精选 原创

SkeyeWebPlayer.js H5免费播放器

  • [x] 支持Websocket-RTSP播放;
  • [x] 支持 m3u8(HLS) 播放;
  • [x] 支持 HTTP-FLV/WS-FLV 播放;
  • [x] 支持 HEVC/H265 播放;
  • [x] 支持直播和点播播放;
  • [x] 支持全屏显示;
  • [x] 支持重连播放;
  • [x] 具有 H.264 + AAC 编解码器播放功能
  • [x] HTTP FLV RTSP低延迟实时流播放 (HLS延时稍大一点,大概在2s—3s左右)
  • [x] WS-FLV 通过 WebSocket 实时流播放
  • [x] 兼容 Chrome, FireFox, Safari IE11 和 Edge
  • [x] 十分低开销,并且通过你的浏览器进行硬件加速
  • [x] 支持全屏或比例显示;
  • [x] 支持播放器快照截图;
  • [x] 支持Android、iOS播放;

本节将实现播放器的控制栏及其功能的实现。效果图如下:

SkeyeWebPlayer.js H5播放器开发之播放器控制栏部分功能的实现(四)_H5免费播放器

(1)、我把控制栏的每一个小功能放到一个单独的js内,最后在统一到control-bar里面进行统一的管理,这样做管理方便,操作简单。如下图目录结构:

SkeyeWebPlayer.js H5播放器开发之播放器控制栏部分功能的实现(四)_SkeyeWebPlayer_02

(2)、控制栏所使用的图标可以使用图片,和font字体图标,字体图标到相关的网站下载即可(例如:​ ​Iconfont-阿里巴巴矢量图标库​​):

(3)、控制栏分左、中、右,3个部分,

  • 左边部分:播放/暂停按钮、停止、声音、以及一个LIVE的标志。
  • 右边部分:视频画面拉伸/按比例显示、快照下载、全屏/还原。
  • 中间部分:有一个视频播放的进度条,对视频文件播放时,才显示,(回放等)

SkeyeWebPlayer.js H5播放器开发之播放器控制栏部分功能的实现(四)_H5免费播放器_03

(4)、实现样式

  • 首先创建最外部div,和左中右 3个div
let controlBarEl = this.createEl('div', {className: 'vss-control-bar'});
let controlBarLeftEl = this.createEl('div', {className: 'vss-control-bar-left'});
let controlBarConterEl = this.createEl('div', {className: 'vss-control-bar-conter'});
let controlBarrRightEl = this.createEl('div', {className: 'vss-control-bar-right'});
  • 播放按钮play-btn.js的相关代码如下:
    (1)、dom.js的内容代码放在最下面
    (2)、同封装的createEl方法创建好元素,绑定click事件来控制播放的状态
    (3)、self.on('status', fun) 来监听 播放状态的变化,来控制显示播放或暂停按钮
/**
* play-btn.js
*/
import {createEl} from '../utils/dom';

class PlayBtn {
constructor(self, dom) {
this.boxDom = self.boxDom;
this.isFill = false;
this.isPlaying = false;
this.PlayBtnEl = createEl(
'div',
{className: 'vss-btn-group vss-pointer'},
{}
);
this.IconEl = createEl(
'span',
{className: 'vssfont vss-play'},
{title: '播放'}
);
this.PlayBtnEl.appendChild(this.IconEl);
dom.appendChild(this.PlayBtnEl);

this.PlayBtnEl.onclick = (e) => {
if (self.url) {
if (this.isPlaying) {
this.isPlaying = false;
this.PlayBtnEl.title = '播放';
this.PlayBtnEl.classList.remove('vss-pause');
this.PlayBtnEl.classList.add('vss-play');
this.pause(self);
} else {
this.isPlaying = true;
this.PlayBtnEl.title = '暂停';
this.PlayBtnEl.classList.remove('vss-play');
this.PlayBtnEl.classList.add('vss-pause');
this.play(self);
}
} else {
console.log('url is empty');
}
};

self.on('status', (status) => {
if (status === 100) {
this.isPlaying = true;
this.PlayBtnEl.title = '暂停';
this.PlayBtnEl.classList.remove('vss-play');
this.PlayBtnEl.classList.add('vss-pause');
}
});
}

// 播放
play(self) {
self.play(self.url, 1, self.seekTimeSecs);
}

// 暂停
pause(self) {
self.pause();
}
}


export default PlayBtn;
  • 全屏按钮fullscreen-toggle.js的相关代码如下,在操作全屏与取消全屏控制时需要注意:
    (1)、开启全屏是使用 this.boxDom.requestFullscreen() ,this.boxDom是表示播放器容器的这个元素,对他调用requestFullscreen方法。
    (2)、但是在取消全屏的时候 是使用 document.exitFullscreen(); 是对document进行操作,这两个操作的对象需要注意一下,别搞错了。
/**
* fullscreen-toggle.js
*/
import {createEl} from '../utils/dom';

class FullscreenToggle {
constructor(self, dom) {
this.boxDom = self.boxDom;
this.isFullScreen = false;
this.fullScreenBtnEl = createEl('div', {className: 'vss-full-screen-btn vss-pointer'}, {});
this.fullScreenIconEl = createEl('span', {className: 'vssfont vss-fullscreen'}, {
title: '全屏'
});
this.fullScreenBtnEl.appendChild(this.fullScreenIconEl);
dom.appendChild(this.fullScreenBtnEl);

this.fullScreenBtnEl.onclick = (e) => {
if (this.isFullScreen) {
// 取消全屏
this.isFullScreen = false;
self.callbackFunc('unFull');
this.fullScreenIconEl.title = '全屏';
this.fullScreenIconEl.classList.remove('vss-cancel-fullscreen');
this.fullScreenIconEl.classList.add('vss-fullscreen');
this.exitFullScreen();
} else {
// 全屏
this.isFullScreen = true;
self.callbackFunc('full');
this.fullScreenIconEl.title = '取消全屏';
this.fullScreenIconEl.classList.remove('vss-fullscreen');
this.fullScreenIconEl.classList.add('vss-cancel-fullscreen');
this.requestFullscreen();
}
};

// 监听播放器是否为全屏状态
// document.addEventListener('fullscreenchange', function (e) {
//
// });
}

// 全屏
requestFullscreen() {
if (this.boxDom.requestFullscreen) {
this.boxDom.requestFullscreen();
} else if (this.boxDom.mozRequestFullScreen) {
this.boxDom.mozRequestFullScreen();
} else if (this.boxDom.webkitRequestFullscreen) {
this.boxDom.webkitRequestFullscreen();
} else if (this.boxDom.msRequestFullscreen) {
this.boxDom.msRequestFullscreen();
}
}

// 取消全屏
exitFullScreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}
}
}


export default FullscreenToggle;
  • 其他的我就不一一描述了,与上面的写法基本差不多,最后在放一个dom.js。
export function isObject(value) {
return !!value && typeof value === 'object';
}

export function isEl(value) {
return isObject(value) && value.nodeType === 1;
}

export function isTextNode(value) {
return isObject(value) && value.nodeType === 3;
}

export function normalizeContent(content) {

// First, invoke content if it is a function. If it produces an array,
// that needs to happen before normalization.
if (typeof content === 'function') {
content = content();
}

// Next up, normalize to an array, so one or many items can be normalized,
// filtered, and returned.
return (Array.isArray(content) ? content : [content]).map(value => {

// First, invoke value if it is a function to produce a new value,
// which will be subsequently normalized to a Node of some kind.
if (typeof value === 'function') {
value = value();
}

if (isEl(value) || isTextNode(value)) {
return value;
}

if (typeof value === 'string' && (/\S/).test(value)) {
return document.createTextNode(value);
}
}).filter(value => value);
}

export function appendContent(el, content) {
normalizeContent(content).forEach(node => el.appendChild(node));
return el;
}

/**
* 创建元素并应用属性、属性和插入内容.
* @param {string} [tagName='div'] 要创建的标记的名称.
* @param {Object} [properties={}] 要应用的元素属性.
* @param {Object} [attributes={}] 要应用的元素属性.
* @param {string} [content] 内容描述符对象.
* @return {Element} 创建的元素.
*/
export function createEl(tagName = 'div', properties = {}, attributes = {}, content) {

const el = document.createElement(tagName);
Object.getOwnPropertyNames(properties).forEach(function (propName) {
const val = properties[propName];
if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
// log.warn('Setting attributes in the second argument of createEl()\n' +
// 'has been deprecated. Use the third argument instead.\n' +
// `createEl(type, properties, attributes). Attempting to set ${propName} to ${val}.`);
el.setAttribute(propName, val);
} else if (propName === 'textContent') {
// textContent(el, val);
} else if (el[propName] !== val || propName === 'tabIndex') {
el[propName] = val;
}
});

Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
el.setAttribute(attrName, attributes[attrName]);
});

if (content) {
appendContent(el, content);
}

return el;
}
  • 如有错误还请大家指正。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK