(function() {
	'use strict'
	function YBBarrage ({container, barrages = [], config = {}}){
		if(!(this instanceof YBBarrage)){ //如果this不是指向MyClass
		    throw new TypeError("TypeError: Class constructor YBBarrage cannot be invoked without 'new'")
		}
	   this.container = container
	   this.canvas = null
	   this.paused = true
	   this.barrages = barrages
	   this.config = {}
	   this._ctx = null
	   this._barrages = []
	   this._w = 0
	   this._h = 0
	   this._drawTimer = null
	   this._drawAnima = null
	   this.currentTime = 0
	   this._count = 0
	   this._startTime = 0
	   this._refreshTimer = null
	   this.setConfig(config)
	   this._init()
	}
	Object.defineProperty(YBBarrage.prototype,'_init',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._init is not a constructor")
	        }
	        this.canvas = document.createElement('CANVAS');
	        this.canvas.setAttribute('width', this.container.offsetWidth);
	        this.canvas.setAttribute('height', this.container.offsetHeight);
	        this.container.appendChild(this.canvas);
	        this._ctx = this.canvas.getContext('2d');
	        let rect = this.canvas.getBoundingClientRect();
	        this._w = rect.right - rect.left;
	        this._h = rect.bottom - rect.top;
	        this.currentTime = this.config.initialTime;
	        window.addEventListener('resize', this.refresh.bind(this))
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_destroy',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._destroy is not a constructor")
	        }
	        this.paused = true;
	        this._clearTimer();
	        this._cancelAnima();
	        this._clear();
	        this.barrages = [];
	        this._barrages = [];
	        this.container = null;
	        this._ctx = null;
	        if ( this.canvas ) {
	        	this.canvas.remove();
	        	this.canvas = null;
	        }
	        window.removeEventListener('resize', this.refresh.bind(this))
	    },
	    enumerable:false
	})
	//开始绘制
	Object.defineProperty(YBBarrage.prototype,'_render',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._render is not a constructor")
	        }
	        this._cancelAnima();
	        if ( this.paused ) return;
	        if (this._barrages.length) {
	        	this._clear();
	        	for (let i = 0; i < this._barrages.length; i++) {
	        		let b = this._barrages[i];
	        		if (b.left + b.width <= 0) {
	        			this._barrages.splice(i, 1);
	        			i--;
	        			continue;
	        		}
	        		b.offset = this._detectionBump(b);
	        		let offset = b.offset * this.config.playbackRate;
	        		b.left -= offset;
	        		this._drawText(b);
	        	}
	        }
	        this._drawAnima = window.requestAnimationFrame(this._render.bind(this));
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_cancelAnima',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._cancelAnima is not a constructor")
	        }
	        if ( this._drawAnima ) {
	        	window.cancelAnimationFrame(this._drawAnima)
	        	this._drawAnima = null
	        }
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_clear',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._clear is not a constructor")
	        }
	        this._ctx && this._ctx.clearRect(0, 0, this._w, this._h);
	    },
	    enumerable:false
	})
	//绘制文字
	Object.defineProperty(YBBarrage.prototype,'_drawText',{
	    value:function(barrage){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._drawText is not a constructor")
	        }
	        if ( this._ctx ) {
	        	this._ctx.beginPath();
	        	this._ctx.font = `${barrage.fontSize || this.config.fontSize}px ${this.config.fontFamily}`;
	        	this._ctx.strokeStyle = this._hex2rgba(this._getStrokeColor(barrage.color || this.config.defaultColor), this.config.opacity);
	        	this._ctx.strokeText(barrage.text, barrage.left, barrage.top);
	        	this._ctx.fillStyle = this._hex2rgba(barrage.color || this.config.defaultColor, this.config.opacity);
	        	this._ctx.fillText(barrage.text, barrage.left, barrage.top);
	        	this._ctx.closePath();
	        }
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_startTimer',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._startTimer is not a constructor")
	        }
	       this._startTime = new Date().getTime();
	       this._count = 0;
	       this._drawTimer = window.setTimeout(this._fixed.bind(this), 1000);
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_clearTimer',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._clearTimer is not a constructor")
	        }
			if ( this._drawTimer ) {
				window.clearTimeout(this._drawTimer)
				this._drawTimer = null
			}
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_fixed',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._fixed is not a constructor")
	        }
			this._clearTimer();
			if ( this.paused ) return;
			if ( this.config.duration > -1 && this.currentTime >= this.config.duration ) {
				this.seek(0);
				return;
			} 
			this._count++;
			this.currentTime += (1 * this.config.playbackRate);
			let barrages = this.barrages.filter(barrage => parseInt(barrage.time) == parseInt(this.currentTime));
			for ( let i = 0; i < barrages.length; i++ ) {
				let bar = this._getBarrage(barrages[i]);
				bar && this._barrages.push(bar);
			}
			let offset = new Date().getTime() - (this._startTime + (this._count * 1000));
			let nextTime = 1000 - offset;
			if (nextTime < 0) nextTime = 0;
			this._drawTimer = window.setTimeout(this._fixed.bind(this), nextTime);
	    },
	    enumerable:false
	})
	//测绘弹幕
	Object.defineProperty(YBBarrage.prototype,'_getBarrage',{
	    value:function(barrage, isVisible = false){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._getBarrage is not a constructor")
	        }
			let fontSize = barrage.fontSize || this.config.fontSize;
			let offset = this._getOffset();
			this._ctx.font = `${barrage.fontSize || this.config.fontSize}px ${this.config.fontFamily}`;
			let width = Math.ceil(this._ctx.measureText(barrage.text).width);
			let top = this._getTop(fontSize, width, offset, isVisible);
			if ( top > -1 ) {
				return {
					text: barrage.text,
					time: barrage.time,
					fontSize: fontSize,
					color: barrage.color || this.config.defaultColor,
					top: top,
					left: this._w,
					offset: offset,
					width: width
				}
			}
			return false;
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_getStrokeColor',{
	    value:function(color){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._getStrokeColor is not a constructor")
	        }
			let hex = color.length == 7 ? color : '#' + color.slice(1, 4) + color.slice(1, 4)
			const r = parseInt(hex.slice(1,3),16);
			const g = parseInt(hex.slice(3,5),16);
			const b = parseInt(hex.slice(5,7),16);
			let $grayLevel = (r * 0.299) + (g * 0.587) + (b * 0.144);
			if ($grayLevel >= 120) {
				return '#000000';
			} else {
			　	return '#ffffff';
			}
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'_hex2rgba',{
	    value:function(hex, opacity){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._hex2rgba is not a constructor")
	        }
			hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
			let str="rgba("
			const r = parseInt(hex.slice(1,3),16).toString();
			const g = parseInt(hex.slice(3,5),16).toString();
			const b = parseInt(hex.slice(5,7),16).toString();
			str += r+","+g+","+b+","+opacity+")";
			return str;
	    },
	    enumerable:false
	})
	//计算弹幕距离顶部位置
	Object.defineProperty(YBBarrage.prototype,'_getTop',{
	    value:function(size, width, offset, isVisible = false){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._getTop is not a constructor")
	        }
			//canvas绘制文字x,y坐标是按文字左下角计算，预留30px
			let top = -1;
			let len = -1
			//根据弹幕高度分割轨道
			for ( let i = 0; i < Math.floor(this._h / (size + this.config.lineHeight)); i++ ) {
				//当前轨道的顶部位置
				let nowTop = ((i + 1) * size) + (i * this.config.lineHeight);
				//当前轨道上有多少条弹幕
				let barrages = this._barrages.filter(barrage => barrage.top < nowTop + size && barrage.fontSize + barrage.top > nowTop );
				if ( barrages.length > 0 ) {
					//当前轨道有弹幕运行
					let arr = barrages.map(barrage => barrage.left + barrage.width);
					if ( !this.config.overlap || isVisible ) {
						//开启防重叠会取消多余弹幕显示
						if ( arr.length < len || len < 0 ) {
							len = arr.length;
							top = nowTop;
						}
					}
					//获取当前轨道最右的弹幕
					let max = Math.max(...arr);
					//如果当前轨道还有空位则将弹幕放入当前轨道
					if ( max < this._w - (isVisible ? width : 10) ) {
						top = nowTop;
						break;
					}
				} else {
					//当前轨道没有弹幕运行
					top = nowTop;
					break;
				}
			}
			return top;
	    },
	    enumerable:false
	})
	//获取偏移量
	Object.defineProperty(YBBarrage.prototype,'_getOffset',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._getOffset is not a constructor")
	        }
			return parseFloat( ((this.config.speed / 70) + Math.random()).toFixed(1) );
	    },
	    enumerable:false
	})
	//碰撞检测 速度计算
	Object.defineProperty(YBBarrage.prototype,'_detectionBump',{
	    value:function(bar){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage._detectionBump is not a constructor")
	        }
			let nowTop = bar.top;
			let offset = bar.offset;
			//检测当前弹幕轨道有多少条弹幕
			let barrages =  this._barrages.filter(barrage => barrage.top < nowTop + bar.fontSize && barrage.fontSize + barrage.top > nowTop && bar.left != barrage.left);
			//检测当前弹幕轨道前方有多少条弹幕
			let headbarrages = barrages.filter(barrage => bar.left + bar.width >= barrage.left + barrage.width + 10 && barrage.left + barrage.width > 0);
			//检测当前弹幕轨道后方有多少条弹幕
			let footbarrages = barrages.filter(barrage => bar.left + bar.width + 10 < barrage.left && barrage.left + barrage.width > 0);
			if ( headbarrages.length > 0 ) {
				let arr = headbarrages.map(barrage => barrage.left + barrage.width);
				//取出离当前弹幕最近的那条弹幕
				let max = Math.max(...arr);
				let i = arr.indexOf(max);
				if ( bar.left > max + 20 ) {
					//如果离最近弹幕距离大于10，则加速
					offset = headbarrages[i].offset + 0.01;
				} else {
					//否则减速
					offset = headbarrages[i].offset - 0.01;
				}
			}
			if ( footbarrages.length > 0 ) {
				//前方一条弹幕都没有 且 未弹幕未完全显示 则加速
				if ( bar.left + bar.width > this._w && headbarrages.length == 0 ) {
					offset = bar.offset + 0.01;
				} else {
					let arr = footbarrages.map(barrage => barrage.left);
					//取出离当前弹幕最近的那条弹幕
					let min = Math.min(...arr);
					let i = arr.indexOf(min);
					//如果后方存在弹幕 且距离小于10则加速
					if ( min - (bar.left + bar.width) <= 20 ) {
						offset = bar.offset + 0.01;
					}
				}
			}
			if ( offset < 0.5 ) offset = 0.5;
			if ( offset > 3 ) offset = 3;
			return offset;
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'setBarrages',{
	    value:function(barrages){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.setBarrages is not a constructor")
	        }
			this.barrages = barrages
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'add',{
	    value:function(barrage){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.add is not a constructor")
	        }
			const arr = Object.prototype.toString.call(barrage) == '[object Array]' ? barrage : [barrage]
			arr.forEach(item => {
				item.time = item.time || this.currentTime;
				this.barrages.push(item);
				if ( parseInt(item.time) == parseInt(this.currentTime) ) {
					const bar = this._getBarrage(item, true);
					bar.left = this._w - bar.width;
					this._barrages.push(bar);
				}
			})
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'setConfig',{
	    value:function(config){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.setConfig is not a constructor")
	        }
			this.config = Object.assign({}, {
				duration: -1, // 弹幕动画的循环周期，-1 表示不循环播放
				speed: 150, // 弹幕的运动速度
				fontSize: 24, // 文字大小，单位：像素
				fontFamily: 'Microsoft Yahei', // 字体，默认值：微软雅黑
				opacity: 1, // 透明度，有效值 0-1
				defaultColor: '#fff', // 默认颜色，与 CSS 颜色属性一致
				lineHeight: 5,//行间距
				overlap: true,//开启防重叠 可能会导致部分弹幕不显示
				playbackRate: 1.0,//播放倍速
				initialTime: 0//初始时间
			}, this.config, config)
	    },
	    enumerable:false
	})
	//刷新尺寸（当容器大小变化时调用此接口）
	Object.defineProperty(YBBarrage.prototype,'refresh',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.refresh is not a constructor")
	        }
			if ( this._refreshTimer ) {
				window.clearTimeout(this._refreshTimer)
				this._refreshTimer = null
			}
			this._refreshTimer = window.setTimeout(() => {
				if ( this.canvas && this.container ) {
					this.canvas.width = this.container.offsetWidth;
					this.canvas.height = this.container.offsetHeight;
					this._w = this.container.offsetWidth;
					this._h = this.container.offsetHeight;
					let paused = this.paused;
					if ( !paused ) {
						this.play()
					}
					window.clearTimeout(this._refreshTimer)
					this._refreshTimer = null
				}
			}, 200)
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'play',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.play is not a constructor")
	        }
			if ( this.paused ) {
				this.paused = false;
				this._startTimer();
				this._render();
			}
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'pause',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.pause is not a constructor")
	        }
			if ( !this.paused ) {
				this.paused = true;
				this._clearTimer();
				this._cancelAnima();
			}
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'stop',{
	    value:function(){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.stop is not a constructor")
	        }
			this._destroy();
	    },
	    enumerable:false
	})
	Object.defineProperty(YBBarrage.prototype,'seek',{
	    value:function(time){
	        if(!(this instanceof YBBarrage)){//那么相反 不是正常调用的就是错误的调用
	           throw new TypeError("TypeError: YBBarrage.seek is not a constructor")
	        }
			this._barrages = [];
			this.currentTime = parseInt(time);
			this._clear();
			let paused = this.paused;
			if ( !paused ) {
				this.play()
			}
	    },
	    enumerable:false
	})
	if ("object" == typeof exports && "undefined" != typeof module) module.exports = YBBarrage;
	else if ("function" == typeof define && define.amd) define([], YBBarrage);
	else {
		var t;
		t = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this, t.YBBarrage = YBBarrage
	}
})();