UNPKG

vue-barrage

Version:

一个基于Canvas+Vue的弹幕组件

1,096 lines (1,011 loc) 45.8 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else { var a = factory(); for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } })(typeof self !== 'undefined' ? self : this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = "/dist/"; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 1); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // import faceMap from '../../assets/emoji' /* harmony default export */ __webpack_exports__["a"] = ({ name: 'Barrage', props: { barrageList: { type: Array, default: () => [] }, speed: { type: Number, default: 4 }, loop: { type: Boolean, default: true }, channels: { type: Number, default: 2 }, barrageHeight: { type: Number, default: 60 }, screenPercent: { type: Number, default: 0.3 }, borderColor: { type: String, default: '' }, background: { type: String, default: '' }, linearGradient: { type: Object, default: () => { return { startColor: '', endColor: '' }; } } }, data() { return { newBarrageArray: [], // 新增弹幕之后的总弹幕 barrageArray: [], barrageQueue: [], containerWidth: 0, containerHeight: 0, channelsArray: [], barrageChannels: 1 }; }, watch: { barrageList(val) { if (val.length !== 0) { this.barrageQueue = JSON.parse(JSON.stringify(val)); this.newBarrageArray = JSON.parse(JSON.stringify(val)); this.initData(); window.requestAnimationFrame(this.render); } } }, mounted() { this.containerWidth = document.body.clientWidth * 2; this.containerHeight = window.screen.height * this.screenPercent > 2 * this.barrageHeight ? window.screen.height * this.screenPercent : this.barrageHeight + 30; // 设定总高度 this.barrageChannels = Math.floor(this.containerHeight / (this.barrageHeight + 30)) || this.channels; // 总高度对应的轨道数 this.ctx = this.$refs.canvas.getContext('2d'); this.ctx1 = this.$refs.canvasContainer.getContext('2d'); this.barrageClickEvent(); }, methods: { /** * 数据初始化 */ initData() { for (let i = 0; i < this.barrageQueue.length; i++) { // 此处处理只显示50个字符 let tagImg = null; let img = null; if (this.barrageQueue[i].icon) { img = new Image(); img.src = this.barrageQueue[i].icon; } if (this.barrageQueue[i].tagImage) { tagImg = new Image(); tagImg.src = this.barrageQueue[i].tagImage; } let content = this.dealStr(this.barrageQueue[i].content); this.barrageArray.push({ id: this.barrageQueue[i].id, content: content, x: this.containerWidth + this.barrageHeight, icon: img, tagImage: tagImg, width: this.ctx1.measureText(content).width * 3 + (this.barrageQueue[i].icon ? 60 : 0), color: this.barrageQueue[i].color || '#FFFFFF', bgColor: this.barrageQueue[i].bgColor || 'rgba(0,0,0,0.4)' }); } this.initChannel(); }, /** * 初始化轨道数据 */ initChannel() { for (let i = 0; i < this.barrageChannels; i++) { let item = this.barrageArray.shift(); if (item) { this.channelsArray[i] = [item]; } else { this.channelsArray[i] = []; } } }, /** * 渲染 */ render() { this.ctx.clearRect(0, 0, this.containerWidth, this.containerHeight); this.ctx.font = '30px Microsoft YaHei'; this.draw(); window.requestAnimationFrame(this.render); }, draw() { for (let i = 0; i < this.channelsArray.length; i++) { for (let j = 0; j < this.channelsArray[i].length; j++) { try { let barrage = this.channelsArray[i][j]; barrage.x -= this.speed; if (barrage.x <= this.containerWidth) { // 弹幕显示 this.borderColor && this.drawRoundRectBorder(this.ctx, barrage.x - this.barrageHeight / 2, i * (this.barrageHeight + 20) + 20, barrage.width + this.barrageHeight, this.barrageHeight, this.barrageHeight / 2); this.drawRoundRect(this.ctx, barrage.bgColor, barrage.x - this.barrageHeight / 2, i * (this.barrageHeight + 20) + 21, barrage.width + this.barrageHeight, this.barrageHeight - 2, this.barrageHeight / 2); this.ctx.fillStyle = `${barrage.color}`; this.ctx.fillText(barrage.content, barrage.x + (barrage.icon ? this.barrageHeight / 2 + 20 : -5), i * (this.barrageHeight + 20) + this.barrageHeight); if (barrage.icon) { this.circleImg(this.ctx, barrage.icon, barrage.x - 10, i * (this.barrageHeight + 20) + 26, 24); } if (barrage.tagImage) { this.originImg(this.ctx, barrage.tagImage, barrage.x - this.barrageHeight - 10, i * (this.barrageHeight + 20) + 20, this.barrageHeight, this.barrageHeight); } } if (barrage.x < -(barrage.width + this.barrageHeight)) { // 弹幕删除 let arr = this.channelsArray.reduce((a, b) => a.concat(b)); if (this.loop) { if (this.checkBarrageStatus(arr)) { this.barrageQueue = []; this.barrageQueue = JSON.parse(JSON.stringify(this.newBarrageArray)); this.initData(); } } } // 弹幕插入时机判断 if (barrage.x <= Math.floor(this.containerWidth - barrage.width - 40) && barrage.x >= Math.floor(this.containerWidth - barrage.width - 40 - this.speed) && j === this.channelsArray[i].length - 1 && this.barrageArray.length !== 0) { let item = this.barrageArray.shift(); this.channelsArray[i].push(item); } } catch (e) { console.log(e); } } } }, /** * 重置数据 */ add(obj) { let content = this.dealStr(obj.content); let img = null; let tagImg = null; if (obj.icon) { img = new Image(); img.src = obj.icon; } if (obj.tagImage) { tagImg = new Image(); tagImg.src = obj.tagImage; } let item = { id: obj.id, content: content, x: this.containerWidth + this.barrageHeight, icon: obj.icon ? img : '', tagImage: obj.tagImage ? tagImg : '', width: this.ctx1.measureText(content).width * 3 + (obj.icon ? this.barrageHeight : 0), color: obj.color || '#FFFFFF', bgColor: obj.bgColor || 'rgba(0,0,0,0.4)' }; let originItem = { id: obj.id, content: obj.content, icon: obj.icon, tagImage: obj.tagImage, color: obj.color || '#FFFFFF', bgColor: obj.bgColor || 'rgba(0,0,0,0.4)' }; if (this.barrageArray.length === 0) { // 剩余弹幕数为0 this.newBarrageArray.unshift(originItem); } else { this.barrageArray.unshift(item); let insertIndex = this.barrageList.length - this.barrageArray.length; this.newBarrageArray.splice(insertIndex, 0, originItem); } }, /** * 弹幕点击事件 */ barrageClickEvent() { document.getElementById('canvas').addEventListener('click', e => { const p = this.getEventPosition(e); let channelIndex = Math.floor(p.y / (this.barrageHeight + 30)); const tempArray = JSON.parse(JSON.stringify(this.channelsArray[channelIndex])); for (let i = 0; i < tempArray.length; i++) { let channelItemArray = tempArray[i]; if (p.x > channelItemArray.x && p.x < channelItemArray.x + channelItemArray.width) { if (channelItemArray.id) { this.$emit('doLike', channelItemArray.id); } } } }, false); }, /** * 获取点击位置 */ getEventPosition(ev) { let x, y; if (ev.layerX || ev.layerX === 0) { x = ev.layerX; y = ev.layerY; } else if (ev.offsetX || ev.offsetX === 0) { x = ev.offsetX; y = ev.offsetY; } return { x: 2 * x, y: 2 * y }; }, /** * 判断所有的弹幕是否滚动完成 * @params arr */ checkBarrageStatus(arr) { for (let i = 0; i < arr.length; i++) { if (arr[i].x > -arr[i].width) return false; } return true; }, /** * 处理字符 */ dealStr(str) { return str.length > 50 ? `${str.substring(0, 50)}...` : str; }, /** * 获取随机颜色 */ getColor() { return `#${Math.floor(Math.random() * 16777215).toString(16)}`; }, /** * 裁剪图片 * @param ctx * @param img * @param x * @param y * @param r */ circleImg(ctx, img, x, y, r) { ctx.save(); let d = 2 * r; let cx = x + r; let cy = y + r; ctx.beginPath(); ctx.arc(cx, cy, r, 0, 2 * Math.PI); ctx.clip(); ctx.drawImage(img, x, y, d, d); ctx.restore(); ctx.closePath(); }, /** * 绘制原始图片 * @param ctx * @param img * @param x * @param y * @param width * @param height */ originImg(ctx, img, x, y, width, height) { try { ctx.beginPath(); ctx.drawImage(img, x, y, width, height); ctx.closePath(); } catch (e) { console.log(e); } }, /** * 绘画圆角矩形 * @param context * @param bgColor * @param x * @param y * @param width * @param height * @param radius */ drawRoundRect(context, bgColor, x, y, width, height, radius) { if (this.linearGradient.startColor && this.linearGradient.endColor) { let linearGrad = context.createLinearGradient(x, y, x, y + height); linearGrad.addColorStop(0, this.linearGradient.startColor); linearGrad.addColorStop(1, this.linearGradient.endColor); context.fillStyle = linearGrad || bgColor; } else { context.fillStyle = this.background || bgColor; } context.beginPath(); context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2); context.lineTo(width - radius + x, y); context.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2); context.lineTo(width + x, height + y - radius); context.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2); context.lineTo(radius + x, height + y); context.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI); context.fill(); context.closePath(); }, /** * 绘画圆角矩形 * @param context * @param x * @param y * @param width * @param height * @param radius 半径 */ drawRoundRectBorder(context, x, y, width, height, radius) { context.beginPath(); context.lineWidth = 2; context.strokeStyle = this.borderColor; context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2); context.lineTo(width - radius + x, y); context.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2); context.lineTo(width + x, height + y - radius); context.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2); context.lineTo(radius + x, height + y); context.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI); context.stroke(); context.closePath(); } } }); /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__barrage_vue__ = __webpack_require__(2); /** * @author Nickyzhang * @date 2019/7/13 * @description file templates */ __WEBPACK_IMPORTED_MODULE_0__barrage_vue__["a" /* default */].install = function (Vue) { Vue.component('Barrage', __WEBPACK_IMPORTED_MODULE_0__barrage_vue__["a" /* default */]); }; /* harmony default export */ __webpack_exports__["default"] = (__WEBPACK_IMPORTED_MODULE_0__barrage_vue__["a" /* default */]); /***/ }), /* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_barrage_vue__ = __webpack_require__(0); /* unused harmony namespace reexport */ /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_2ac013d2_hasScoped_true_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_barrage_vue__ = __webpack_require__(9); var disposed = false function injectStyle (ssrContext) { if (disposed) return __webpack_require__(3) } var normalizeComponent = __webpack_require__(8) /* script */ /* template */ /* template functional */ var __vue_template_functional__ = false /* styles */ var __vue_styles__ = injectStyle /* scopeId */ var __vue_scopeId__ = "data-v-2ac013d2" /* moduleIdentifier (server only) */ var __vue_module_identifier__ = null var Component = normalizeComponent( __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_barrage_vue__["a" /* default */], __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_2ac013d2_hasScoped_true_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_barrage_vue__["a" /* default */], __vue_template_functional__, __vue_styles__, __vue_scopeId__, __vue_module_identifier__ ) Component.options.__file = "src/barrage.vue" /* hot reload */ if (false) {(function () { var hotAPI = require("vue-hot-reload-api") hotAPI.install(require("vue"), false) if (!hotAPI.compatible) return module.hot.accept() if (!module.hot.data) { hotAPI.createRecord("data-v-2ac013d2", Component.options) } else { hotAPI.reload("data-v-2ac013d2", Component.options) } module.hot.dispose(function (data) { disposed = true }) })()} /* harmony default export */ __webpack_exports__["a"] = (Component.exports); /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { // style-loader: Adds some css to the DOM by adding a <style> tag // load the styles var content = __webpack_require__(4); if(typeof content === 'string') content = [[module.i, content, '']]; if(content.locals) module.exports = content.locals; // add the styles to the DOM var update = __webpack_require__(6)("d2fdf31c", content, false, {}); // Hot Module Replacement if(false) { // When the styles change, update the <style> tags if(!content.locals) { module.hot.accept("!!../node_modules/css-loader/index.js?sourceMap!../node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-2ac013d2\",\"scoped\":true,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./barrage.vue", function() { var newContent = require("!!../node_modules/css-loader/index.js?sourceMap!../node_modules/vue-loader/lib/style-compiler/index.js?{\"vue\":true,\"id\":\"data-v-2ac013d2\",\"scoped\":true,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./barrage.vue"); if(typeof newContent === 'string') newContent = [[module.id, newContent, '']]; update(newContent); }); } // When the module is disposed, remove the <style> tags module.hot.dispose(function() { update(); }); } /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(5)(true); // imports // module exports.push([module.i, "\n.z_barrage-container[data-v-2ac013d2] {\n pointer-events: none;\n}\n.z_container[data-v-2ac013d2] {\n width: 100%;\n overflow: hidden;\n}\n.z_barrage[data-v-2ac013d2] {\n position: absolute;\n top: 0;\n left: 0;\n}\n", "", {"version":3,"sources":["/Users/Cral-Gates/Project/vue-plugin/src/src/barrage.vue"],"names":[],"mappings":";AAuYA;EACA,qBAAA;CACA;AACA;EACA,YAAA;EACA,iBAAA;CACA;AACA;EACA,mBAAA;EACA,OAAA;EACA,QAAA;CACA","file":"barrage.vue","sourcesContent":["<!--\n * @Author: your name\n * @Date: 2020-03-18 13:26:53\n * @LastEditTime: 2020-09-02 22:05:29\n * @LastEditors: Please set LastEditors\n * @Description: In User Settings Edit\n * @FilePath: /vue-barrage/src/views/src/barrage_new.vue\n-->\n<template>\n <div class=\"z_barrage-container\">\n <canvas\n ref=\"canvasContainer\"\n :width=\"containerWidth\"\n :height=\"containerHeight\"\n style=\"display: none;\"\n />\n <div\n class=\"z_container\"\n :style=\"{height: containerHeight/2+'px'}\"\n >\n <canvas\n id=\"canvas\"\n ref=\"canvas\"\n class=\"z_barrage\"\n :width=\"containerWidth\"\n :height=\"containerHeight\"\n :style=\"{'width': containerWidth/2 + 'px',\n 'height': containerHeight/2 + 'px'}\"\n />\n </div>\n </div>\n</template>\n\n<script>\n// import faceMap from '../../assets/emoji'\nexport default {\n name: 'Barrage',\n props: {\n barrageList: {\n type: Array,\n default: () => []\n },\n speed: {\n type: Number,\n default: 4\n },\n loop: {\n type: Boolean,\n default: true\n },\n channels: {\n type: Number,\n default: 2\n },\n barrageHeight: {\n type: Number,\n default: 60\n },\n screenPercent: {\n type: Number,\n default: 0.3\n },\n borderColor: {\n type: String,\n default: ''\n },\n background: {\n type: String,\n default: ''\n },\n linearGradient: {\n type: Object,\n default: () => {\n return {\n startColor: '',\n endColor: ''\n }\n }\n }\n },\n data () {\n return {\n newBarrageArray: [], // 新增弹幕之后的总弹幕\n barrageArray: [],\n barrageQueue: [],\n containerWidth: 0,\n containerHeight: 0,\n channelsArray: [],\n barrageChannels: 1\n }\n },\n watch: {\n barrageList (val) {\n if (val.length !== 0) {\n this.barrageQueue = JSON.parse(JSON.stringify(val))\n this.newBarrageArray = JSON.parse(JSON.stringify(val))\n this.initData()\n window.requestAnimationFrame(this.render)\n }\n }\n },\n mounted () {\n this.containerWidth = document.body.clientWidth * 2\n this.containerHeight = window.screen.height * this.screenPercent > 2 * this.barrageHeight ? window.screen.height * this.screenPercent : (this.barrageHeight + 30) // 设定总高度\n this.barrageChannels = Math.floor(this.containerHeight / (this.barrageHeight + 30)) || this.channels // 总高度对应的轨道数\n this.ctx = this.$refs.canvas.getContext('2d')\n this.ctx1 = this.$refs.canvasContainer.getContext('2d')\n this.barrageClickEvent()\n },\n methods: {\n /**\n * 数据初始化\n */\n initData () {\n for (let i = 0; i < this.barrageQueue.length; i++) { // 此处处理只显示50个字符\n let tagImg = null\n let img = null\n if (this.barrageQueue[i].icon) {\n img = new Image()\n img.src = this.barrageQueue[i].icon\n }\n if (this.barrageQueue[i].tagImage) {\n tagImg = new Image()\n tagImg.src = this.barrageQueue[i].tagImage\n }\n let content = this.dealStr(this.barrageQueue[i].content)\n this.barrageArray.push({\n id: this.barrageQueue[i].id,\n content: content,\n x: this.containerWidth + this.barrageHeight,\n icon: img,\n tagImage: tagImg,\n width: this.ctx1.measureText(content).width * 3 + (this.barrageQueue[i].icon ? 60 : 0),\n color: this.barrageQueue[i].color || '#FFFFFF',\n bgColor: this.barrageQueue[i].bgColor || 'rgba(0,0,0,0.4)'\n })\n }\n this.initChannel()\n },\n /**\n * 初始化轨道数据\n */\n initChannel () {\n for (let i = 0; i < this.barrageChannels; i++) {\n let item = this.barrageArray.shift()\n if (item) {\n this.channelsArray[i] = [item]\n } else {\n this.channelsArray[i] = []\n }\n }\n },\n /**\n * 渲染\n */\n render () {\n this.ctx.clearRect(0, 0, this.containerWidth, this.containerHeight)\n this.ctx.font = '30px Microsoft YaHei'\n this.draw()\n window.requestAnimationFrame(this.render)\n },\n draw () {\n for (let i = 0; i < this.channelsArray.length; i++) {\n for (let j = 0; j < this.channelsArray[i].length; j++) {\n try {\n let barrage = this.channelsArray[i][j]\n barrage.x -= this.speed\n if (barrage.x <= this.containerWidth) { // 弹幕显示\n this.borderColor && this.drawRoundRectBorder(this.ctx, barrage.x - this.barrageHeight / 2, i * (this.barrageHeight + 20) + 20, barrage.width + this.barrageHeight, this.barrageHeight, this.barrageHeight / 2)\n this.drawRoundRect(this.ctx, barrage.bgColor, barrage.x - this.barrageHeight / 2, i * (this.barrageHeight + 20) + 21, barrage.width + this.barrageHeight, this.barrageHeight - 2, this.barrageHeight / 2)\n this.ctx.fillStyle = `${barrage.color}`\n this.ctx.fillText(barrage.content, barrage.x + (barrage.icon ? this.barrageHeight / 2 + 20 : -5), i * (this.barrageHeight + 20) + this.barrageHeight)\n if (barrage.icon) {\n this.circleImg(this.ctx, barrage.icon, barrage.x - 10, i * (this.barrageHeight + 20) + 26, 24)\n }\n if (barrage.tagImage) {\n this.originImg(this.ctx, barrage.tagImage, barrage.x - this.barrageHeight - 10, i * (this.barrageHeight + 20) + 20, this.barrageHeight, this.barrageHeight)\n }\n }\n if (barrage.x < -(barrage.width + this.barrageHeight)) { // 弹幕删除\n let arr = this.channelsArray.reduce((a, b) => a.concat(b))\n if (this.loop) {\n if (this.checkBarrageStatus(arr)) {\n this.barrageQueue = []\n this.barrageQueue = JSON.parse(JSON.stringify(this.newBarrageArray))\n this.initData()\n }\n }\n }\n // 弹幕插入时机判断\n if (barrage.x <= Math.floor((this.containerWidth - barrage.width - 40)) && barrage.x >= Math.floor(this.containerWidth - barrage.width - 40 - this.speed) && (j === this.channelsArray[i].length - 1) && this.barrageArray.length !== 0) {\n let item = this.barrageArray.shift()\n this.channelsArray[i].push(item)\n }\n } catch (e) {\n console.log(e)\n }\n }\n }\n },\n /**\n * 重置数据\n */\n add (obj) {\n let content = this.dealStr(obj.content)\n let img = null\n let tagImg = null\n if (obj.icon) {\n img = new Image()\n img.src = obj.icon\n }\n if (obj.tagImage) {\n tagImg = new Image()\n tagImg.src = obj.tagImage\n }\n let item = {\n id: obj.id,\n content: content,\n x: this.containerWidth + this.barrageHeight,\n icon: obj.icon ? img : '',\n tagImage: obj.tagImage ? tagImg : '',\n width: this.ctx1.measureText(content).width * 3 + (obj.icon ? this.barrageHeight : 0),\n color: obj.color || '#FFFFFF',\n bgColor: obj.bgColor || 'rgba(0,0,0,0.4)'\n }\n let originItem = {\n id: obj.id,\n content: obj.content,\n icon: obj.icon,\n tagImage: obj.tagImage,\n color: obj.color || '#FFFFFF',\n bgColor: obj.bgColor || 'rgba(0,0,0,0.4)'\n }\n if (this.barrageArray.length === 0) { // 剩余弹幕数为0\n this.newBarrageArray.unshift(originItem)\n } else {\n this.barrageArray.unshift(item)\n let insertIndex = this.barrageList.length - this.barrageArray.length\n this.newBarrageArray.splice(insertIndex, 0, originItem)\n }\n },\n /**\n * 弹幕点击事件\n */\n barrageClickEvent () {\n document.getElementById('canvas').addEventListener('click', (e) => {\n const p = this.getEventPosition(e)\n let channelIndex = Math.floor(p.y / (this.barrageHeight + 30))\n const tempArray = JSON.parse(JSON.stringify(this.channelsArray[channelIndex]))\n for (let i = 0; i < tempArray.length; i++) {\n let channelItemArray = tempArray[i]\n if (p.x > channelItemArray.x && p.x < (channelItemArray.x + channelItemArray.width)) {\n if (channelItemArray.id) {\n this.$emit('doLike', channelItemArray.id)\n }\n }\n }\n }, false)\n },\n /**\n * 获取点击位置\n */\n getEventPosition (ev) {\n let x, y\n if (ev.layerX || ev.layerX === 0) {\n x = ev.layerX\n y = ev.layerY\n } else if (ev.offsetX || ev.offsetX === 0) {\n x = ev.offsetX\n y = ev.offsetY\n }\n return { x: 2 * x, y: 2 * y }\n },\n /**\n * 判断所有的弹幕是否滚动完成\n * @params arr\n */\n checkBarrageStatus (arr) {\n for (let i = 0; i < arr.length; i++) {\n if (arr[i].x > -arr[i].width) return false\n }\n return true\n },\n /**\n * 处理字符\n */\n dealStr (str) {\n return str.length > 50 ? `${str.substring(0, 50)}...` : str\n },\n /**\n * 获取随机颜色\n */\n getColor () {\n return `#${Math.floor(Math.random() * 16777215).toString(16)}`\n },\n /**\n * 裁剪图片\n * @param ctx\n * @param img\n * @param x\n * @param y\n * @param r\n */\n circleImg (ctx, img, x, y, r) {\n ctx.save()\n let d = 2 * r\n let cx = x + r\n let cy = y + r\n ctx.beginPath()\n ctx.arc(cx, cy, r, 0, 2 * Math.PI)\n ctx.clip()\n ctx.drawImage(img, x, y, d, d)\n ctx.restore()\n ctx.closePath()\n },\n /**\n * 绘制原始图片\n * @param ctx\n * @param img\n * @param x\n * @param y\n * @param width\n * @param height\n */\n originImg (ctx, img, x, y, width, height) {\n try {\n ctx.beginPath()\n ctx.drawImage(img, x, y, width, height)\n ctx.closePath()\n } catch (e) {\n console.log(e)\n }\n },\n /**\n * 绘画圆角矩形\n * @param context\n * @param bgColor\n * @param x\n * @param y\n * @param width\n * @param height\n * @param radius\n */\n drawRoundRect (context, bgColor, x, y, width, height, radius) {\n if (this.linearGradient.startColor && this.linearGradient.endColor) {\n let linearGrad = context.createLinearGradient(x, y, x, y + height)\n linearGrad.addColorStop(0, this.linearGradient.startColor)\n linearGrad.addColorStop(1, this.linearGradient.endColor)\n context.fillStyle = linearGrad || bgColor\n } else {\n context.fillStyle = this.background || bgColor\n }\n context.beginPath()\n context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2)\n context.lineTo(width - radius + x, y)\n context.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2)\n context.lineTo(width + x, height + y - radius)\n context.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2)\n context.lineTo(radius + x, height + y)\n context.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI)\n context.fill()\n context.closePath()\n },\n /**\n * 绘画圆角矩形\n * @param context\n * @param x\n * @param y\n * @param width\n * @param height\n * @param radius 半径\n */\n drawRoundRectBorder (context, x, y, width, height, radius) {\n context.beginPath()\n context.lineWidth = 2\n context.strokeStyle = this.borderColor\n context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2)\n context.lineTo(width - radius + x, y)\n context.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2)\n context.lineTo(width + x, height + y - radius)\n context.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2)\n context.lineTo(radius + x, height + y)\n context.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI)\n context.stroke()\n context.closePath()\n }\n }\n}\n</script>\n\n<style lang=\"css\" scoped>\n .z_barrage-container {\n pointer-events: none;\n }\n .z_container {\n width: 100%;\n overflow: hidden;\n }\n .z_barrage {\n position: absolute;\n top: 0;\n left: 0;\n }\n</style>\n"],"sourceRoot":""}]); // exports /***/ }), /* 5 */ /***/ (function(module, exports) { /* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ // css base code, injected by the css-loader module.exports = function(useSourceMap) { var list = []; // return the list of modules as css string list.toString = function toString() { return this.map(function (item) { var content = cssWithMappingToString(item, useSourceMap); if(item[2]) { return "@media " + item[2] + "{" + content + "}"; } else { return content; } }).join(""); }; // import a list of modules into the list list.i = function(modules, mediaQuery) { if(typeof modules === "string") modules = [[null, modules, ""]]; var alreadyImportedModules = {}; for(var i = 0; i < this.length; i++) { var id = this[i][0]; if(typeof id === "number") alreadyImportedModules[id] = true; } for(i = 0; i < modules.length; i++) { var item = modules[i]; // skip already imported module // this implementation is not 100% perfect for weird media query combinations // when a module is imported multiple times with different media queries. // I hope this will never occur (Hey this way we have smaller bundles) if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { if(mediaQuery && !item[2]) { item[2] = mediaQuery; } else if(mediaQuery) { item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; } list.push(item); } } }; return list; }; function cssWithMappingToString(item, useSourceMap) { var content = item[1] || ''; var cssMapping = item[3]; if (!cssMapping) { return content; } if (useSourceMap && typeof btoa === 'function') { var sourceMapping = toComment(cssMapping); var sourceURLs = cssMapping.sources.map(function (source) { return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' }); return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); } return [content].join('\n'); } // Adapted from convert-source-map (MIT) function toComment(sourceMap) { // eslint-disable-next-line no-undef var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; return '/*# ' + data + ' */'; } /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { /* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra Modified by Evan You @yyx990803 */ var hasDocument = typeof document !== 'undefined' if (typeof DEBUG !== 'undefined' && DEBUG) { if (!hasDocument) { throw new Error( 'vue-style-loader cannot be used in a non-browser environment. ' + "Use { target: 'node' } in your Webpack config to indicate a server-rendering environment." ) } } var listToStyles = __webpack_require__(7) /* type StyleObject = { id: number; parts: Array<StyleObjectPart> } type StyleObjectPart = { css: string; media: string; sourceMap: ?string } */ var stylesInDom = {/* [id: number]: { id: number, refs: number, parts: Array<(obj?: StyleObjectPart) => void> } */} var head = hasDocument && (document.head || document.getElementsByTagName('head')[0]) var singletonElement = null var singletonCounter = 0 var isProduction = false var noop = function () {} var options = null var ssrIdKey = 'data-vue-ssr-id' // Force single-tag solution on IE6-9, which has a hard limit on the # of <style> // tags it will allow on a page var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\b/.test(navigator.userAgent.toLowerCase()) module.exports = function (parentId, list, _isProduction, _options) { isProduction = _isProduction options = _options || {} var styles = listToStyles(parentId, list) addStylesToDom(styles) return function update (newList) { var mayRemove = [] for (var i = 0; i < styles.length; i++) { var item = styles[i] var domStyle = stylesInDom[item.id] domStyle.refs-- mayRemove.push(domStyle) } if (newList) { styles = listToStyles(parentId, newList) addStylesToDom(styles) } else { styles = [] } for (var i = 0; i < mayRemove.length; i++) { var domStyle = mayRemove[i] if (domStyle.refs === 0) { for (var j = 0; j < domStyle.parts.length; j++) { domStyle.parts[j]() } delete stylesInDom[domStyle.id] } } } } function addStylesToDom (styles /* Array<StyleObject> */) { for (var i = 0; i < styles.length; i++) { var item = styles[i] var domStyle = stylesInDom[item.id] if (domStyle) { domStyle.refs++ for (var j = 0; j < domStyle.parts.length; j++) { domStyle.parts[j](item.parts[j]) } for (; j < item.parts.length; j++) { domStyle.parts.push(addStyle(item.parts[j])) } if (domStyle.parts.length > item.parts.length) { domStyle.parts.length = item.parts.length } } else { var parts = [] for (var j = 0; j < item.parts.length; j++) { parts.push(addStyle(item.parts[j])) } stylesInDom[item.id] = { id: item.id, refs: 1, parts: parts } } } } function createStyleElement () { var styleElement = document.createElement('style') styleElement.type = 'text/css' head.appendChild(styleElement) return styleElement } function addStyle (obj /* StyleObjectPart */) { var update, remove var styleElement = document.querySelector('style[' + ssrIdKey + '~="' + obj.id + '"]') if (styleElement) { if (isProduction) { // has SSR styles and in production mode. // simply do nothing. return noop } else { // has SSR styles but in dev mode. // for some reason Chrome can't handle source map in server-rendered // style tags - source maps in <style> only works if the style tag is // created and inserted dynamically. So we remove the server rendered // styles and inject new ones. styleElement.parentNode.removeChild(styleElement) } } if (isOldIE) { // use singleton mode for IE9. var styleIndex = singletonCounter++ styleElement = singletonElement || (singletonElement = createStyleElement()) update = applyToSingletonTag.bind(null, styleElement, styleIndex, false) remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true) } else { // use multi-style-tag mode in all other cases styleElement = createStyleElement() update = applyToTag.bind(null, styleElement) remove = function () { styleElement.parentNode.removeChild(styleElement) } } update(obj) return function updateStyle (newObj /* StyleObjectPart */) { if (newObj) { if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) { return } update(obj = newObj) } else { remove() } } } var replaceText = (function () { var textStore = [] return function (index, replacement) { textStore[index] = replacement return textStore.filter(Boolean).join('\n') } })() function applyToSingletonTag (styleElement, index, remove, obj) { var css = remove ? '' : obj.css if (styleElement.styleSheet) { styleElement.styleSheet.cssText = replaceText(index, css) } else { var cssNode = document.createTextNode(css) var childNodes = styleElement.childNodes if (childNodes[index]) styleElement.removeChild(childNodes[index]) if (childNodes.length) { styleElement.insertBefore(cssNode, childNodes[index]) } else { styleElement.appendChild(cssNode) } } } function applyToTag (styleElement, obj) { var css = obj.css var media = obj.media var sourceMap = obj.sourceMap if (media) { styleElement.setAttribute('media', media) } if (options.ssrId) { styleElement.setAttribute(ssrIdKey, obj.id) } if (sourceMap) { // https://developer.chrome.com/devtools/docs/javascript-debugging // this makes source maps inside style tags work properly in Chrome css += '\n/*# sourceURL=' + sourceMap.sources[0] + ' */' // http://stackoverflow.com/a/26603875 css += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */' } if (styleElement.styleSheet) { styleElement.styleSheet.cssText = css } else { while (styleElement.firstChild) { styleElement.removeChild(styleElement.firstChild) } styleElement.appendChild(document.createTextNode(css)) } } /***/ }), /* 7 */ /***/ (function(module, exports) { /** * Translates the list format produced by css-loader into something * easier to manipulate. */ module.exports = function listToStyles (parentId, list) { var styles = [] var newStyles = {} for (var i = 0; i < list.length; i++) { var item = list[i] var id = item[0] var css = item[1] var media = item[2] var sourceMap = item[3] var part = { id: parentId + ':' + i, css: css, media: media, sourceMap: sourceMap } if (!newStyles[id]) { styles.push(newStyles[id] = { id: id, parts: [part] }) } else { newStyles[id].parts.push(part) } } return styles } /***/ }), /* 8 */ /***/ (function(module, exports) { /* globals __VUE_SSR_CONTEXT__ */ // IMPORTANT: Do NOT use ES2015 features in this file. // This module is a runtime utility for cleaner component module output and will // be included in the final webpack user bundle. module.exports = function normalizeComponent ( rawScriptExports, compiledTemplate, functionalTemplate, injectStyles, scopeId, moduleIdentifier /* server only */ ) { var esModule var scriptExports = rawScriptExports = rawScriptExports || {} // ES6 modules interop var type = typeof rawScriptExports.default if (type === 'object' || type === 'function') { esModule = rawScriptExports scriptExports = rawScriptExports.default } // Vue.extend constructor export interop var options = typeof scriptExports === 'function' ? scriptExports.options : scriptExports // render functions if (compiledTemplate) { options.render = compiledTemplate.render options.staticRenderFns = compiledTemplate.staticRenderFns options._compiled = true } // functional template if (functionalTemplate) { options.functional = true } // scopedId if (scopeId) { options._scopeId = scopeId } var hook if (moduleIdentifier) { // server build hook = function (context) { // 2.3 injection context = context || // cached call (this.$vnode && this.$vnode.ssrContext) || // stateful (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__ } // inject component styles if (injectStyles) { injectStyles.call(this, context) } // register component module identifier for async chunk inferrence if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier) } } // used by ssr in case component is cached and beforeCreate // never gets called options._ssrRegister = hook } else if (injectStyles) { hook = injectStyles } if (hook) { var functional = options.functional var existing = functional ? options.render : options.beforeCreate if (!functional) { // inject component registration as beforeCreate hook options.beforeCreate = existing ? [].concat(existing, hook) : [hook] } else { // for template-only hot-reload because in that case the render fn doesn't // go through the normalizer options._injectStyles = hook // register for functioal component in vue file options.render = function renderWithStyleInjection (h, context) { hook.call(context) return existing(h, context) } } } return { esModule: esModule, exports: scriptExports, options: options } } /***/ }), /* 9 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h return _c("div", { staticClass: "z_barrage-container" }, [ _c("canvas", { ref: "canvasContainer", staticStyle: { display: "none" }, attrs: { width: _vm.containerWidth, height: _vm.containerHeight } }), _vm._v(" "), _c( "div", { staticClass: "z_container", style: { height: _vm.containerHeight / 2 + "px" } }, [ _c("canvas", { ref: "canvas", staticClass: "z_barrage", style: { width: _vm.containerWidth / 2 + "px", height: _vm.containerHeight / 2 + "px" }, attrs: { id: "canvas", width: _vm.containerWidth, height: _vm.containerHeight } }) ] ) ]) } var staticRenderFns = [] render._withStripped = true var esExports = { render: render, staticRenderFns: staticRenderFns } /* harmony default export */ __webpack_exports__["a"] = (esExports); if (false) { module.hot.accept() if (module.hot.data) { require("vue-hot-reload-api") .rerender("data-v-2ac013d2", esExports) } } /***/ }) /******/ ]); }); //# sourceMappingURL=barrage.min.js.map