HTML がキャンバスを使用して弾幕機能を実装する方法

墨辰丷
リリース: 2018-06-04 15:00:47
オリジナル
1966 人が閲覧しました

この記事では、HTML での弾幕機能の実装方法を中心に紹介します。興味のある方は参考にしていただければ幸いです。

はじめに

最近、大きな仕事をしていたときに弾幕プレイヤーを作る必要がありました。他の人のソースコードを借りて自分で再実装したデモは次のとおりです

主な機能は

弾幕の色、速度、種類を設定
弾幕を表示

既知の欠陥:

全画面表示できない

キャンバスがアダプティブではない

カスタムプレーヤーコントロールがない
再生時間に応じて対応する弾幕が表示されない
弾幕をホバーさせることができない
既知の不具合は将来的に解決される予定 改善。インターネット上で見つかる弾幕プレイヤーのソース コードは、通常、ローリング弾幕のみを作成し、静的な弾幕を作成しません。ここでは、特別に静的な弾幕の実装を追加しました。

キャンバスはテキストとテキストスクロール効果を描画します

プレーヤー全体の核となるのは、キャンバス内のテキストを描画し、テキストスクロールをアニメーション化することです。これは自分でのみ実現できます。常に画面をクリアしてからテキストを書き直すという考え方です。画面のクリアと書き換えの頻度が 24fps に達すると、アニメーションがスムーズになります。

まず、HTMLファイルにvideoビデオタグとキャンバスタグを追加します

<p id="barrageplayer">
    <canvas id="cv_video" width="900px" height="450px"></canvas>
    <video id="v_video" src="test.MP4" controls type="video/mp4"></video>
</p>
ログイン後にコピー

キャンバスタグの位置スタイルをposition:absoluteに設定すると、ビデオとキャンバスが重なって弾幕のように見えますプレイヤー。次に、弾幕関連のコンテンツをキャンバスに追加します。まず、キャンバスの関連情報を取得し、

var c=document.getElementById("cv_video");
//获取画布大小
var c_height=c.height;
var c_width=c.width;
//获取画布
ctx=c.getContext("2d");
//设置字体样式
ctx.font="25px DengXian";
画布信息已经获取和设置,巧妇难为无米之炊,接着我们就要构造弹幕对象,使用的构造模式是动态原型模式
//弹幕对象
function Barrage(content,color,type,speed){
    this.content=content;
    this.color=color;
    this.type=type;
    this.speed=speed;
    if(this.type=="default"){
        this.height=parseInt(Math.random()*c_height)+10;
    }else if (this.type=="static top"){
        this.height=parseInt((c_height/2)-Math.random()*c_height/2)+10;
    }else if (this.type=="static bottom"){
        this.height=parseInt((c_height/2)+Math.random()*c_height/2)+10;
    }
    if(typeof this.move!="function"){
        Barrage.prototype.move=function(){
            if(this.type=="default"){
                this.left=this.left-this.speed;
            }
        }
    }
}
ログイン後にコピー

によって構築された弾幕オブジェクトは、コンテンツを含むさまざまなパラメータを初期化します。 move() メソッドは、起動されるたびに 1 ピクセルの単位速度で左にスクロールします。

弾幕オブジェクトの構築が完了したら、テーマとアニメーション制作を入力し、コードを直接入力します

//循环擦写画布实现动画效果
setInterval(function(){
    ctx.clearRect(0,0,c_width,c_height);
    ctx.save();
    for(var i=0;i<msgs.length;i++){
        if(msgs[i]!=null){
            if(msgs[i].type=="default"){
                handleDefault(msgs[i]);
            }else{
                handleStatic(msgs[i]);
           }
        }
    }
},20)
ログイン後にコピー

20msごとに消去を実行し、ctx.clearRect(0,0,c_width,c_height);を変換します。画像全体 現在のキャンバスをクリアし、ctx.save() を使用して現在のキャンバスを保存し、弾幕リストを走査します (msgs は弾幕リストです。弾幕が送信されるたびに、弾幕インスタンスがリストに追加されます)。弾幕のデフォルト スタイルと静的スタイルの弾幕は別々に処理されます。デフォルトスタイルの弾幕の場合は以下のメソッドで処理されます

//处理默认弹幕样式
function handleDefault(barrage){
    if(barrage.left==undefined||barrage.left==null){
        barrage.left=c.width;
    }else{
         if(barrage.left<-200){
            barrage=null;
        }else{
            barrage.move()
            ctx.fillStyle=barrage.color;
            ctx.fillText(barrage.content,barrage.left,barrage.height)
            ctx.restore();
        }
    }  
}
ログイン後にコピー

