自定义视频播放控件样式
In this guide, you will learn how to create a custom video player using the <video> element and CanJS. The custom video player will:
在本指南中,您将学习如何使用<video>元素和CanJS创建自定义视频播放器。 自定义视频播放器将:
Have custom play and pause buttons. 具有自定义播放和暂停按钮。 Show the current time and duration of the video. 显示视频的当前时间和时长。Have a <input type="range"> slider that can adjust the position of the video.
有一个<input type="range">滑块可以调整视频的位置。
The final player looks like:
最终玩家看起来像:
演示地址
The following sections are broken down into the following parts:
以下各节分为以下几部分:
The problem — A description of what the section is trying to accomplish.
问题 -对本节要完成的工作的描述。
What you need to know — Browser or CanJS APIs that are useful for solving the problem.
您需要知道的 -对解决问题有用的浏览器或CanJS API。
The solution — The solution to the problem.
解决方案 —解决问题的方法。
START THIS TUTORIAL BY Forking THE FOLLOWING CodePen:
通过分叉以下CodePen开始本教程 :
Click the Edit in CodePen button. The CodePen will open in a new window. Click the Fork button.
单击Edit in CodePen按钮。 CodePen将在新窗口中打开。 单击Fork按钮。
演示地址
This CodePen:
该CodePen:
Creates a <video> element that loads a video. Right click and select “Show controls” to see the video’s controls.
创建一个<video>元素来加载视频。 右键单击并选择“显示控件”以查看视频的控件 。
Loads CanJS's custom element library: Component.
加载CanJS的自定义元素库: Component 。
In this section, we will:
在本节中,我们将:
Create a custom <video-player> element that takes a src attribute and creates a <video> element within itself. We should be able to create the video like:
创建一个具有src属性的自定义<video-player>元素,并在其内部创建一个<video>元素。 我们应该能够像这样创建视频:
<video-player src:raw="http://bit.ly/can-tom-n-jerry"> </video-player>The embedded <video> element should have the native controls enabled.
嵌入的<video>元素应启用本机控件。
When complete, the result will look exactly the same as the player when you started. The only difference is that we will be using a custom <video-player> element in the HTML tab instead of the native <video> element.
完成后,结果将与开始时的播放器完全相同。 唯一的区别是我们将在HTML选项卡中使用自定义的<video-player>元素,而不是本机的<video>元素。
To set up a basic CanJS application (or widget), you define a custom element in JavaScript and use the custom element in your page’s HTML.
要设置基本的CanJS应用程序(或小部件),请在JavaScript中定义一个自定义元素,然后在页面的HTML使用该自定义元素。
To define a custom element, extend Component with a tag that matches the name of your custom element. For example:
要定义自定义元素,请使用与您的自定义元素名称匹配的标签扩展Component 。 例如:
Component.extend({ tag: "video-player" })Then you can use this tag in your HTML page:
然后,您可以在HTML页面中使用此标记:
<video-player></video-player>But this doesn’t do anything ... yet. Components add their own HTML through their view property:
但这还没有做任何事情...。 组件通过其view属性添加自己HTML:
Component.extend({ tag: "video-player", view: `<h2>I am a player!</h2>` });A component’s view is rendered with its ViewModel. For example, we can make a <video> display "http://bit.ly/can-tom-n-jerry" by defining a src property on the ViewModel and using it in the view like:
组件的视图使用其ViewModel呈现。 例如,我们可以通过在ViewModel上定义src属性并在如下视图中使用它来使<video>显示"http://bit.ly/can-tom-n-jerry" :
Component.extend({ tag: "video-player", view: ` <video> <source src="{{src}}"/> </video> `, ViewModel: { src: {type: "string", default: "http://bit.ly/can-tom-n-jerry"} } });But we want the <video-player> to take a src attribute value itself and use that for the <source>’s src. For example, if we wanted the video to play "http://dl3.webmfiles.org/big-buck-bunny_trailer.webm" instead of "http://bit.ly/can-tom-n-jerry", we would:
但是我们希望<video-player>本身采用src属性值,并将其用于<source>的src 。 例如,如果我们希望视频播放"http://dl3.webmfiles.org/big-buck-bunny_trailer.webm"而不是"http://bit.ly/can-tom-n-jerry" ,我们将:
Update <video-player> to pass "http://dl3.webmfiles.org/big-buck-bunny_trailer.webm" with toChild:raw:
更新<video-player>以通过toChild:raw传递"http://dl3.webmfiles.org/big-buck-bunny_trailer.webm" :
<video-player src:raw="http://dl3.webmfiles.org/big-buck-bunny_trailer.webm"/>Update the ViewModel to define a src property like:
更新ViewModel以定义src属性,例如:
Component.extend({ tag: "video-player", view: ` <video> <source src="{{src}}"/> {{!👀}} </video> `, ViewModel: { src: "string" } });Finally, to have a <video> element show the native controls, add a controls attribute like:
最后,要使<video>元素显示本机控件,请添加如下controls属性:
<video controls>Update the JS tab to:
将JS标签更新为:
import {Component} from "//unpkg.com/can@5/core.mjs"; Component.extend({ //👀 tag: "video-player", //👀 view: ` {{!👀}} <video controls> {{!👀}} <source src="{{src}}"/> {{!👀}} </video> {{!👀}} `, //👀 ViewModel: { //👀 src: "string", //👀 } //👀 }); //👀Update the HTML to:
将HTML更新为:
<video-player src:raw="http://bit.ly/can-tom-n-jerry"></video-player> <!--👀-->When the video is played or paused using the native controls, we want to change the content of a <button> to say “Play” or “Pause”.
使用本机控件播放或暂停视频时,我们希望将<button>的内容更改为“播放”或“暂停” 。
When the video is played, the button should say “Pause”. When the video is paused, the button should say “Play”.
播放视频时,按钮应显示“暂停” 。 视频暂停时,按钮应显示“播放” 。
We want the button to be within a <div> after the video element like:
我们希望按钮位于视频元素之后的<div>内,例如:
</video> <div> <button>Play</button> </div>To change the HTML content of the page, use {{#if(expression)}} and {{else}} like:
要更改页面HTML内容,请使用{{#if(expression)}}和{{else}},如下所示:
<button>{{#if(playing)}} Pause {{else}} Play {{/if}}</button>The view responds to values in the ViewModel. To create a boolean value in the ViewModel do:
该视图响应ViewModel中的值。 要在ViewModel中创建boolean值,请执行以下操作:
ViewModel: { // ... playing: "boolean", }Methods can be used to change the ViewModel. The following might create methods that change the playing value:
方法可用于更改ViewModel 。 以下可能会创建更改playing价值的方法:
ViewModel: { // ... play() { this.playing = true; }, pause() { this.playing = false; }, }You can listen to events on the DOM with on:event. For example, the following might listen to a click on a <div> and call doSomething():
您可以使用on:event监听DOM 上的事件 。 例如,以下内容可能会监听<div>的点击并调用doSomething() :
<div on:click="doSomething()"><video> elements have a variety of useful events, including play and pause events that are emitted when the video is played or paused.
<video>元素具有各种有用的事件 ,包括在播放或暂停视频时发出的播放和暂停事件。
Update the JavaScript tab to:
将JavaScript标签更新为:
import {Component} from "//unpkg.com/can@5/core.mjs"; Component.extend({ tag: "video-player", view: ` <video controls on:play="play()" {{!👀}} on:pause="pause()"> {{!👀}} <source src="{{src}}"/> </video> <div> {{!👀}} <button> {{!👀}} {{#if(playing)}} Pause {{else}} Play {{/if}} {{!👀}} </button> {{!👀}} </div> {{!👀}} `, ViewModel: { src: "string", playing: "boolean", //👀 play() { //👀 this.playing = true; //👀 }, //👀 pause() { //👀 this.playing = false; //👀 }, //👀 } });When the play/pause <button> we created in the previous section is clicked, we want to either play or pause the video.
单击上一节中创建的播放/暂停 <button> ,我们要播放或暂停视频。
CanJS prefers to manage the state of your application in ViewModel. The <video> player has state, such as if the video is playing. When the play/pause button is clicked, we want to update the state of the ViewModel and have the ViewModel update the state of the video player as a side effect.
CanJS倾向于在ViewModel中管理应用程序的状态。 <video>播放器具有状态,例如正在playing视频。 单击“ 播放/暂停”按钮时,我们要更新ViewModel的状态,并让ViewModel更新视频播放器的状态作为副作用。
What this means is that instead of something like:
这意味着它不是这样的:
togglePlay() { if ( videoElement.paused ) { videoElement.play() } else { videoElement.pause() } }We update the state like:
我们将状态更新为:
togglePlay() { this.playing = !this.playing; }And listen to when playing changes and update the video element like:
并在playing更改时收听并更新video元素,例如:
viewModel.listenTo("playing", function(event, isPlaying) { if ( isPlaying ) { videoElement.play() } else { videoElement.pause() } })This means that you need to:
这意味着您需要:
Listen to when the <button> is clicked and call a ViewModel method that updates the playing state.
收听单击<button>时的声音,并调用更新playing状态的ViewModel方法。
Listen to when the playing state changes and update the state of the <video> element.
收听playing状态更改时的playing并更新<video>元素的状态。
You already know everything you need to know for step #1. (Have the button call a togglePlay method with on:click="togglePlay()" and make the togglePlay() method toggle the state of the playing property.)
你已经知道你需要知道的步骤#1的一切。 (让按钮使用on:click="togglePlay()"调用togglePlay方法,并使togglePlay()方法切换playing属性的状态。)
For step #2, you need to use the connectedCallback lifecycle hook. This hook gives you access to the component’s element and is a good place to do side-effects. Its use looks like this:
对于步骤2 ,您需要使用connectedCallback生命周期挂钩。 该挂钩使您可以访问组件的元素,并且是处理副作用的好地方。 它的用法如下所示:
ViewModel: { // ... connectedCallback(element) { // perform mischief } }connectedCallback gets called once the component’s element is in the page. You can use listenTo to listen to changes in the ViewModel’s properties and perform side-effects. The following listens to when playing changes:
一旦组件的element在页面中,就会调用connectedCallback 。 您可以使用listenTo来侦听ViewModel的属性中的更改并执行副作用。 playing更改时,以下内容会监听:
ViewModel: { // ... connectedCallback(element) { this.listenTo("playing", function(event, isPlaying) { }) } }Use querySelector to get the <video> element from the <video-player> like:
使用querySelector从<video-player>获取<video>元素,例如:
element.querySelector("video")<video> elements have a .play() and .pause() methods that can start and stop a video.
<video>元素具有可以启动和停止视频的.play()和.pause()方法。
Update the JavaScript tab to:
将JavaScript标签更新为:
import {Component} from "//unpkg.com/can@5/core.mjs"; Component.extend({ tag: "video-player", view: ` <video controls on:play="play()" on:pause="pause()"> <source src="{{src}}"/> </video> <div> <button on:click="togglePlay()"> {{!👀}} {{#if(playing)}} Pause {{else}} Play {{/if}} </button> </div> `, ViewModel: { src: "string", playing: "boolean", play() { this.playing = true; }, pause() { this.playing = false; }, togglePlay() { //👀 this.playing = !this.playing; //👀 }, //👀 connectedCallback(element) { //👀 this.listenTo("playing", function(event, isPlaying) { //👀 if (isPlaying) { //👀 element.querySelector("video").play(); //👀 } else { //👀 element.querySelector("video").pause(); //👀 } //👀 }); //👀 } //👀 } });Show the current time and duration of the video element. The time and duration should be formatted like: mm:SS. They should be presented within two spans like:
显示视频元素的当前时间和持续时间。 时间和持续时间的格式应为: mm:SS 。 它们应在两个范围内显示:
</button> <span>1:22</span> <span>2:45</span>Methods can be used to format values in can-stache. For example, you can uppercase values like this:
方法可用于格式化can-stache中的值。 例如,您可以像这样大写值:
<span>{{upper(value)}}</span>With a method like:
使用类似的方法:
ViewModel: { // ... upper(value) { return value.toString().toUpperCase(); } }The following can be used to format time:
以下内容可用于格式化时间:
formatTime(time) { if (time === null || time === undefined) { return "--"; } const minutes = Math.floor(time / 60); let seconds = Math.floor(time - minutes * 60); if (seconds < 10) { seconds = "0" + seconds; } return minutes + ":" + seconds; }Time is given as a number. Use the following to create a number property on the ViewModel:
时间以数字形式给出。 使用以下命令在ViewModel上创建一个number属性:
ViewModel: { // ... duration: "number", currentTime: "number" }<video> elements emit a loadmetadata event when they know how long the video is. They also emit a timeupdate event when the video’s current play position changes.
<video>元素在知道视频多长时间时会发出loadmetadata事件 。 当视频的当前播放位置更改时,它们还会发出timeupdate事件 。
videoElement.duration reads the duration of a video.videoElement.duration读取视频的时长。 videoElement.currentTime reads the current play position of a video.videoElement.currentTime读取视频的当前播放位置。You can get the element in an stache on:event binding with scope.element like:
您可以使用scope.element像下面这样on:event绑定中获取元素:
<video on:timeupdate="updateTimes(scope.element)"/>Update the JavaScript tab to:
将JavaScript标签更新为:
import {Component} from "//unpkg.com/can@5/core.mjs"; Component.extend({ tag: "video-player", view: ` <video controls on:play="play()" on:pause="pause()" on:timeupdate="updateTimes(scope.element)" {{!👀}} on:loadedmetadata="updateTimes(scope.element)"> {{!👀}} <source src="{{src}}"/> </video> <div> <button on:click="togglePlay()"> {{#if(playing)}} Pause {{else}} Play {{/if}} </button> <span>{{formatTime(currentTime)}}</span> / {{!👀}} <span>{{formatTime(duration)}} </span> {{!👀}} </div> `, ViewModel: { src: "string", playing: "boolean", duration: "number", //👀 currentTime: "number", //👀 updateTimes(videoElement) { //👀 this.currentTime = videoElement.currentTime || 0; //👀 this.duration = videoElement.duration; //👀 }, //👀 formatTime(time) { //👀 if (time === null || time === undefined) { //👀 return "--"; //👀 } //👀 const minutes = Math.floor(time / 60); //👀 let seconds = Math.floor(time - minutes * 60); //👀 if (seconds < 10) { //👀 seconds = "0" + seconds; //👀 } //👀 return minutes + ":" + seconds; //👀 }, //👀 play() { this.playing = true; }, pause() { this.playing = false; }, togglePlay() { this.playing = !this.playing; }, connectedCallback(element) { this.listenTo("playing", function(event, isPlaying) { if (isPlaying) { element.querySelector("video").play(); } else { element.querySelector("video").pause(); } }); } } });Create a <input type="range"/> element that changes its position as the video playing position changes.
创建一个<input type="range"/>元素,该元素随视频播放位置的变化而改变其位置。
The <input type="range"/> element should be after the <button> and before the currentTime span like:
<input type="range"/>元素应位于<button>和currentTime跨度之前,例如:
</button> <input type="range"/> <span>{{formatTime(currentTime)}}</span> /The range input can have an initial value, max value, and step size specified like:
范围输入可以指定一个初始值,最大值和步长,例如:
<input type="range" value="0" max="1" step="any"/>The range will have values from 0 to 1. We will need to translate the currentTime to a number between 0 and 1. We can do this with a computed getter property like:
范围的值从0到1。我们需要将currentTime转换为0到1之间的数字。我们可以使用计算得到的getter属性来完成此操作,例如:
ViewModel: { // ... get percentComplete() { return this.currentTime / this.duration; }, }Use key:from to update a value from a ViewModel property like:
使用key:from从ViewModel属性更新值,例如:
<input value:from="percentComplete"/>Update the JavaScript tab to:
将JavaScript标签更新为:
import {Component} from "//unpkg.com/can@5/core.mjs"; Component.extend({ tag: "video-player", view: ` <video controls on:play="play()" on:pause="pause()" on:timeupdate="updateTimes(scope.element)" on:loadedmetadata="updateTimes(scope.element)"> <source src="{{src}}"/> </video> <div> <button on:click="togglePlay()"> {{#if(playing)}} Pause {{else}} Play {{/if}} </button> <input type="range" value="0" max="1" step="any" {{!👀}} value:from="percentComplete"/> {{!👀}} <span>{{formatTime(currentTime)}}</span> / <span>{{formatTime(duration)}} </span> </div> `, ViewModel: { src: "string", playing: "boolean", duration: "number", currentTime: "number", get percentComplete() { //👀 return this.currentTime / this.duration; //👀 }, //👀 updateTimes(videoElement) { this.currentTime = videoElement.currentTime || 0; this.duration = videoElement.duration; }, formatTime(time) { if (time === null || time === undefined) { return "--"; } const minutes = Math.floor(time / 60); let seconds = Math.floor(time - minutes * 60); if (seconds < 10) { seconds = "0" + seconds; } return minutes + ":" + seconds; }, play() { this.playing = true; }, pause() { this.playing = false; }, togglePlay() { this.playing = !this.playing; }, connectedCallback(element) { this.listenTo("playing", function(event, isPlaying) { if (isPlaying) { element.querySelector("video").play(); } else { element.querySelector("video").pause(); } }); } } });In this section we will:
在本节中,我们将:
Remove the native controls from the video player. We don’t need them anymore! 从视频播放器中删除本机控件。 我们不再需要它们了! Make it so when a user moves the range slider, the video position updates. 当用户移动范围滑块时,视频位置会更新。Similar to when we made the play/pause button play or pause the video, we will want to update the currentTime property and then listen to when currentTime changes and update the <video> element’s currentTime as a side-effect.
类似于使播放/暂停按钮播放或暂停视频时 ,我们将要更新currentTime属性,然后在currentTime更改时进行监听,并更新<video>元素的currentTime作为副作用 。
This time, we need to translate the sliders values between 0 and 1 to currentTime values. We can do this by creating a percentComplete setter that updates currentTime like:
这次,我们需要将0到1之间的滑块值转换为currentTime值。 为此,我们可以创建一个percentComplete 设置器来更新currentTime例如:
ViewModel: { // ... get percentComplete() { return this.currentTime / this.duration; }, set percentComplete(newVal) { this.currentTime = newVal * this.duration; }, // ... }Use key:bind to two-way bind a value to a ViewModel property:
使用key:bind双向绑定一个值到ViewModel属性:
<input value:bind="someViewModelProperty"/>Update the JavaScript tab to:
将JavaScript标签更新为:
import {Component} from "//unpkg.com/can@5/core.mjs"; Component.extend({ tag: "video-player", view: ` <video {{!👀}} on:play="play()" on:pause="pause()" on:timeupdate="updateTimes(scope.element)" on:loadedmetadata="updateTimes(scope.element)"> <source src="{{src}}"/> </video> <div> <button on:click="togglePlay()"> {{#if(playing)}} Pause {{else}} Play {{/if}} </button> <input type="range" value="0" max="1" step="any" value:bind="percentComplete"/> {{!👀}} <span>{{formatTime(currentTime)}}</span> / <span>{{formatTime(duration)}} </span> </div> `, ViewModel: { src: "string", playing: "boolean", duration: "number", currentTime: "number", get percentComplete() { return this.currentTime / this.duration; }, set percentComplete(newVal) { //👀 this.currentTime = newVal * this.duration; //👀 }, //👀 updateTimes(videoElement) { this.currentTime = videoElement.currentTime || 0; this.duration = videoElement.duration; }, formatTime(time) { if (time === null || time === undefined) { return "--"; } const minutes = Math.floor(time / 60); let seconds = Math.floor(time - minutes * 60); if (seconds < 10) { seconds = "0" + seconds; } return minutes + ":" + seconds; }, play() { this.playing = true; }, pause() { this.playing = false; }, togglePlay() { this.playing = !this.playing; }, connectedCallback(element) { this.listenTo("playing", function(event, isPlaying) { if (isPlaying) { element.querySelector("video").play(); } else { element.querySelector("video").pause(); } }); this.listenTo("currentTime", function(event, currentTime) { //👀 const videoElement = element.querySelector("video"); //👀 if (currentTime !== videoElement.currentTime) { //👀 videoElement.currentTime = currentTime; //👀 } //👀 }); //👀 } } });When finished, you should see something like the following JS Bin:
完成后,您应该会看到类似以下JS Bin的内容:
演示地址
翻译自: https://davidwalsh.name/custom-html5-video
自定义视频播放控件样式
相关资源:jQuery自定义视频弹幕插件,网页自适应视频播放器