Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用canvas实现视频播放与弹幕功能 #30

Open
Aaaaaaaty opened this issue Dec 3, 2017 · 6 comments
Open

用canvas实现视频播放与弹幕功能 #30

Aaaaaaaty opened this issue Dec 3, 2017 · 6 comments
Labels

Comments

@Aaaaaaaty
Copy link
Owner

写在最前

本次分享一下使用canvas来进行视频播放并且添加弹幕功能。

欢迎关注我的博客,不定期更新中——

效果图

示例源码见:源码地址
ezgif com-optimize

可以看到上方为一段视频,下面是用canvas来重新绘制的视频,并且支持动态的添加弹幕。

canvas载入视频

canvas中的drawImage方法绘制图片所需要的数据源不单单是某张图片,同样可以是使用视频的某一帧来进行绘制。就像这样:

var video = document.getElementById('video')
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var ctx.drawImage(video, 0, 0, width, height);//当视频开始播放后触发这个方法可以开始绘制视频

为什么通过canvas绘制视频?

因为canvas提供了getImageData && putImageData方法使得操作者可以动态得来更改每一帧图像的显示状态,如果你知道它应该怎么变:)

比如像MDN中提到的可以对上面这段视频中的黄色背景进行色调的变化:mdn示例地址

this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
    let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
    let l = frame.data.length / 4;

    for (let i = 0; i < l; i++) {
      let r = frame.data[i * 4 + 0];
      let g = frame.data[i * 4 + 1];
      let b = frame.data[i * 4 + 2];
      if (g > 100 && r > 100 && b < 43)
        frame.data[i * 4 + 3] = 0; //将视频黄色部分的透明度进行了变化
    }
    this.ctx2.putImageData(frame, 0, 0);

视频中效果截图如下:
image
更多关于canvas的图像操作可以参考下面这两篇文章:

基于canvas的图像处理可以实现很强大的功能,比如滤镜啊之类的~

腾讯的Aolly Team团队出品的AlloyImage - 基于HTML5技术的专业图像处理库就是个很好的范例。作者就搞不明白那些高深的东西了,什么拉普拉斯算子,各种算子:)

弹幕功能

弹幕功能分为两部分:

  • 监听新弹幕的推送
  • 渲染弹幕到页面

监听新弹幕的推送

通过维护一个弹幕数组来实时去渲染每一个弹幕字条的应有位置。而何时更新这个数组,为了解耦作者使用了发布订阅的方式来进行数组的更新。当然这里并不是一定要使用这种模式,只不过作者刚刚学习完所以拿来用一下而已。千万别喷我:)

var Event = (function(){
    var list = {},
        listen,
        trigger,
        remove;
        listen = function(key,fn){ /收集监听事件
            if(!list[key]) {
                list[key] = [];
            }
            list[key].push(fn);
        };
        trigger = function(){/触发后依次执行回调
            var key = Array.prototype.shift.call(arguments),
                 fns = list[key];
            if(!fns || fns.length === 0) {
                return false;
            }
            for(var i = 0, fn; fn = fns[i++];) {
                fn.apply(this,arguments);
            }
        };
        remove = function(key,fn){
            var fns = list[key];
            if(!fns) {
                return false;
            }
            if(!fn) {
                fns && (fns.length = 0);
            }else {
                for(var i = fns.length - 1; i >= 0; i--){
                    var _fn = fns[i];
                    if(_fn === fn) {
                        fns.splice(i,1);
                    }
                }
            }
        };
        return {
            listen: listen,
            trigger: trigger,
            remove: remove
        }
})();
//调用方式
Event.listen('data', addNewWord)

$('#submit').click(function() { //点击发送后便触发data事件
  var data = $('input').val()
    Event.trigger('data', {
      value: data,
    })
})

function addNewWord (data) {
    var newWord = new Barrage(this.canvas, this.ctx, data) //构建新的弹幕实例
    wordObj.push(newWord)
},

渲染弹幕到页面

声明了一个弹幕的构造函数,内部包含了其各种属性并且在原型链中添加了draw方法来进行绘制:

function Barrage(canvas, ctx, data) {
    this.width = canvas.width
    this.height = canvas.height
    this.ctx = ctx
    this.color = data.color || '#'+Math.floor(Math.random()*16777215).toString(16) //随机颜色
    this.value = data.value
    this.x = this.width //x坐标
    this.y = Math.random() * this.height
    this.speed = Math.random() + 0.5
    this.fontSize = Math.random() * 10 + 12
}
Barrage.prototype.draw = function() {
        if(this.x < -200) {
            return
        } else {
            this.ctx.font = this.fontSize + 'px "microsoft yahei", sans-serif';
            this.ctx.fillStyle = this.color
            this.x = this.x - this.speed
            this.ctx.fillText(this.value, this.x, this.y)
        }
}

最后

惯例po作者的博客,不定时更新中——
有问题欢迎在issues下交流。

@Aaaaaaaty
Copy link
Owner Author

@happyWeber 声音。。肯定是canvas自己不行啦,我其实并不是要用canvas实现播放器,只是想介绍下canvas可以处理视频帧,并且可以拿到帧数据然后可以自行改变成自己想要的效果。

@gavin-cooker
Copy link

var ctx.drawImage(video, 0, 0, width, height);//当视频开始播放后触发这个方法可以开始绘制视频
这句是不是写错了?

@Aaaaaaaty
Copy link
Owner Author

@LinShouGui 这句话就是把视频帧绘制到canvas呀 可能我的表达不准确

@fugang1996
Copy link

大佬,我可以问下多人视频聊天语音转文字吗,我现在能把语音转换为文字了但是文字只能在自己这方看到,有什么办法可以使用canvas把文字绘制到视频流 里面去吗

@liejiayong
Copy link

请问在移动端中,某些Android浏览器中canvas不能渲染video怎么解决?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants
@Aaaaaaaty @liejiayong @gavin-cooker @fugang1996 and others