まず、弾幕インスタンスにleft属性が設定されていない場合は、キャンバスの幅を与えます。弾幕インスタンスがキャンバスから出た場合、メモリーを節約するために null を設定します。そうでない場合は、弾幕インスタンスの move() メソッドを呼び出して left 属性の値を変更し、テキストの色を設定して、新しいテキストを書き込みます。最初のレベルを復元し、キャンバスを復元します。これで 1 フレームのアニメーションが完成します。

静的弾幕の実装方法は以下の通りです

//处理静止弹幕样式
function handleStatic(barrage){
    ctx.moveTo(c_width/2,barrage.height);
    ctx.textAlign="center";
    ctx.fillStyle=barrage.color;
    ctx.fillText(barrage.content,c_width/2,barrage.height);
    if(barrage.left==undefined||barrage.left==null){
        barrage.left=c.width;
    }else{
        if(barrage.left<-200){
            ctx.fillText("",c_width/2,barrage.height);                
            barrage=null;
            //ctx.restore();
            ctx.clearRect(0,0,c_width,c_height);        
        }else{
            barrage.left=barrage.left-6;
        }
    }
}
ログイン後にコピー

まず、キャンバスの基点をキャンバスの中心に移動します。このとき、新しいキャンバスが生成されることに注意してください。元のキャンバスの () このメソッドはこのキャンバスには適用されなくなりました。次に、テキストの配置を中央揃えに設定し、テキストのスタイルを設定して、テキストを塗りつぶします。静止弾幕なので緩和する必要はありませんが、定期的に静止弾幕も消えるフラグを立てる必要があります。ここで追加の属性を占有しないようにするために、 left 属性をフラグとして直接使用します。 left 属性もデクリメントしますが、 left がしきい値に達した場合、 ctx.clearRect() メソッドを使用します。弾幕をクリアします。このようにして、静的弾幕の処理が実現される。

色やスタイルの設定に一定の基礎を持っている人は、ここでは詳しく説明しませんが、実行可能なコード部分を見て理解するだけで十分です。

概要

このプロジェクトは主にテキスト描画とテキストイージングアニメーションにキャンバスを使用します。使用される主なメソッドは

canvasDom.getContext()
canvas.save()/canvas.restore()
canvas.clearRect()
canvas.moveTo()
ログイン後にコピー

です。save()とrestoreについては豊富な経験があることがわかりました。 ()は理解不能ですが、キャンバスの状態を変更すると、現在のキャンバスは元のキャンバスではなくなるので、キャンバスの状態を変更する前に、キャンバスの状態を保存して、キャンバスの状態を切り替えて作業を終了します。 . 、元のキャンバスの状態に戻って作業を続けます。たとえば、静的な弾幕に対処してキャンバスの基点を変更すると、元のキャンバスのクリア方法は現在のキャンバスに適用されなくなり、現在のキャンバスでは別のクリア方法のみを使用できます。その後、元のキャンバスに戻ります。

実行可能なコード

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<style type="text/css">
    .pickp{
        width: 30px;
        height: 30px;
        cursor: pointer;
        border: 2px solid gray;
        display: inline-block;
    }
    #white{
        background: white;
    }
    #red{
        background:#ff6666;
    }
    #yellow{
        background:#ffff00;
    }
    #blue{
        background:#333399;
    }
    #green{
        background:#339933;
    }
    #cv_video{
        position: absolute;
        z-index: 1;
    }
    #barrageplayer{
        position: relative;
        display: block;
        width: 900px;
        height: 500px;
    }
    #v_video{
        position: absolute;
        width: 100%;
        height: 100%;
        z-index: 0;
    }
