Rumah hujung hadapan web html tutorial 怎样用HTML实现滚动弹幕功能

怎样用HTML实现滚动弹幕功能

Jan 17, 2018 am 09:55 AM
html Fungsi


这次给大家带来怎样用HTML实现滚动弹幕功能,HTML实现滚动弹幕功能的注意事项有哪些,下面就是实战案例,一起来看一下。

主要的功能有;发送弹幕,设置弹幕的颜色,速度和类型,显示弹幕

已知缺陷:不能全屏

canvas没有做自适应
没有自定义播放器控件
没有根据播放时间显示相应的弹幕
弹幕不能实现悬停
已知的缺陷以后会进行改进。网上能找到的弹幕播放器的源码一般只做了滚动的弹幕而没有做静止的弹幕,这里我特意加上了静止弹幕的实现。

Canvas绘制文字以及文字滚动效果

 整个播放器的核心就是绘制文字以及做文字滚动的动画,canvas中对于文字并没有很好的动画支持,只能通过自己实现,实现的思路就是不断的清屏然后重写文字,当清屏重写的频率达到24fps的时候就是流畅的动画了。

先在HTML文件中添加视频video标签和画布canvas标签

<div id="barrageplayer">
    <canvas id="cv_video" width="900px" height="450px"></canvas>
    <video id="v_video" src="test.MP4" controls type="video/mp4"></video>
</div>
Salin selepas log masuk

把canvas标签的位置样式设置为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;
            }
        }
    }
}
Salin selepas log masuk

构造的弹幕对象初始化了各种参数,包括内容,颜色,运动类型和速度,定义了move()方法来控制弹幕的缓动,每出发一次move()方法向左滚动一个单位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)
Salin selepas log masuk

每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();
        }
    } 
}
Salin selepas log masuk

首先如果弹幕实例没有设置left属性则将画布的宽度赋予它,如果弹幕实例已经退出画布则将其置null以节省内存,否则的话就调用弹幕实例的move()方法改变left属性的值,然后设置文字的颜色,一级写入新的文字,恢复画布。这样就完成了一帧动画。

对于静止弹幕的实现方法如下

//处理静止弹幕样式
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;
        }
    }
}
Salin selepas log masuk

首先将画布的基点移动到画布的中心,需要注意的是这时候相对与生成了一张新的画布,原来画布的clearRect()方法已经不适用与这张画布了。然后再设置文字对齐为居中对齐,设置文字样式,填充文字。因为弹幕是静止的所以不需要进行缓动,但是静止弹幕也是会消失的,需要设置一个标志位来使他定时消失。在这里为了不占用额外的属性,我们直接使用left属性作为标志位,同样进行left属性的递减,但不把递减反映到画布中,当left达到阈值,则使用ctx.clearRect()方法将弹幕清除。这样就实现了静止弹幕的处理。

其他关于颜色,样式的设置有一定基础的人应该是很容易掌握的在这里就不多介绍了,自己看可运行代码部分理解一下就好。

总结

这个项目主要是使用了canvas进行文字绘制以及实现文字的缓动动画,主要用到的方法有

canvasDom.getContext()
canvas.save()/canvas.restore()
canvas.clearRect()
canvas.moveTo()
Salin selepas log masuk


原来我对与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">
    .pickdiv{
        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>
    <div id="barrageplayer">
        <canvas id="cv_video" width="900px" height="450px"></canvas>
        <video id="v_video" src="test.MP4" controls type="video/mp4"></video>
    </div>
    <div id="barrageinput">
        <div>
            <input type="text" id="smsg" placeholder="请输入弹幕内容"/>
            <button id="send"> 发送</button>
        </div>
        <div id="colorpick">
            <div class="pickdiv" id="white"></div>
            <div class="pickdiv" id="red"></div>
            <div class="pickdiv" id="yellow"></div>
            <div class="pickdiv" id="blue"></div>
            <div class="pickdiv" id="green"></div>
        </div>
        <div 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">静止底部
        </div>
        <div 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
        </div>
        <div id="stylepick"></div>
    </div>
    <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>
Salin selepas log masuk

相信看了这些案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

相关阅读:

怎样在HTML网页中插入视频

怎样做出HTML文本框只读效果

如何使用HTML+CSS做出鼠标划过就可以显示二级菜单栏

Atas ialah kandungan terperinci 怎样用HTML实现滚动弹幕功能. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Sempadan Jadual dalam HTML Sempadan Jadual dalam HTML Sep 04, 2024 pm 04:49 PM

Panduan untuk Sempadan Jadual dalam HTML. Di sini kita membincangkan pelbagai cara untuk menentukan sempadan jadual dengan contoh Sempadan Jadual dalam HTML.

Jadual Bersarang dalam HTML Jadual Bersarang dalam HTML Sep 04, 2024 pm 04:49 PM

Ini ialah panduan untuk Nested Table dalam HTML. Di sini kita membincangkan cara membuat jadual dalam jadual bersama-sama dengan contoh masing-masing.

HTML jidar-kiri HTML jidar-kiri Sep 04, 2024 pm 04:48 PM

Panduan untuk HTML margin-kiri. Di sini kita membincangkan gambaran keseluruhan ringkas tentang HTML margin-left dan Contoh-contohnya bersama-sama dengan Pelaksanaan Kodnya.

Susun Atur Jadual HTML Susun Atur Jadual HTML Sep 04, 2024 pm 04:54 PM

Panduan untuk Susun Atur Jadual HTML. Di sini kita membincangkan Nilai Susun Atur Jadual HTML bersama-sama dengan contoh dan output n perincian.

Pemegang Tempat Input HTML Pemegang Tempat Input HTML Sep 04, 2024 pm 04:54 PM

Panduan untuk Pemegang Tempat Input HTML. Di sini kita membincangkan Contoh Pemegang Tempat Input HTML bersama-sama dengan kod dan output.

Memindahkan Teks dalam HTML Memindahkan Teks dalam HTML Sep 04, 2024 pm 04:45 PM

Panduan untuk Memindahkan Teks dalam HTML. Di sini kita membincangkan pengenalan, cara teg marquee berfungsi dengan sintaks dan contoh untuk dilaksanakan.

Senarai Tertib HTML Senarai Tertib HTML Sep 04, 2024 pm 04:43 PM

Panduan kepada Senarai Tertib HTML. Di sini kami juga membincangkan pengenalan senarai dan jenis Tertib HTML bersama-sama dengan contoh mereka masing-masing

Butang onclick HTML Butang onclick HTML Sep 04, 2024 pm 04:49 PM

Panduan untuk Butang onclick HTML. Di sini kita membincangkan pengenalan, kerja, contoh dan onclick Event masing-masing dalam pelbagai acara.

See all articles