Vytvořte vlastní ovládací prvky přehrávače videa pomocí CanJS

V této příručce se dozvíte, jak vytvořit vlastní přehrávač videa pomocí <video> prvek a CanJS. Vlastní přehrávač videa:

  • Mějte vlastní tlačítka pro přehrávání a pozastavení.
  • Zobrazit aktuální čas a trvání videa.
  • Mějte <input type="range"> posuvník, kterým lze upravit polohu videa.

Finální přehrávač vypadá takto:

Následující sekce jsou rozděleny do následujících částí:

  • Problém — Popis toho, čeho se sekce snaží dosáhnout.
  • Co potřebujete vědět — Rozhraní API prohlížeče nebo CanJS, která jsou užitečná pro řešení problému.
  • Řešení — Řešení problému.

Nastavení

ZAČNĚTE TENTO VÝUČNÝ NÁVOD PROVEDENÍM NÁSLEDUJÍCÍHO CodePen :

Toto CodePen:

  • Vytvoří <video> prvek, který načte video. Kliknutím pravým tlačítkem a výběrem možnosti „Zobrazit ovládací prvky“ zobrazíte ovládací prvky videa .
  • Načte vlastní knihovnu prvků CanJS:Component.

Problém

V této sekci budeme:

  • Vytvořte vlastní <video-player> prvek, který má src atribut a vytvoří <video> prvek v sobě. Měli bychom být schopni vytvořit video jako:

    <video-player src:raw="http://bit.ly/can-tom-n-jerry">
    </video-player>
  • Vložený <video> prvek by měl mít povoleny nativní ovládací prvky.

Po dokončení bude výsledek vypadat úplně stejně jako přehrávač, když jste začali. Jediný rozdíl je v tom, že budeme používat vlastní <video-player> prvek v HTML namísto nativního <video> prvek.

Co potřebujete vědět

Chcete-li nastavit základní aplikaci CanJS (nebo widget), definujte vlastní prvek v JavaScriptu a použijte vlastní prvek v HTML vaší stránky. .

Chcete-li definovat vlastní prvek, rozšiřte komponentu o značku, která odpovídá názvu vašeho vlastního prvku. Například:

Component.extend({
    tag: "video-player"
})

Pak můžete na své HTML stránce použít tuto značku:

<video-player></video-player>

Ale tohle nic nedělá...zatím. Komponenty přidávají své vlastní HTML prostřednictvím svých prvků v rámci zobrazení jsou nahrazeny zdrojovými prvky v rámci značky komponenty.">viewproperty:

Component.extend({
    tag: "video-player",
    view: `<h2>I am a player!</h2>`
});

Prvky komponenty v pohledu jsou nahrazeny zdrojovými prvky ve značce komponenty.">pohled je vykreslen pomocí ViewModel. Můžeme například vytvořit <video> zobrazit "http://bit.ly/can-tom-n-jerry" definováním src vlastnost na ViewModel a jeho použití v prvcích v pohledu jsou nahrazeny zdrojovými prvky ve značce komponenty.">zobrazit jako:

Component.extend({
    tag: "video-player",
    view: `
        <video>
            <source src="{{src}}"/>
        </video>
    `,
    ViewModel: {
        src: {type: "string", default: "http://bit.ly/can-tom-n-jerry"}
    }
});

Ale my chceme <video-player> vzít src samotnou hodnotu atributu a použijte ji pro <source> src . Pokud bychom například chtěli, aby se video přehrávalo "http://dl3.webmfiles.org/big-buck-bunny_trailer.webm" místo "http://bit.ly/can-tom-n-jerry" , my bychom:

  1. Aktualizujte <video-player> předat "http://dl3.webmfiles.org/big-buck-bunny_trailer.webm" s toChild:raw:
    <video-player src:raw="http://dl3.webmfiles.org/big-buck-bunny_trailer.webm"/>
  2. Aktualizujte ViewModel a definujte src vlastnost jako:
    Component.extend({
        tag: "video-player",
        view: `
            <video>
                <source src="{{src}}"/> {{!👀}}
            </video>
        `,
        ViewModel: {
            src: "string"
        }
    });

A konečně mít <video> element zobrazit nativní ovládací prvky, přidejte controls atribut jako:

<video controls>

Řešení

Aktualizujte JS tab na:

import {Component} from "//unpkg.com/can@5/core.mjs";