</style>
<body>
    <p id="barrageplayer">
        <canvas id="cv_video" width="900px" height="450px"></canvas>
        <video id="v_video" src="test.MP4" controls type="video/mp4"></video>
    </p>
    <p id="barrageinput">
        <p>
            <input type="text" id="smsg" placeholder="请输入弹幕内容"/>
            <button id="send"> 发送</button>
        </p>
        <p id="colorpick">
            <p class="pickp" id="white"></p>
            <p class="pickp" id="red"></p>
            <p class="pickp" id="yellow"></p>
            <p class="pickp" id="blue"></p>
            <p class="pickp" id="green"></p>
        </p>
        <p id="typepick">
            <input type="radio" name="type" value="default">默认
            <input type="radio" name="type" value="static top">静止顶部 
            <input type="radio" name="type" value="static bottom">静止底部
        </p>
        <p id="speedpick">
            <input type="radio" name="speed" value="1">1X
            <input type="radio" name="speed" value="2">2X
            <input type="radio" name="speed" value="3">3X
        </p>
        <p id="stylepick"></p>
    </p>
    <script>
        var c=document.getElementById("cv_video");
        var typeDom=document.getElementsByName("type");
        var speedDom=document.getElementsByName("speed");
        var colorpick=document.getElementById("colorpick");
        var smsg=document.getElementById("smsg");
        var color="#white";
        var speed=1;
        var type="default";
        var msgs=[];
        //获取画布大小
        var c_height=c.height;
        var c_width=c.width;
        //获取画布
        ctx=c.getContext("2d");
        ctx.font="25px DengXian";
        //处理颜色选择
        colorpick.addEventListener(&#39;click&#39;,function(event){
            switch(event.target.id){
                case "white":
                    color="white";
                    break;
                case "red":
                    color="#ff6666";
                    break;
                case "yellow":
                    color="#ffff00";
                    break;
                case "green":
                    color="#339933";
                    break;
                case "blue":
                    color="#333399";
                    break;
            }
        })
        //处理发送弹幕
        document.getElementById("send").onclick=function(){
            var text=smsg.value;
            for(var i=0;i<typeDom.length;i++){
                if(typeDom[i].checked){
                    type=typeDom[i].value;
                    break;
                }
            }
            for(var i=0;i<speedDom.length;i++){
                if(speedDom[i].checked){
                    speed=2*parseInt(speedDom[i].value);
                    break;
                }
            }
            var tempBarrage=new Barrage(text,color,type,speed);
            msgs.push(tempBarrage);
        }
        //
        //弹幕功能部分代码
        //
        //弹幕对象
        function Barrage(content,color,type,speed){
            this.content=content;
            this.color=color;
            this.type=type;
            this.speed=speed;
            if(this.type=="default"){
                this.height=parseInt(Math.random()*c_height)+10;
            }else if (this.type=="static top"){
                this.height=parseInt((c_height/2)-Math.random()*c_height/2)+10;
            }else if (this.type=="static bottom"){
                this.height=parseInt((c_height/2)+Math.random()*c_height/2)+10;
            }
            if(typeof this.move!="function"){
                Barrage.prototype.move=function(){
                    if(this.type=="default"){
                        this.left=this.left-this.speed;
                    }
                }
            }
        }
        //循环擦写画布实现动画效果
        setInterval(function(){
            ctx.clearRect(0,0,c_width,c_height);
            ctx.save();
            for(var i=0;i<msgs.length;i++){
                if(msgs[i]!=null){
                    if(msgs[i].type=="default"){
                        handleDefault(msgs[i]);
                    }else{
                        handleStatic(msgs[i]);
                    }
                }
            }
        },20)
    //处理默认弹幕样式
    function handleDefault(barrage){
        if(barrage.left==undefined||barrage.left==null){
            barrage.left=c.width;
        }else{
            if(barrage.left<-200){
                barrage=null;
            }else{
                barrage.move()
                ctx.fillStyle=barrage.color;
                ctx.fillText(barrage.content,barrage.left,barrage.height)
                ctx.restore();
            }
        }  
    }
    //处理静止弹幕样式
    function handleStatic(barrage){
        ctx.moveTo(c_width/2,barrage.height);
        ctx.textAlign="center";
        ctx.fillStyle=barrage.color;
        ctx.fillText(barrage.content,c_width/2,barrage.height);
        if(barrage.left==undefined||barrage.left==null){
            barrage.left=c.width;
        }else{
            if(barrage.left<-200){
                ctx.fillText("",c_width/2,barrage.height);                
                barrage=null;
                //ctx.restore();
                ctx.clearRect(0,0,c_width,c_height);        
            }else{
                barrage.left=barrage.left-6;
            }
        }
    }
    </script>
</body>
</html>
ログイン後にコピー

概要: 上記がこの記事の全内容です。皆様の学習に役立つことを願っています。

関連するおすすめ:

H5+Canvasユースケース詳細説明

Vue+canvasを使ってモバイル手書きパッド機能を実装する方法

js+canvasスライディングパズル認証コード機能を実装

以上がHTML がキャンバスを使用して弾幕機能を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!