JS 用CANVAS自定义VIDEO播放器
概述
HTML5用规范的VIDEO标签取代了以往需要借助外部控件进行视频播放的历史(如Flash播放器),实现了浏览器无插件统一视频播放标准。
统一的VIDEO标签也有其自身局限,如VIDEO标签在不同浏览器实现时层级控制不尽相同,播放器外观也在不同浏览器表现不一,虽然通过浏览器私有CSS可以定义风格,但其可定义风格是非常有限的,等等问题。
为了实现客户定制统一风格视频播放器,HTML网页可以借助CANVAS进行自定义VIDEO绘制。
CANVAS绘制核心代码
let canvas = this.canvas;
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(this.video, 0, 0, canvas.width, canvas.height); // this.video = document.getElementById(videoId);
VIDEO标签可以和普通IMG一样进行绘制;
VIDEO仅作与服务器通讯、视频下载、解码等功能,显示和UI则通过CANVAS和其他HTML元素替代。
播放器代码
/*
* singleplayer.js
* 为了优化性能,对于页面中多于一个video标签,但同时只有一个VIDEO点播的,SinglePlayer仅使用一个VIDEO标签
* 第一个SinglePlayer保留VIDEO标签,并将VIDEO设置为1x1(苹果浏览器可能要求VIDEO不能隐藏,否则直接冻结优化掉视频流)
* if (null == g_SinglePlayer) { // 仅保留第一个VIDEO标签,并将尺寸设置为1x1
$(this.video).css("width", "1px").css("height", "1px");
$(this.video).parent().append(html);
g_SinglePlayer = this;
}
else { // 用SinglePlayer替换VIDEO标签
$(this.video).parent().html(html);
this.videoId = g_SinglePlayer.playerId;
this.video = g_SinglePlayer.video;
if (!this.isFullPlayer) {
g_VidepPlayerList.push(this);
}
}
* 对于部分浏览器,设置video.src则会自动缓存视频,即使通过DOM操作删除video,也不会导致已经缓存下载的视频流取消缓存和下载
* SinglePlayer通过自定义videoSource属性替代src属性,在激活对应player时再用videoSource值设置src
* */
var g_VidepPlayerList = new Array();
var g_FullscreenPlayer = null;
var g_CurrentPlayer = null;
var g_SinglePlayer = null;
var g_ActivedPlayer = null;
var g_PlayerClicked = false;
class SinglePlayer {
constructor(videoId, videoWidth = 960, videoHeight = 540, full = false) {
this.videoId = videoId;
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
this.isVisibled = true;
this.isFullPlayer = full;
this.playerId = videoId "_player";
this.canvasId = videoId "_canvas";
this.controlsId = videoId "_controls";
this.timeId = videoId "_time";
this.playId = videoId "_play";
this.muteId = videoId "_mute";
this.seekId = videoId "_seek";
this.fullscreenId = videoId "_fullscreen";
this.isSeeking = false;
this.isFirstPlayed = false;
this.video = document.getElementById(videoId);
if (null == this.video) {
console.log("没有找到视频[" videoId "]");
}
this.poster = new Image();
this.poster.src = $(this.video).attr("poster");
this.videoSource = $(this.video).attr("videoSource");
this.canvas = null;
this.player = null;
this.isFullscreen = false;
this.isPlaying = false;
this.idletime = 0;
this.lasttimestamp = 0;
this.frames = 0;
this.frametime = 0;
this.fps = 0;
this.touchX = null;
this.touchY = null;
this.seekStart = 0;
this.message = "";
var html = "";
if (!full) {
html = '<div id="' this.playerId '" class="canvas_player" style="position:absolute;width:100%;">';
html = ' <canvas class="canvasplayer_canvas" id="' this.canvasId '" width="' this.videoWidth '" height="' this.videoHeight '" style="width:100%;"></canvas>';
html = ' <div class="canvasplayer_controls" id="' this.controlsId '" style="position:absolute;width:100%;height:20%;bottom:1.6%;left:0px;background-image:url(\'img/videoplayer/backgnd.png\');transform: translateZ(10px);z-order:10">';
html = ' <div class="canvasplayer_controls" style="position:absolute;width:85%;height:30%;left:10%;top:40%"><input type="range" min="0.0" max="1.0" value="0.0" step="0.01" id="' this.seekId '" style="width:100%;height:15%;::-webkit-slider-thumb{width:10px;height:10px;}" /></div>';
html = ' <div class="canvasplayer_controls" id="' this.timeId '" style="position:absolute;width:60%;left:10%;top:15%;text-align:left;"><span style="color:white;text-align:left;font-size:50%">0:00 / 0:00</span></div>';
html = ' <div class="canvasplayer_controls" id="' this.playId '" style="position:absolute;width:3%;left:5%;top:39%;padding-top:4px;"><img src="https://blog.csdn.net/qq_31042143/article/details/img/videoplayer/play.png" /></div>';
html = ' <div class="canvasplayer_controls" id="' this.muteId '" style="position:absolute;width:3%;left:80%;top:15%"><img src="https://blog.csdn.net/qq_31042143/article/details/img/videoplayer/mute.png" /></div>';
html = ' <div class="canvasplayer_controls" id="' this.fullscreenId '" style="position:absolute;width:3%;left:90%;top:15%"><img src="https://blog.csdn.net/qq_31042143/article/details/img/videoplayer/fullscreen.png" /></div>';
html = ' </div>';
html = '</div>';
}
else {
html = '<div id="' this.playerId '" class="canvas_player" style="position:absolute;width:100%;left:0px;top:0px;">';
html = ' <canvas class="canvasplayer_canvas" id="' this.canvasId '" width="' this.videoWidth '" height="' this.videoHeight '" style="width:100%;height:' window.innerWidth 'px"></canvas>';
html = ' <div class="canvasplayer_controls" id="' this.controlsId '" style="position:absolute;width:100%;height:20%;bottom:0px;left:0px;background-image:url(\'img/videoplayer/backgnd.png\');transform: translateZ(10px);z-order:10">';
html = ' <div class="canvasplayer_controls" style="position:absolute;width:85%;height:30%;left:10%;bottom:20%"><input type="range" min="0.0" max="1.0" value="0.0" step="0.01" id="' this.seekId '" style="width:100%;height:15%" /></div>';
html = ' <div class="canvasplayer_controls" id="' this.timeId '" style="position:absolute;width:60%;left:10%;top:15%;text-align:left;"><span style="color:white;text-align:left;font-size:50%">0:00 / 0:00</span></div>';
html = ' <div class="canvasplayer_controls" id="' this.playId '" style="position:absolute;width:3%;left:5%;top:50%"><img src="https://blog.csdn.net/qq_31042143/article/details/img/videoplayer/play.png" /></div>';
html = ' <div class="canvasplayer_controls" id="' this.muteId '" style="position:absolute;width:3%;left:80%;top:15%"><img src="https://blog.csdn.net/qq_31042143/article/details/img/videoplayer/mute.png" /></div>';
html = ' <div class="canvasplayer_controls" id="' this.fullscreenId '" style="position:absolute;width:3%;left:90%;top:15%"><img src="https://blog.csdn.net/qq_31042143/article/details/img/videoplayer/unfullscreen.png" /></div>';
html = ' </div>';
html = '</div>';
g_FullscreenPlayer = this;
}
if (null == g_SinglePlayer) {
$(this.video).css("width", "1px").css("height", "1px");
$(this.video).parent().append(html);
g_SinglePlayer = this;
}
else {
$(this.video).parent().html(html);
this.videoId = g_SinglePlayer.playerId;
this.video = g_SinglePlayer.video;
if (!this.isFullPlayer) {
g_VidepPlayerList.push(this);
}
}
$("#" this.canvasId).click(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var id = $(this).attr("id");
var player = SinglePlayer.getPlayerByCanvasId(id);
if (!player.isFullPlayer && SinglePlayer.GetActivePlayer() != player) {
SinglePlayer.ActivePlayer(player);
}
player.showcontrols();
if (!player.isFirstPlayed) {
player.play();
}
});
$("#" this.playId).click(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var playerId = $(this).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
if (!player.isFullPlayer && SinglePlayer.GetActivePlayer() != player) {
SinglePlayer.ActivePlayer(player);
}
if (player.isPlaying) {
player.pause();
}
else {
player.play();
}
});
$("#" this.muteId).click(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var playerId = $(this).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
player.mute(!player.isMuted());
player.updateUI();
});
$("#" this.seekId).change(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var playerId = $(this).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
player.seek($(this).val());
});
var seek = document.getElementById(this.seekId);
seek.addEventListener('touchstart', function (event) {
if (!SinglePlayer.TestPlayerClicked()) return;
//event.preventDefault();
var playerId = $(seek).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
player.isSeeking = true;
player.showcontrols();
if (event.targetTouches.length == 1) {
var touch = event.targetTouches[0];
player.touchX = touch.clientX;
player.touchY = touch.clientY;
player.seekStart = new Number($(seek).val());
}
});
seek.addEventListener('touchmove', function (event) {
if (!SinglePlayer.TestPlayerClicked()) return;
//event.preventDefault();
var playerId = $(seek).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
player.showcontrols();
player.isSeeking = true;
if (event.targetTouches.length == 1) {
var touch = event.targetTouches[0];
var dx = touch.clientX - player.touchX;
var dy = touch.clientY - player.touchY;
dy /= $(seek).outerWidth(false);
player.message = "setPos(" player.seekStart dy ")";
$(seek).val(player.seekStart dy)
player.seek(player.seekStart dy);
}
});
seek.addEventListener('touchend', function (event) {
if (!SinglePlayer.TestPlayerClicked()) return;
//event.preventDefault();
// alert("touchend at " $(seek).closest('.canvas_player').attr("id") " with " event.targetTouches.length);
if (event.targetTouches.length == 0) {
var playerId = $(seek).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
setTimeout(function () {
player.isSeeking = false;
}, 100, this)
}
});
$("#" this.seekId).mousedown(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var playerId = $(this).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
player.isSeeking = true;
});
$("#" this.seekId).mouSEO((Search Engine Optimization))ut(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var playerId = $(this).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
player.isSeeking = false;
});
$("#" this.fullscreenId).click(function () {
if (!SinglePlayer.TestPlayerClicked()) return;
var playerId = $(this).closest('.canvas_player').attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
console.log("全屏按钮[" this.isFullPlayer "]");
if (!player.isFullPlayer) {
player.fullscreen();
}
else {
SinglePlayer.ExitFullscreen();
}
});
this.canvas = document.getElementById(this.canvasId);
this.player = document.getElementById(this.playerId);
}
static getPlayerByPlayerId(playerId) {
if (null != g_FullscreenPlayer && g_FullscreenPlayer.playerId == playerId) {
return g_FullscreenPlayer;
}
for (var i = 0; i < g_VidepPlayerList.length; i) {
if (g_VidepPlayerList[i].playerId == playerId) {
return g_VidepPlayerList[i];
}
}
return null;
}
static getPlayerByVideoId(videoId) {
if (null != g_FullscreenPlayer && g_FullscreenPlayer.videoId == videoId) {
return g_FullscreenPlayer;
}
for (var i = 0; i < g_VidepPlayerList.length; i) {
if (g_VidepPlayerList[i].videoId == videoId) {
return g_VidepPlayerList[i];
}
}
return null;
}
static getPlayerByCanvasId(canvasId) {
if (null != g_FullscreenPlayer && g_FullscreenPlayer.canvasId == canvasId) {
return g_FullscreenPlayer;
}
for (var i = 0; i < g_VidepPlayerList.length; i) {
if (g_VidepPlayerList[i].canvasId == canvasId) {
return g_VidepPlayerList[i];
}
}
return null;
}
static toTime(seconds) {
if (isNaN(seconds)) {
return "0:00";
}
else {
var minutes = parseInt(seconds / 60);
seconds = parseInt(seconds - minutes * 60);
if (seconds < 10) {
return minutes ":0" seconds;
}
else {
return minutes ":" seconds;
}
}
}
static isNullOrEmpty(str) {
return null == str || undefined == str || "" == str || "undefined" == str;
}
updateUI() {
if (this.isMuted()) {
$("#" this.muteId).find("img").attr("src", "img/videoplayer/mute.png");
}
else {
$("#" this.muteId).find("img").attr("src", "img/videoplayer/unmute.png");
}
if (!this.isPlaying) {
$("#" this.playId).find("img").attr("src", "img/videoplayer/play.png");
}
else {
$("#" this.playId).find("img").attr("src", "img/videoplayer/pause.png");
}
if (null == g_FullscreenPlayer) {
$("#" this.fullscreenId).hide();
}
else {
$("#" this.fullscreenId).show();
}
}
play() {
if (this.isFirstPlayed) {
}
this.video.play();
this.isPlaying = true;
this.isFirstPlayed = true;
}
pause() {
this.video.pause();
this.isPlaying = false;
}
showcontrols() {
this.idletime = 0;
$("#" this.controlsId).show();
}
mute(muted) {
this.video.muted = muted;
}
isMuted() {
return this.video.muted;
}
seek(pos) {
if (!isNaN(this.video.currentTime) && !isNaN(this.video.duration)) {
this.video.currentTime = pos * this.video.duration;
}
}
fullscreen() {
if (null != g_FullscreenPlayer) {
this.isFullscreen = true;
g_CurrentPlayer = this;
g_FullscreenPlayer.setVisible(true);
g_FullscreenPlayer.isPlaying = this.isPlaying;
g_FullscreenPlayer.isMuted = this.isMuted;
g_FullscreenPlayer.isFirstPlayed = this.isFirstPlayed;
}
}
static ExitFullscreen() {
console.log("ExitFullscreen");
if (null != g_FullscreenPlayer && null != g_CurrentPlayer) {
g_CurrentPlayer.isPlaying = g_FullscreenPlayer.isPlaying;
g_CurrentPlayer.isMuted = g_FullscreenPlayer.isMuted;
g_CurrentPlayer.isFirstPlayed = g_FullscreenPlayer.isFirstPlayed;;
g_CurrentPlayer.isFullscreen = false;
g_FullscreenPlayer.setVisible(false);
g_CurrentPlayer = null;
}
}
setVisible(visible = true) {
this.isVisibled = visible;
if (visible) {
$(this.player).parent().show();
this.showcontrols();
}
else {
$(this.player).parent().hide();
}
}
nextFrame() {
}
render(timestamp) {
var ticks = (timestamp - this.lasttimestamp) / 1000;
this.lasttimestamp = timestamp;
this.frametime = ticks;
if (this.frametime >= 0.1) {
this.nextFrame();
this.frametime = 0;
}
this.frames;
this.fps = this.frames * 1000 / this.timestamp;
this.idletime = ticks;
// 绘制视频
let canvas = this.canvas;
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
var vh = this.video.videoWidth;
var vw = this.video.videoHeight;
var aspect = vw / vh;
var height = canvas.width * aspect;
var width = canvas.width;
var top = (canvas.height - height) / 2;
var left = 0;
if (top < 0) {
width = canvas.height / aspect;
height = canvas.height;
top = 0;
left = (canvas.width - width) / 2
}
ctx.drawImage(this.video, left, top, width, height);
if (!this.isFirstPlayed) {
if (null != this.poster) {
var vh = this.poster.width;
var vw = this.poster.height;
var aspect = vw / vh;
var height = canvas.width * aspect;
var width = canvas.width;
var top = (canvas.height - height) / 2;
var left = 0;
if (top < 0) {
width = canvas.height / aspect;
height = canvas.height;
top = 0;
left = (canvas.width - width) / 2
}
ctx.drawImage(this.poster, left, top, width, height);
}
}
var text = timestamp " - " this.message;
ctx.font = "30px Verdana";
ctx.fillStyle = "red";
// ctx.fillText(text, canvas.width * 0.05, canvas.height * 0.05);
var text = SinglePlayer.toTime(this.video.currentTime) " \/ " SinglePlayer.toTime(this.video.duration);
$("#" this.timeId).find("span").html(text);
if (!this.isSeeking) {
var seek = 0;
if (!isNaN(this.video.currentTime) && !isNaN(this.video.duration)) {
seek = this.video.currentTime / this.video.duration
}
$("#" this.seekId).val(seek);
}
if (this.idletime > 3) {
$("#" this.controlsId).hide();
}
}
static RenderPlayers(timestamp) {
if (null != g_FullscreenPlayer && g_FullscreenPlayer.isVisibled) {
g_FullscreenPlayer.render(timestamp);
}
else {
for (var i = 0; i < g_VidepPlayerList.length; i) {
var player = g_VidepPlayerList[i];
if (player.isVisibled) {
player.render(timestamp);
}
}
}
window.requestAnimationFrame((timestamp) => SinglePlayer.RenderPlayers(timestamp));
}
static Init() {
if (null != g_FullscreenPlayer) {
g_FullscreenPlayer.setVisible(false);
}
window.requestAnimationFrame((timestamp) => SinglePlayer.RenderPlayers(timestamp));
}
static ActivePlayer(player) {
if (player == g_FullscreenPlayer) {
console.log("不能将全屏播放器设置为激活");
return;
}
for (var i = 0; i < g_VidepPlayerList.length; i) {
if (g_VidepPlayerList[i] != player) {
g_VidepPlayerList[i].setVisible(false)
}
}
if (null != g_ActivedPlayer) {
g_ActivedPlayer.pause();
}
g_ActivedPlayer = player;
if (null != g_ActivedPlayer) {
g_ActivedPlayer.setVisible(true);
g_SinglePlayer.video.pause();
$(g_SinglePlayer.video).attr("src", g_ActivedPlayer.videoSource);
g_ActivedPlayer.isFirstPlayed = false;
g_ActivedPlayer.isPlaying = false;
g_ActivedPlayer.updateUI();
}
}
static GetActivePlayer() {
return g_ActivedPlayer;
}
static IsPlayerClicked() {
return g_PlayerClicked;
}
static TestPlayerClicked() {
console.log("g_PlayerClicked=" g_PlayerClicked);
if (g_PlayerClicked) {
return false;
}
g_PlayerClicked = true;
setTimeout(function () {
g_PlayerClicked = false;
}, 100, null);
return true;;
}
}
使用页面HTML代码
<!-- 视频 -->
<div class="popCon popCon1">
<div class="videoPop">
<video videoSource="video/bird_family1.mp4" id="myVideo1" poster="video/bird_family1.JPG" controls></video>
</div>
</div>
<div class="popCon popCon2">
<div class="videoPop">
<video videoSource="video/bird_family2.mp4" id="myVideo2" poster="video/bird_family2.JPG" controls></video>
</div>
</div>
<div class="popCon popCon3">
<div class="videoPop">
<video videoSource="video/bird_family3.mp4" id="myVideo3" poster="video/bird_family3.JPG" controls></video>
</div>
</div>
<div class="popCon popCon4">
<div class="videoPop">
<video videoSource="video/bird_family4.mp4" id="myVideo4" poster="video/bird_family4.JPG" controls></video>
</div>
</div>
<div id="realplayer"
style="position:absolute;width:1px;height:1px;z-index:-1;transform:translateZ(-1px);left:-10000px">
<video id="realVideo" class="full_player" poster="video/casa_v1.JPG" webkit-playsinline="true"
playsinline="true" x5-video-player-type="h5" x5-video-orientation="landscape" controls></video>
</div>
<div id="fullplayer"
style="position:absolute;width:100%;height:100vh;z-index:999;transform:translateZ(999px);left:0px;top:0px;display:none;">
<video id="fullVideo" class="full_player" poster="video/casa_v1.JPG" webkit-playsinline="true"
playsinline="true" x5-video-player-type="h5" x5-video-orientation="landscape" controls></video>
</div>
使用页面JS代码
// 先创建真实播放器和全屏播放器
new SinglePlayer("realVideo", window.innerHeight, window.innerWidth, false);
new SinglePlayer("fullVideo", window.innerHeight, window.innerWidth, true);
// 创建每个VIDEO对应播放器(不会生成CANVAS)
var index = 0;
$("video").each(function (index, element) {
if (!$(element).hasClass("full_player")) {
$(element).attr("id", "myVideo_" index);
new SinglePlayer("myVideo_" index, 960, 540, false);
index ;
}
});
// 切换slider时激活对应视频
function SlideVideo(pop) {
var domPlayer = $("." pop).find(".canvas_player");
var playerId = $(domPlayer).attr("id");
var player = SinglePlayer.getPlayerByPlayerId(playerId);
if (null != player) {
SinglePlayer.ActivePlayer(player);
console.log("切换视频成功[" playerId "]")
}
else {
console.log("切换视频失败[" playerId "]")
}
}
使用示例
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfibbbb
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel图片置于文字下方的方法
PHP中文网 06-27 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22