Component.extend({                        //👀
    tag: "video-player",                  //👀
    view: `                              {{!👀}}
        <video controls>                 {{!👀}}
            <source src="{{src}}"/>      {{!👀}}
        </video>                         {{!👀}}
    `,                                    //👀
    ViewModel: {                          //👀
        src: "string",                    //👀
    }                                     //👀
});                                       //👀

Aktualizujte HTML komu:

<video-player src:raw="http://bit.ly/can-tom-n-jerry"></video-player>   <!--👀-->

Změnit tlačítko přehrávání/pozastavení během přehrávání a pozastavení videa

Problém

Když se video přehrává nebo pozastavuje pomocí nativních ovládacích prvků, chceme změnit obsah <button> říct „Hrát“ nebo „Pozastavit“ .

Při přehrávání videa by na tlačítku mělo být uvedeno „Pozastavit“ .Když je video pozastaveno, na tlačítku by mělo být uvedeno Přehrát .

Chceme, aby tlačítko bylo uvnitř <div> za prvkem videa jako:

</video>
<div>
    <button>Play</button>
</div>

Co potřebujete vědět

  • Chcete-li změnit obsah HTML stránky, použijte {{#if(expression)}} a {{else}} jako:

    <button>{{#if(playing)}} Pause {{else}} Play {{/if}}</button>
  • Prvky v pohledu jsou nahrazeny zdrojovými prvky ve značce komponenty.">zobrazení odpovídá hodnotám v ViewModel. Chcete-li vytvořit boolean hodnotu v ViewModel do:

    ViewModel: {
        // ...
        playing: "boolean",
    }
  • Ke změně ViewModelu lze použít metody. Následující může vytvořit metody, které změní playing hodnota:

    ViewModel: {
        // ...
        play() {
            this.playing = true;
        },
        pause() {
            this.playing = false;
        },
    }
  • Události na DOM můžete poslouchat pomocí on:event. Následující text může například poslouchat kliknutí na <div> a zavolejte doSomething() :

    <div on:click="doSomething()">

    <video> prvky mají řadu užitečných událostí, včetně událostí přehrávání a pauzy, které jsou vysílány při přehrávání nebo pozastavení videa.

Řešení

Aktualizujte JavaScript tab na:

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;                                  //👀
        },                                                         //👀
    }
});

Zahájení přehrávání nebo pozastavení videa kliknutím na tlačítko přehrávání/pozastavení

Problém

Když přehrát/pozastavit <button> jsme vytvořili v předchozí sekci, chceme video přehrát nebo pozastavit.

Co potřebujete vědět

CanJS preferuje správu stavu vaší aplikace v ViewModel. <video> přehrávač má stav, například pokud je video playing . Když přehrát/pozastavit po kliknutí na tlačítko, chceme aktualizovat stav ViewModelu a jako vedlejší efekt nechat ViewModel aktualizovat stav přehrávače videa.

To znamená, že místo něčeho jako:

togglePlay() {
    if ( videoElement.paused ) {
        videoElement.play()
    } else {
        videoElement.pause()
    }
}

Stav aktualizujeme jako:

togglePlay() {
    this.playing = !this.playing;
}

A poslouchat, když playing změny a aktualizace video prvek jako:

viewModel.listenTo("playing", function(event, isPlaying) {
    if ( isPlaying ) {
        videoElement.play()
    } else {
        videoElement.pause()
    }
})

To znamená, že musíte:

  1. Poslouchejte, když <button> klikne a zavolá metodu ViewModel, která aktualizuje playing stavu.
  2. Poslouchejte, když playing změny stavu a aktualizaci stavu <video> prvek.

Už víte vše, co potřebujete vědět pro krok #1 . (Nechte tlačítko zavolat togglePlay metoda s on:click="togglePlay()" a vytvořte togglePlay() metoda přepíná stav playing majetek.)

Pro krok #2 , musíte použít háček životního cyklu connectCallback. Tento háček vám poskytuje přístup k prvku součásti a je dobrým místem pro provádění vedlejších účinků. Jeho použití vypadá takto:

ViewModel: {
    // ...
    connectedCallback(element) {
        // perform mischief
    }
}

connectedCallback je volána, jakmile komponenta obsahuje element je na stránce. Pomocí funkce ListenTo můžete poslouchat změny ve vlastnostech ViewModel a provádět vedlejší účinky. Následující poslouchá, když playing změny:

