JS实现视频弹幕效果,不需下载资源直接用

弹幕需要用到canvas 标签

<canvas id="canvas" width="640" height="360" style="background:#333;"></canvas>

<input type="text" id="dminput" placeholder="输入弹幕内容">
<button id="btn_dm">发送弹幕</button>
let cvs = document.getElementById('canvas');
let ctx = cvs.getContext('2d');
// 声明dmlist, 保存所有的弹幕信息
let dmlist = [];

// 为弹幕按钮绑定事件,发送弹幕内容
    btn_dm = document.getElementById('btn_dm');
    btn_dm.addEventListener('click', ()=>{
      let val = document.getElementById('dminput').value;
      // 构造弹幕对象,并存储到dmlist中
      dmlist.push({
        text: val, x: 600, 
        y: (Math.floor(Math.random()*12)+1)*30
      })
      console.log(dmlist);
    })


 function draw(){
      //把canvas上的所有像素点都给抹掉
      ctx.clearRect(0, 0, 640, 360);
      dmlist.forEach(item=>{ //item: {text:'343', x:0, y:0}
        item.x--;
        // 将当前item对象输出到canvas上
        ctx.font = '22px 微软雅黑';
        ctx.fillStyle = "white";
        ctx.fillText(item.text, item.x, item.y);
      })
      window.requestAnimationFrame(draw);
    }
window.requestAnimationFrame(draw);


 这是一个vue 可本地播放MP4视频和播放webRTC推流地址的播放器,可以发送弹幕的demo
使用的是srs的脚本

http://10.2.28.226:8080/players/srs_player.html?autostart=true&app=live&stream=livestream.flv&server=10.2.28.226&port=8080&vhost=10.2.28.226&schema=http

srs官网的GitHub上有相关的依赖脚本 

<template>
    <div>
        <el-card class="box-card">
            <div>
                <div style="padding-bottom: 20px;">
                    <el-input style="width: 360px;" v-model="input" placeholder="输入HTTP-FLV/HLS地址后播放视频"></el-input>
                    <el-button style="margin-left: 10px;" @click="startPlay" type="primary">播放FLV地址</el-button>
                    <input ref="Btn" id="fileurl" type="file" @change="onInputFileChange"/>
                    <el-button style="margin-left: 10px;" type="primary" @click="InputCkick">播放本地文件</el-button>
                </div>
                <div  @mouseenter="handleEnter" @mouseleave="handleLeave" style="position: relative;display: inline-flex;background: rgb(51, 51, 51);">
                    <!-- loop muted controller controls autoplay -->
                    <video @timeupdate="gettimeupdate" ref="reference" style="
                        width: 1200px;
                        min-width: 600px;"></video>
                        <div v-if="isPlay" class="centerIcon"  @click="getisPlay">
                            <i class="iconfont icon-bofangqi-bofang" style="font-size: 120px;" ></i>
                        </div>
                        <div style="position: absolute;left:0;right:0;bottom: -14px;padding: 8px 10px;background: #000;color: #fff;">
                            <el-slider :format-tooltip="formatDuring" @change="getMenuLong" :max="maxVal" style="width: 100%;position: absolute;top: 0;" v-model="time"></el-slider>
                            <div v-if="active" style="display: flex;justify-content: space-between;">
                                <div>
                                    <i @click="getisPlay" :class="isPlay ? 'icon-bofangqi-bofang':'icon-iconstop'" class="iconfont leftIcon"></i>
                                    <i class="iconfont icon-zuihouyiyemoyexiayishou leftIcon"></i>
                                    <span class="leftText">{{qi|formatDuring}} / {{shi|formatDuring}}</span>
                                </div>
                                <div>
                                    <i class="iconfont icon-danmuguanbi rightIcon"></i>
                                    <i @mouseenter="handleMenu"  @click="getvolume" class="iconfont icon-shengyin_shiti rightIcon"></i>
                                    <i @click="Fullscreen" class="iconfont icon-24gf-fullScreenEnter rightIcon"></i>
                                    <div @mouseleave="MenuLeave" v-if="Menu" class="topBottom" style="position: absolute;
                                        bottom: 46px;
                                        right: 70px;
                                        height: 120px;">
                                        <el-slider
                                            v-model="Menuvalue"
                                            vertical
                                            width="2px"
                                            :min="0"
                                            :max="10"
                                            @change="getMenuvalue"
                                            height="120px">
                                        </el-slider>
                                    </div>
                                </div>
                            </div>
                        </div>
                    <canvas width="1200" height="560" id="canvas" @click="getisPlay" style="background:none"></canvas>
                </div>
                <div style="padding: 20px 0;">
                    <el-input style="width: 360px;margin-right: 10px;" v-model="valInput" placeholder="请输入弹幕内容"></el-input>
                    <el-button @click="_btnDm" type="primary">发送弹幕</el-button>
                </div>
            </div>
        </el-card>
    </div>
</template>
<script>
export default {
    data() {
        return {
            input:'',
            qi:0,
            shi:0,
            isPlay:true,
            time:0,
            sdk:null,
            active:true,
            valInput:'',
            dmlist:[],
            Menuvalue:10,
            Menu:false,
            maxVal:0,
            ctx:null
        }
    },
    filters:{
        formatDuring(mss){
            var arr = mss*1000
            if(arr === 0) return '00:00:00'
            var days = parseInt(arr / (1000 * 60 * 60 * 24));
            var hours = parseInt((arr % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
            var minutes = parseInt((arr % (1000 * 60 * 60)) / (1000 * 60));
            var seconds = ((arr % (1000 * 60)) / 1000).toFixed(0);
            // days + ":" + .toFixed(0)
            var str = (hours<9 ? `0${hours}`:hours) + ":" + (minutes<9 ? `0${minutes}`:minutes) + ":"+ (seconds<9 ? `0${seconds}`:seconds)
            return str;
        },
    },
    watch:{
        Menuvalue(){
            this.$refs.reference.volume = this.Menuvalue / 10
        }
    },
    mounted() {
        this.ctx = document.getElementById('canvas').getContext('2d')
        window.requestAnimationFrame(this.draw);
    },
    methods: {
        startPlay() {
            if (this.sdk) {
                this.sdk.close();
            }
            this.sdk = new SrsRtcPlayerAsync();
            var thas = this
            this.sdk.play(this.input).then(function(session){
                thas.$refs.reference.srcObject = thas.sdk.stream;
            }).catch(function (reason) {
                thas.sdk.close();
                console.error(reason);
            });
        },
        handleEnter(){
            this.active = true
        },
        handleLeave(){
            this.active = false
        },
        onInputFileChange(file) {
            this.$refs.reference.src = URL.createObjectURL(file.target.files[0]);
            this.isPlay=false
            this.$refs.reference.play()
            this.$refs.reference.volume = this.Menuvalue / 10
            this.$refs.reference.addEventListener('loadedmetadata',()=>{
                let dt=this.$refs.reference.duration;
                this.shi=dt
                this.maxVal = dt
            })
        },
        InputCkick(){
            this.$refs.Btn.click()
        },
        getisPlay(){
            if(this.$refs.reference.paused){
                this.$refs.reference.play()
                this.isPlay = false
            }else{
                this.$refs.reference.pause()
                this.isPlay = true
            }
        },
        _btnDm(){
            this.dmlist.push({
                text: this.valInput, 
                x: 1120, 
                y: (Math.floor(Math.random()*12)+1)*60
            })
            console.log(this.dmlist);
        },
        draw(){
            //把canvas上的所有像素点都给抹掉
            this.ctx.clearRect(0, 0, 1200, 560);
            this.dmlist.forEach(item=>{ //item: {text:'343', x:0, y:0}
                item.x--;
                // 将当前item对象输出到canvas上 white
                this.ctx.font = '14px 微软雅黑';
                this.ctx.fillStyle = "white";
                this.ctx.fillText(item.text, item.x, item.y);
            })
            window.requestAnimationFrame(this.draw);
        },
        Fullscreen(){
            this.$refs.reference.requestFullscreen();
        },
        getvolume(){
            this.$refs.reference.volume = Math.min(this.$refs.reference.volume+0.1,1)
            console.log(this.$refs.reference.volume)
        },
        getMenuvalue(e){
            console.log(e);
            this.Menuvalue = e
        },
        handleMenu(){
            this.Menu= true
        },
        MenuLeave(){
            this.Menu= false
        },
        gettimeupdate(){
            this.time = this.$refs.reference.currentTime
            this.qi = this.$refs.reference.currentTime
        },
        getMenuLong(e){
            console.log(e);
            this.$refs.reference.currentTime = e
        },
        formatDuring(mss){
            var arr = mss*1000
            if(arr === 0) return '00:00:00'
            var days = parseInt(arr / (1000 * 60 * 60 * 24));
            var hours = parseInt((arr % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
            var minutes = parseInt((arr % (1000 * 60 * 60)) / (1000 * 60));
            var seconds = ((arr % (1000 * 60)) / 1000).toFixed(0);
            // days + ":" + .toFixed(0)
            var str = (hours<9 ? `0${hours}`:hours) + ":" + (minutes<9 ? `0${minutes}`:minutes) + ":"+ (seconds<9 ? `0${seconds}`:seconds)
            return str;
        },
    },
}
</script>
<style>
    .rightIcon{
        margin-right:20px;font-size: 20px;vertical-align: middle;
    }
    .leftIcon{
        font-size: 20px;vertical-align: middle;margin-right:10px;
    }
    .leftText{
        font-size: 13px;vertical-align: middle;
    }
    .centerIcon{
        position: absolute; 
        top: 50%;left: 50%; 
        transform: translate(-50%,-50%);
        color: #fff;
        font-size: 120px;
    }
    .topBottom{
        padding: 10px;
        background: #000;
    }
    .topBottom .el-slider__runway{
        width: 2px !important;
        height: 120px !important;
    }
    .topBottom .el-slider__button{
        width: 10px !important;
        height: 8px !important;
        margin-left: -4px !important;
        border: none !important;
        background-color: #409EFF !important;
        border-radius: 0 !important;
    }
    .topBottom .el-slider__bar{
        width: 2px !important;
    }
    .el-slider{
        width: 98.4% !important;
    }
    .el-slider__runway{
        height: 2px !important;
        margin: 0 !important;
    }
    .el-slider__bar{
        height: 2px !important;
    }
    .el-slider__button{
        width: 16px !important;
        height: 8px !important;
        margin-top: -4px !important;
        border: none !important;
        background-color: #409EFF !important;
        border-radius: 0 !important;
    }
    video::-webkit-media-controls {
        display: none;
    }
    /* 取消点击暂停 */
    video{
        pointer-events: none;
    }
    #fileurl{
        margin-left: 20px;
        color: #fff;
        font-size: 13px;
        border: none;
        padding: 10px;
        background: #409eff;
        border-radius: 2px;
        display: none;
    }
    #canvas{
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 60px;

    }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值