ViewModel: {
    // ...
    connectedCallback(element) {
        this.listenTo("playing", function(event, isPlaying) {

        })
    }
}

Použijte querySelector získat <video> prvek z <video-player> jako:

element.querySelector("video")

<video> prvky mají metody .play() a .pause(), které mohou spustit a zastavit video.

Řešení

Aktualizujte JavaScript tab na:

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();          //👀
                }                                                    //👀
            });                                                      //👀
        }                                                            //👀
    }
});

Zobrazit aktuální čas a trvání

Problém

Zobrazit aktuální čas a trvání prvku videa. Čas a trvání by měly být ve formátu:mm:SS . Měly by být uvedeny ve dvou rozmezích, jako:

</button>
<span>1:22</span>
<span>2:45</span>

Co potřebujete vědět

  1. Metody lze použít k formátování hodnot v can-stache. Například můžete hodnoty velkými písmeny takto:

    <span>{{upper(value)}}</span>

    Metodou jako:

    ViewModel: {
        // ...
        upper(value) {
            return value.toString().toUpperCase();
        }
    }

    K formátování času lze použít následující:

    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;
    }
  2. Čas se udává jako číslo. K vytvoření vlastnosti čísla na ViewModel použijte následující:

    ViewModel: {
        // ...
        duration: "number",
        currentTime: "number"
    }
  3. <video> prvky vydávají událost loadmetadata, když vědí, jak dlouhé je video. Vydávají také událost timeupdate, když se změní aktuální pozice přehrávání videa.

    • videoElement.duration přečte dobu trvání videa.
    • videoElement.currentTime přečte aktuální pozici přehrávání videa.
  4. Prvek můžete získat ve stache on:event vazba s rozsahem.prvkem jako:

    <video on:timeupdate="updateTimes(scope.element)"/>

Řešení

Aktualizujte JavaScript tab na:

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();
                }
            });
        }
    }
});

Nastavit posuvník pro zobrazení polohy v aktuálním čase

Problém

Vytvořte <input type="range"/> prvek, který mění svou pozici se změnou pozice přehrávání videa.

<input type="range"/> prvek by měl být za <button> a před currentTime rozsah jako:

</button>
<input type="range"/>
<span>{{formatTime(currentTime)}}</span> /

Co potřebujete vědět

  • Vstup rozsahu může mít počáteční hodnotu, maximální hodnotu a velikost kroku zadané jako:

    <input type="range" value="0" max="1" step="any"/>
  • Rozsah bude mít hodnoty od 0 do 1. Budeme muset přeložit aktuální čas na číslo mezi 0 a 1. Můžeme to udělat pomocí vypočítané vlastnosti getter, jako je:

    ViewModel: {
        // ...
        get percentComplete() {
            return this.currentTime / this.duration;
        },
    }
  • Použijte key:from k aktualizaci hodnoty z vlastnosti ViewModel, jako je:

    <input value:from="percentComplete"/>

Řešení

Aktualizujte JavaScript tab na:

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();
                }
            });
        }
    }
});

Nechte posunutím rozsahu aktualizovat aktuální čas

Problém

V této sekci budeme:

  • Odstraňte nativní ovládací prvky z přehrávače videa. Už je nepotřebujeme!
  • Zajistěte, aby se poloha videa aktualizovala, když uživatel přesune posuvník rozsahu.

Co potřebujete vědět

Podobně jako když jsme přehráli nebo pozastavili video pomocí tlačítka pro přehrávání/pozastavení, budeme chtít aktualizovat currentTime vlastnost a pak poslouchat, když currentTime změny a aktualizace <video> currentTime prvku jako vedlejší účinek .

Tentokrát musíme převést hodnoty posuvníků mezi 0 a 1 na currentTime hodnoty. Můžeme to udělat vytvořením percentComplete setter, který aktualizuje currentTime jako:

ViewModel: {
    // ...
    get percentComplete() {
        return this.currentTime / this.duration;
    },
    set percentComplete(newVal) {
        this.currentTime = newVal * this.duration;
    },
    // ...
}

Použijte key:bind pro obousměrnou vazbu hodnoty na vlastnost ViewModel:

<input value:bind="someViewModelProperty"/>

Řešení

Aktualizujte JavaScript tab na:

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;              //👀
                }                                                        //👀
            });                                                          //👀
        }
    }
});

Výsledek

Po dokončení byste měli vidět něco jako následující JS Bin: