├── .gitignore
├── .DS_Store
├── favicon.ico
├── 截图
├── whiteboard-3.png
├── whiteboard-1.jpeg
└── whiteboard-2.jpeg
├── public
├── images
│ ├── delPic.png
│ ├── white_bg.png
│ ├── cursors
│ │ ├── pan.png
│ │ ├── arrow.png
│ │ ├── circle.png
│ │ ├── eraser.png
│ │ ├── fill.png
│ │ ├── line.png
│ │ ├── move.png
│ │ ├── path.png
│ │ ├── stroke.png
│ │ ├── text.png
│ │ ├── rectangle.png
│ │ └── strokeColor.png
│ └── whiteboard_bg.png
├── js
│ ├── ImageCache.js
│ ├── UUID.js
│ ├── qbian.js
│ ├── Canvas.js
│ └── index.js
├── index.html
└── css
│ └── whiteBoard.css
├── package.json
├── src
└── ws.js
├── server.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | /node_modules
3 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/.DS_Store
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/favicon.ico
--------------------------------------------------------------------------------
/截图/whiteboard-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/截图/whiteboard-3.png
--------------------------------------------------------------------------------
/截图/whiteboard-1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/截图/whiteboard-1.jpeg
--------------------------------------------------------------------------------
/截图/whiteboard-2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/截图/whiteboard-2.jpeg
--------------------------------------------------------------------------------
/public/images/delPic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/delPic.png
--------------------------------------------------------------------------------
/public/images/white_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/white_bg.png
--------------------------------------------------------------------------------
/public/images/cursors/pan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/pan.png
--------------------------------------------------------------------------------
/public/images/cursors/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/arrow.png
--------------------------------------------------------------------------------
/public/images/cursors/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/circle.png
--------------------------------------------------------------------------------
/public/images/cursors/eraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/eraser.png
--------------------------------------------------------------------------------
/public/images/cursors/fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/fill.png
--------------------------------------------------------------------------------
/public/images/cursors/line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/line.png
--------------------------------------------------------------------------------
/public/images/cursors/move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/move.png
--------------------------------------------------------------------------------
/public/images/cursors/path.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/path.png
--------------------------------------------------------------------------------
/public/images/cursors/stroke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/stroke.png
--------------------------------------------------------------------------------
/public/images/cursors/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/text.png
--------------------------------------------------------------------------------
/public/images/whiteboard_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/whiteboard_bg.png
--------------------------------------------------------------------------------
/public/images/cursors/rectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/rectangle.png
--------------------------------------------------------------------------------
/public/images/cursors/strokeColor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Qbian61/whiteBoard/HEAD/public/images/cursors/strokeColor.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "whiteboard",
3 | "version": "1.0.0",
4 | "description": "白板",
5 | "author": "Qbian",
6 | "license": "ISC",
7 | "main": "server.js",
8 | "scripts": {
9 | "start": "node server.js"
10 | },
11 | "dependencies": {
12 | "express": "^4.15.4",
13 | "socket.io": "^2.0.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ws.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(socket) {
3 |
4 | socket.on('disconnect', function(e) {
5 | console.info(e);
6 | });
7 |
8 | socket.on('drawing', function(obj) {
9 | socket.emit('drawing', obj);
10 | socket.broadcast.emit('drawing', obj);
11 | });
12 |
13 | socket.on('clearAll', function(obj) {
14 | socket.emit('clearAll', obj);
15 | socket.broadcast.emit('clearAll', obj);
16 | });
17 |
18 | };
19 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const http = require('http').Server(app);
4 | const io = require('socket.io')(http);
5 |
6 | const ws = require('./src/ws');
7 |
8 | io.on('connection', ws);
9 |
10 | const port = 8082;
11 |
12 | app.use(express.static('public'));
13 |
14 | app.get('/', function (req, res){
15 | res.sendFile('/index.html');
16 | });
17 |
18 | app.get('/json', function (req, res) {
19 | res.send({name: 'lily'});
20 | });
21 |
22 | http.listen(port, function () {
23 | console.log(`listening on :${port}`);
24 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # whiteBoard
2 |
3 | 可访问地址: http://42.51.0.196:8082/
4 |
5 | 功能列表:
6 |
7 | - 改变线条颜色
8 | - 改变线条宽度
9 | - 画笔
10 | - 直线
11 | - 画矩形
12 | - 画圆
13 | - 填写文本
14 | - 橡皮擦
15 | - 撤销上一步
16 | - 清空画板
17 | - 上传画板背景图片,图片仓库管理。
18 | - 访问地址后跟上查询参数 ?id=roomId 可进入自定义房间免受其他用户干扰
19 | - 画布等比缩放
20 |
21 | # 效果图
22 |
23 | 
24 |
25 | 
26 |
27 | 
28 |
29 | # 启动步骤
30 |
31 | ```
32 | git clone https://github.com/Qbian61/whiteBoard.git
33 |
34 | cd whiteBoard
35 |
36 | npm install
37 |
38 | node server.js
39 | ```
40 |
41 | 启动成功后访问 ```http://127.0.0.1:8082``` 即可。
42 |
--------------------------------------------------------------------------------
/public/js/ImageCache.js:
--------------------------------------------------------------------------------
1 | window.qbian = window.qbian || {};
2 |
3 | window.qbian.ImageCache = (function(qbian) {
4 | 'use strict';
5 |
6 | var localCache = [], imgBox,
7 | imgNode = '
'+
8 | ''+
9 | ''+
10 | '
';
11 |
12 | function ImageCache(pathname, imgBx) {
13 | this.key = pathname || 'undefined';
14 | var cache = localStorage.getItem(this.key);
15 | localCache = cache && cache != '[]' ? JSON.parse(cache) : [new qbian.Image(qbian.config.whiteBgImgUrl)];
16 | imgBox = imgBx;
17 |
18 | reShow();
19 | }
20 |
21 | ImageCache.prototype = {
22 | getImages: function() {
23 | return localCache;
24 | },
25 | addImage: function(image) {
26 | for(var i = 0, len = localCache.length; i < len; ++ i) {
27 | if(image.url === localCache[i].url) {
28 | return;
29 | }
30 | }
31 | imgBox.innerHTML += imgNode.replace(/\{\{url\}\}/g, image.url);
32 | localCache.push(image);
33 | localStorage.setItem(this.key, JSON.stringify(localCache));
34 | window.qbian.bus.emit('updateImages');
35 | },
36 | removeImage: function(deleteImage) {
37 | for(var i = 0, len = localCache.length; i < len; ++ i) {
38 | if(deleteImage.url === localCache[i].url) {
39 | localCache.splice(i, 1);
40 | localStorage.setItem(this.key, JSON.stringify(localCache));
41 | reShow();
42 | window.qbian.bus.emit('updateImages');
43 | break;
44 | }
45 | }
46 | }
47 | };
48 |
49 | // ==========================================================================
50 | // ==========================================================================
51 | // ==========================================================================
52 |
53 | function reShow() {
54 | var html = '';
55 | imgBox.innerHTML = '';
56 | for(var i = 0, len = localCache.length; i < len; ++ i) {
57 | html += imgNode.replace(/\{\{url\}\}/g, localCache[i].url);
58 | }
59 | imgBox.innerHTML += html;
60 | }
61 |
62 |
63 | return ImageCache;
64 | })(window.qbian);
65 |
--------------------------------------------------------------------------------
/public/js/UUID.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;n="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,n.uuid=e()}}(function(){return function e(n,r,o){function t(f,u){if(!r[f]){if(!n[f]){var a="function"==typeof require&&require;if(!u&&a)return a(f,!0);if(i)return i(f,!0);var d=new Error("Cannot find module '"+f+"'");throw d.code="MODULE_NOT_FOUND",d}var s=r[f]={exports:{}};n[f][0].call(s.exports,function(e){var r=n[f][1][e];return t(r?r:e)},s,s.exports,e,n,r,o)}return r[f].exports}for(var i="function"==typeof require&&require,f=0;f>>((3&n)<<3)&255;return i}}n.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],4:[function(e,n,r){function o(e,n,r){var o=n&&r||0,t=n||[];e=e||{};var f=void 0!==e.clockseq?e.clockseq:a,l=void 0!==e.msecs?e.msecs:(new Date).getTime(),c=void 0!==e.nsecs?e.nsecs:s+1,v=l-d+(c-s)/1e4;if(v<0&&void 0===e.clockseq&&(f=f+1&16383),(v<0||l>d)&&void 0===e.nsecs&&(c=0),c>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");d=l,s=c,a=f,l+=122192928e5;var p=(1e4*(268435455&l)+c)%4294967296;t[o++]=p>>>24&255,t[o++]=p>>>16&255,t[o++]=p>>>8&255,t[o++]=255&p;var y=l/4294967296*1e4&268435455;t[o++]=y>>>8&255,t[o++]=255&y,t[o++]=y>>>24&15|16,t[o++]=y>>>16&255,t[o++]=f>>>8|128,t[o++]=255&f;for(var b=e.node||u,w=0;w<6;++w)t[o+w]=b[w];return n?n:i(t)}var t=e("./lib/rng"),i=e("./lib/bytesToUuid"),f=t(),u=[1|f[0],f[1],f[2],f[3],f[4],f[5]],a=16383&(f[6]<<8|f[7]),d=0,s=0;n.exports=o},{"./lib/bytesToUuid":2,"./lib/rng":3}],5:[function(e,n,r){function o(e,n,r){var o=n&&r||0;"string"==typeof e&&(n="binary"==e?new Array(16):null,e=null),e=e||{};var f=e.random||(e.rng||t)();if(f[6]=15&f[6]|64,f[8]=63&f[8]|128,n)for(var u=0;u<16;++u)n[o+u]=f[u];return n||i(f)}var t=e("./lib/rng"),i=e("./lib/bytesToUuid");n.exports=o},{"./lib/bytesToUuid":2,"./lib/rng":3}]},{},[1])(1)});
2 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | qbian-白板
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | - 2
48 | - 4
49 | - 6
50 | - 8
51 | - 10
52 | - 12
53 | - 20
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/public/js/qbian.js:
--------------------------------------------------------------------------------
1 | window.qbian = window.qbian || {};
2 |
3 | (function(qbian) {
4 | 'use strict';
5 |
6 | // ========================= 消息 实体类 ========================
7 |
8 | // 线条类
9 | qbian.Line = function(lineColor, lineWidth, points) {
10 | this.type = 'line';
11 | this.id = uuid.v1();
12 | this.time = Date.now();
13 | this.lineColor = lineColor;
14 | this.lineWidth = lineWidth;
15 | this.points = points;
16 | };
17 |
18 | // 直线类
19 | qbian.StraightLine = function(lineColor, lineWidth, startDot, endDot) {
20 | this.type = 'straightLine';
21 | this.id = uuid.v1();
22 | this.time = Date.now();
23 | this.lineColor = lineColor;
24 | this.lineWidth = lineWidth;
25 | this.startDot = startDot;
26 | this.endDot = endDot;
27 | };
28 |
29 | // 矩形类
30 | qbian.Rect = function(lineColor, lineWidth, startDot, endDot, sideLength) {
31 | this.type = 'rect';
32 | this.id = uuid.v1();
33 | this.time = Date.now();
34 | this.lineColor = lineColor;
35 | this.lineWidth = lineWidth;
36 | this.startDot = startDot;
37 | this.endDot = endDot;
38 | this.sideLength = sideLength;
39 | };
40 |
41 | // 圆形类
42 | qbian.Round = function(lineColor, lineWidth, startDot, endDot, sideLength, r) {
43 | this.type = 'round';
44 | this.id = uuid.v1();
45 | this.time = Date.now();
46 | this.lineColor = lineColor;
47 | this.lineWidth = lineWidth;
48 | this.startDot = startDot;
49 | this.endDot = endDot;
50 | this.sideLength = sideLength;
51 | this.r = r;
52 | };
53 |
54 | // 文本类
55 | qbian.Text = function(lineColor, lineWidth, content, startDot) {
56 | this.type = 'text';
57 | this.id = uuid.v1();
58 | this.time = Date.now();
59 | this.lineColor = lineColor;
60 | this.lineWidth = lineWidth;
61 | this.content = content;
62 | this.startDot = startDot;
63 | };
64 |
65 | // 背景图
66 | qbian.Image = function(url) {
67 | this.type = 'image';
68 | this.id = uuid.v1();
69 | this.time = Date.now();
70 | this.url = url;
71 | };
72 |
73 | // 删除背景图
74 | qbian.DeleteImage = function(url) {
75 | this.type = 'deleteImage';
76 | this.id = uuid.v1();
77 | this.time = Date.now();
78 | this.url = url;
79 | };
80 |
81 | // 删除
82 | qbian.Delete = function(id) {
83 | this.type = 'delete';
84 | this.id = id;
85 | };
86 |
87 | // ========================= 功能类 对象 ========================
88 |
89 | // 观察者模式
90 | function Event() {
91 | this._events = {};
92 | }
93 | Event.prototype = {
94 | emit: function(name, obj) {
95 | if(name in this._events) {
96 | for(var i = 0, len = this._events[name].length; i < len; ++ i) {
97 | this._events[name][i](obj);
98 | }
99 | }
100 | },
101 | on: function(name, callback) {
102 | if(!(name in this._events)) this._events[name] = [];
103 | this._events[name].push(callback);
104 | },
105 | remove: function(name, callback) {
106 | this._events[name] ? this._events[name].splice(this._events[name].indexOf(callback), 1) : void 0;
107 | },
108 | removeAll: function(name) {
109 | this._events[name] = [];
110 | }
111 | };
112 |
113 | // 用于不同闭包内之间消息通讯
114 | qbian.bus = new Event();
115 |
116 | // 日志
117 | qbian.logger = function(moduleName) {
118 | return function() {
119 | if(!qbian.config.outputLog) return;
120 | var args = Array.prototype.slice.call(arguments, 0);
121 | console.info('【' + new Date().toLocaleString() + '】[' + moduleName + ']', ...args);
122 | }
123 | };
124 |
125 | // 是否是网络图片地址
126 | var allPicTypes = ['jpg', 'png', 'jpeg'];
127 | qbian.isNetWorkPicUrl = function(strPicUrl) {
128 | // && strPicUrl.indexOf('.') > 0
129 | if(strPicUrl.indexOf('http') === 0 ) {
130 | // var picUrls = strPicUrl.split('.');
131 | // return allPicTypes.indexOf(picUrls[picUrls.length - 1]) > -1;
132 | return true
133 | }
134 | return false;
135 | };
136 |
137 | // ========================== 获取页面 id
138 | var search = location.search,
139 | pathname = '';
140 | if(search && search.indexOf('=') > 0) {
141 | var querys = search.substr(1, search.length).split('&');
142 | for(var i = 0, len = querys.length; i < len; ++ i) {
143 | if(querys[i].split('=')[0] === 'id') {
144 | pathname = querys[i].split('=')[1];
145 | break;
146 | }
147 | }
148 | }
149 |
150 | // 配置信息
151 | qbian.config = {
152 | wsUrl: 'ws://127.0.0.1:8082',
153 | whiteBgImgUrl: 'http://127.0.0.1:8082/images/white_bg.png',
154 | outputLog: true,
155 | pathname: pathname
156 | };
157 |
158 |
159 | })(window.qbian);
160 |
--------------------------------------------------------------------------------
/public/js/Canvas.js:
--------------------------------------------------------------------------------
1 |
2 | window.qbian = window.qbian || {};
3 | window.qbian.Canvas = (function(qbian) {
4 | 'use strict';
5 |
6 | var canvas,
7 | context,
8 | cvsW,
9 | cvsH,
10 | container = {
11 | selected: {
12 | id: '',
13 | extensionWidth: 1,
14 | lineColor: 'red'//'#00ff00'
15 | },
16 | session: {}
17 | };
18 |
19 | function Canvas(cvs, ctx) {
20 | canvas = cvs;
21 | context = ctx;
22 | cvsW = cvs.width;
23 | cvsH = cvs.height;
24 | }
25 |
26 | // ========================== 获取页面 id
27 |
28 | // 绘制图形
29 | Canvas.prototype = {
30 | drawing: function(d) {
31 | if(d.type === 'delete') {
32 | delete container.session[d.id];
33 | } else {
34 | container.session[d.id] = d;
35 | }
36 |
37 | redraw(); // 重新绘制画布
38 | },
39 | // 清空 画板
40 | clearAll: function(d) {
41 | canvas.width = canvas.width;
42 | container.session = {};
43 | },
44 | setWH: function(w, h) {
45 | cvsW = w;
46 | cvsH = h;
47 | },
48 | getContainer: function() {
49 | return container;
50 | },
51 | getLastOptId: function() {
52 | var lastTime = 0, id = '';
53 | for(var key in container.session) {
54 | if(lastTime < container.session[key].time) {
55 | lastTime = container.session[key].time;
56 | id = container.session[key].id;
57 | }
58 | }
59 | return id;
60 | },
61 | redraw: redraw
62 | };
63 |
64 | // ============================================================================================
65 | // ============================================================================================
66 | // ============================================================================================
67 |
68 | function redraw() {
69 | context.clearRect(0, 0, canvas.width, canvas.height);
70 | var o, lineColor, lineWidth;
71 | for(var key in container.session) {
72 | o = container.session[key];
73 | lineColor = o.lineColor;
74 | lineWidth = o.lineWidth;
75 | if(container.selected.id === o.id) {
76 | lineColor = container.selected.lineColor;
77 | lineWidth = o.lineWidth * 1 + container.selected.extensionWidth;
78 | }
79 | switch(o.type) {
80 | case 'line' : { // 线条
81 | if(o.points.length < 2) break;
82 |
83 | context.beginPath(); // 起始一条路径,或重置当前路径
84 | context.moveTo(o.points[0].x*cvsW, o.points[0].y*cvsH); // 把路径移动到画布中的指定点,不创建线条
85 | for(var i = 1, len = o.points.length; i < len; i ++) {
86 | context.lineTo(o.points[i].x*cvsW, o.points[i].y*cvsH); // 添加一个新点,然后在画布中创建从该点到最后指定点的线条
87 | }
88 | context.strokeStyle = lineColor; // 设置或返回用于笔触的颜色、渐变或模式
89 | context.lineWidth = lineWidth; // 设置或返回当前的线条宽度
90 | context.stroke(); // 绘制已定义的路径
91 | context.closePath(); // 创建从当前点回到起始点的路径
92 | break;
93 | }
94 | case 'straightLine' : { // 直线
95 | context.beginPath();
96 | context.moveTo(o.startDot.x*cvsW, o.startDot.y*cvsH);
97 | context.lineTo(o.endDot.x*cvsW, o.endDot.y*cvsH);
98 | context.strokeStyle = lineColor;
99 | context.lineWidth = lineWidth;
100 | context.stroke();
101 | context.closePath();
102 | break;
103 | }
104 | case 'rect' : { // 矩形
105 | context.beginPath();
106 | context.rect(o.startDot.x*cvsW, o.startDot.y*cvsH, o.endDot.x*cvsW - o.startDot.x*cvsW, o.endDot.y*cvsH - o.startDot.y*cvsH);
107 | context.strokeStyle = lineColor;
108 | context.lineWidth = lineWidth;
109 | // context.fill();
110 | context.stroke();
111 | break;
112 | }
113 | case 'round' : { // 圆形
114 | context.beginPath();
115 | context.arc(o.startDot.x*cvsW, o.startDot.y*cvsH, o.r*(cvsW/cvsH), 0, 2 * Math.PI);
116 | context.strokeStyle = lineColor;
117 | context.lineWidth = lineWidth;
118 | context.stroke();
119 | break;
120 | }
121 | case 'text' : { // 文本
122 | context.font = parseInt(lineWidth) * 10 + "px serif";
123 | context.fillStyle = lineColor;
124 | context.fillText(o.content, o.startDot.x*cvsW, o.startDot.y*cvsH);
125 | break;
126 | }
127 | default: continue;
128 | }
129 | }
130 | }
131 |
132 | return Canvas;
133 | })(window.qbian);
134 |
--------------------------------------------------------------------------------
/public/css/whiteBoard.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | border: 0;
4 | margin: 0;
5 | box-sizing: border-box;
6 | }
7 | body {
8 | padding: 0;
9 | border: 0;
10 | margin: 0;
11 | width: 100%;
12 | height: 100%;
13 | }
14 | .board ul,.board li{
15 | list-style: none;
16 | }
17 | .board{
18 | width: 100%;
19 | height: 100%;
20 | position: absolute;
21 | overflow: hidden;
22 | }
23 | .board .canvas{
24 | border: 1px solid #4e4e4e;
25 | /* margin: 0 auto; */
26 | }
27 | .board .opts{
28 | width: 360px;
29 | left: 50%;
30 | transform: translateX(-50%);
31 | position: fixed;
32 | bottom: 20px;
33 | }
34 | .board .opts-layer{
35 | width: 362px;
36 | height: 32px;
37 | line-height: 32px;
38 | background-color: rgba(0,0,0,0.64);
39 | border-radius: 5px;
40 | margin: 0 auto;
41 | padding-left: 7px;
42 | }
43 | .board .opt{
44 | width: 34px;
45 | float: left;
46 | height: 100%;
47 | border-right: 1px solid #4e4e4e;
48 | background: url("../images/whiteboard_bg.png") no-repeat;
49 | }
50 | .board .opt:last-child{
51 | border: none;
52 | }
53 | .board .opt:hover{
54 | background-color: rgba(0,0,0,0.1);
55 | cursor: pointer;
56 | }
57 | .board .setting{
58 | min-width: 36px;
59 | float: left;
60 | height: 100%;
61 | border-right: 1px solid #4e4e4e;
62 | position: relative;
63 | padding-right: 12px;
64 | }
65 | .board .setColor{
66 | width: 13px;
67 | height: 13px;
68 | border: 2px solid #fff;
69 | border-radius: 50%;
70 | vertical-align: middle;
71 | display: inline-block;
72 | cursor: pointer;
73 | margin-right: 6px;
74 | }
75 | .board .setSize{
76 | color: #fff;
77 | display: inline-block;
78 | font-size: 12px;
79 | font-family: arial;
80 | cursor: pointer;
81 | width: 9px;
82 | text-align: center;
83 | }
84 | .board .line{
85 | background-position: 0px 0px;
86 | }
87 | .board .straightLine{
88 | background-position: -34px 0px;
89 | }
90 | .board .rect{
91 | background-position: -68px 0px;
92 | }
93 | .board .round{
94 | background-position: -104px 0px;
95 | }
96 | .board .text{
97 | background-position: -136px 0px;
98 | }
99 | .board .delete{
100 | background-position: -168px 0px;
101 | }
102 | .board .back{
103 | background-position: -200px 0px;
104 | }
105 | .board .clear{
106 | background-position: -234px 0px;
107 | }
108 | .board .setImageNo{
109 | background: none;
110 | position: relative;
111 | }
112 | .board .setImagePic{
113 | background-position: -269px 0px;
114 | position: relative;
115 | }
116 | .board .setImage-layer {
117 | position: absolute;
118 | width: 400px;
119 | height: 220px;
120 | left: -186px;
121 | bottom: 47px;
122 | border: 1px solid #bebebe;
123 | border-radius: 3px;
124 | box-shadow: 0px 1px 7px #b9b9b9;
125 | background-color: #fff;
126 | display: none;
127 | }
128 | .imgBox{
129 | width: 100%;
130 | height: 172px;
131 | overflow-y: auto;
132 | padding-top: 5px;
133 | }
134 | .img-opt {
135 | border-bottom: 1px solid #b9b9b9;
136 | width: 100%;
137 | height: 35px;
138 | }
139 | .image-item-box {
140 | position: relative;
141 | display: block;
142 | float: left;
143 | }
144 | .image-item-delete{
145 | position: absolute;
146 | top: -2px;
147 | right: -3px;
148 | display: none;
149 | width: 22px;
150 | height: 23px;
151 | background: url("../images/delPic.png") no-repeat;
152 | background-position: 0px 0px;
153 | }
154 | .image-item {
155 | display:block;
156 | width:50px;
157 | height:50px;
158 | float:left;
159 | margin:5px 0 0 4px;
160 | }
161 | .image-item:hover{
162 | border: 1px solid #b9b9b9;
163 | }
164 |
165 | .board .setColor-layer,
166 | .board .setSize-layer{
167 | position: absolute;
168 | width: 160px;
169 | height: 27px;
170 | left:-70px;
171 | top:-40px;
172 | border: 1px solid #bebebe;
173 | border-radius: 3px;
174 | box-shadow: 0px 1px 7px #b9b9b9;
175 | background-color: #fff;
176 | display: none;
177 | }
178 | .board .setSize-layer{
179 | left: -48px;
180 | }
181 | .board .setImage-layer::after,
182 | .board .setColor-layer::after,
183 | .board .setSize-layer::after{
184 | content: "";
185 | width: 10px;
186 | height: 10px;
187 | border-bottom: 1px solid #bebebe;
188 | border-right:1px solid #bebebe;
189 | position: absolute;
190 | left:70px;
191 | bottom: -6px;
192 | background-color: #fff;
193 | transform: rotate(45deg);
194 | }
195 | .board .setImage-layer::after{
196 | left: 195px;
197 | }
198 | .board .setColor-layer li{
199 | width: 14px;
200 | height: 14px;
201 | border-radius: 50%;
202 | float: left;
203 | margin: 6px 6px 3px;
204 | }
205 | .board .setSize-layer li{
206 | float: left;
207 | line-height:27px;
208 | color: #9e9e9e;
209 | font-size: 12px;
210 | text-align: center;
211 | margin-left: 11px;
212 | }
213 | .board .setSize-layer li:hover{
214 | cursor: pointer;
215 | color: #333;
216 | }
217 | .board .setColor-layer{
218 | height: 73px;
219 | top: -85px;
220 | }
221 | .board .setColor-layer::after{
222 | top: 66px;
223 | }
224 | .board .setColor-layer .col_red{
225 | cursor: pointer;
226 | }
227 | .file-path{
228 | width: 288px;
229 | height: 30px;
230 | margin-left: 2px;
231 | }
232 | .file-path-btn {
233 | width: 99px;
234 | height: 30px;
235 | cursor: pointer;
236 | }
237 | .canvas-line {
238 | cursor: url('../images/cursors/path.png'), auto;
239 | }
240 | .canvas-straightLine {
241 | cursor: url('../images/cursors/line.png'), auto;
242 | }
243 | .canvas-rect {
244 | cursor: url('../images/cursors/rectangle.png'), auto;
245 | }
246 | .canvas-round {
247 | cursor: url('../images/cursors/circle.png'), auto;
248 | }
249 | .canvas-text{
250 | cursor: url('../images/cursors/text.png'), auto;
251 | }
252 | .canvas-delete {
253 | cursor: url('../images/cursors/eraser.png'), auto;
254 | }
255 | .setImage-layer{
256 |
257 | }
--------------------------------------------------------------------------------
/public/js/index.js:
--------------------------------------------------------------------------------
1 | window.qbian = window.qbian || {};
2 |
3 | (function(qbian) {
4 | 'use strict';
5 |
6 | // 自定义日志
7 | var log = window.qbian.logger('index');
8 |
9 | // ========================== 获取页面 id
10 | var pathname = window.qbian.config.pathname;
11 |
12 | var canvasDom = document.getElementById('canvas'),
13 | cvsW, cvsH,
14 | canvas = new window.qbian.Canvas(canvasDom, canvasDom.getContext('2d')),
15 | imageCache = new window.qbian.ImageCache(pathname, document.getElementById('imgBox')),
16 | socket = io.connect(qbian.config.wsUrl);
17 |
18 | log('id=' +pathname+ ' 缓存的本地图片:', imageCache.getImages());
19 |
20 | window.qbian.socket = socket;
21 |
22 | socket.on('clearAll', function(d) {
23 | if(pathname !== d.pathname) return;
24 | canvas.clearAll(d);
25 | });
26 |
27 | socket.on('drawing', function(d) {
28 | if(pathname !== d.pathname) return;
29 | if(d.type === 'image') {
30 | canvasDom.style.background = 'url('+ d.url +') no-repeat center';
31 | canvasDom.style.backgroundSize = 'contain';
32 | return imageCache.addImage(d);
33 | }
34 | if(d.type === 'deleteImage') {
35 | return imageCache.removeImage(d);
36 | }
37 | canvas.drawing(d);
38 | });
39 |
40 | // ========================== 清空画板
41 | document.getElementById('clearAll').addEventListener('click', function() {
42 | socket.emit('clearAll', {pathname});
43 | }, false);
44 |
45 | // ========================== 改变线条颜色、更新画笔颜色
46 | var lineColor = '#000',
47 | setColorDom = document.getElementById('setColor'),
48 | colorDom = document.getElementById('color'),
49 | colorDoms = document.getElementsByClassName('color-item');
50 | setColorDom.style.backgroundColor = lineColor;
51 |
52 | setColorDom.addEventListener('click', function(e) {
53 | colorDom.style.display = colorDom.style.display === 'block' ? 'none' : 'block';
54 | }, false);
55 |
56 | for (var i = 0; i < colorDoms.length; i++){
57 | colorDoms[i].addEventListener('click', function(e) {
58 | lineColor = e.target.style.backgroundColor;
59 | setColorDom.style.backgroundColor = lineColor;
60 | hideAll();
61 | }, false);
62 | }
63 |
64 | // ========================== 改变线条粗细
65 | var lineWidth = 2,
66 | setLineWidth = document.getElementById('setLineWidth'),
67 | lineWidthDom = document.getElementById('lineWidth'),
68 | lineDoms = document.getElementsByClassName('line-item');
69 | setLineWidth.innerHTML = lineWidth;
70 |
71 | setLineWidth.addEventListener('click', function() {
72 | lineWidthDom.style.display = lineWidthDom.style.display === 'block' ? 'none' : 'block';
73 | }, false);
74 |
75 | for (var i = 0; i < lineDoms.length; i++){
76 | lineDoms[i].addEventListener('click', function(e) {
77 | lineWidth = e.target.innerHTML;
78 | setLineWidth.innerHTML = lineWidth;
79 | hideAll();
80 | }, false);
81 | }
82 |
83 | // ========================== 线条
84 | var polygon = 0;
85 | document.getElementById('line').addEventListener('click', function() {
86 | polygon = 0;
87 | delet = false;
88 | className = 'canvas-line';
89 | updateCursor();
90 | }, false);
91 |
92 | // ========================== 直线
93 | document.getElementById('straightLine').addEventListener('click', function() {
94 | polygon = 2;
95 | delet = false;
96 | className = 'canvas-straightLine';
97 | updateCursor();
98 | }, false);
99 |
100 | // ========================== 矩形
101 | document.getElementById('rect').addEventListener('click', function() {
102 | polygon = 4;
103 | delet = false;
104 | className = 'canvas-rect';
105 | updateCursor();
106 | }, false);
107 |
108 | // ========================== 圆形
109 | document.getElementById('round').addEventListener('click', function() {
110 | polygon = 100;
111 | delet = false;
112 | className = 'canvas-round';
113 | updateCursor();
114 | }, false);
115 |
116 | // ========================== 文本
117 | var content = '';
118 | document.getElementById('text').addEventListener('click', function() {
119 | content = prompt('在下面输入你想写的内容');
120 | if(!content) return content = '';
121 | updateCursor('canvas-text');
122 | log('输入的文本内容', content);
123 | }, false);
124 |
125 | // ========================== 在 canvas 内,按下鼠标事件
126 | var drawing = false;
127 | canvasDom.addEventListener('mousedown', function(event) {
128 | hideAll();
129 |
130 | // 删除
131 | if(delet && container.selected.id) {
132 | return emit(new qbian.Delete(container.selected.id));
133 | }
134 | var startDot ={
135 | x: (event.clientX+document.documentElement.scrollLeft+document.body.scrollLeft - canvasDom.offsetLeft)/cvsW,
136 | y: (event.clientY+document.documentElement.scrollTop+document.body.scrollTop - canvasDom.offsetTop)/cvsH
137 | }, straightLine, rect, round, line;
138 |
139 | // 文本
140 | if(content != '') {
141 | emit(new qbian.Text(lineColor, lineWidth, content, startDot));
142 | updateCursor(className);
143 | return content = '';
144 | }
145 | // 直线
146 | else if(polygon === 2) {
147 | straightLine = new qbian.StraightLine(lineColor, lineWidth, startDot, startDot);
148 | }
149 | // 矩形
150 | else if(polygon === 4) {
151 | rect = new qbian.Rect(lineColor, lineWidth, startDot, startDot, {x: 0, y: 0});
152 | }
153 | // 圆形
154 | else if(polygon === 100) {
155 | round = new qbian.Round(lineColor, lineWidth, startDot, startDot, {x: 0, y: 0}, 0);
156 | }
157 | // 线条
158 | else {
159 | line = new qbian.Line(lineColor, lineWidth, [startDot]);
160 | }
161 |
162 | drawing = true;
163 | var lastTime = Date.now();
164 | // ========================== 在 canvas 内,按下鼠标 且 移动事件
165 | window.onmousemove = function(event){
166 | if(delet) {// 点击下去,并且移动,删除操作将取消
167 | delet = false;
168 | updateCursor(tempClass);
169 | }
170 |
171 | if(!drawing) return;
172 | if((Date.now() - lastTime) < 20) return;
173 | lastTime = Date.now();
174 |
175 | event = event || window.event;
176 | var endDot = {
177 | x: (event.clientX+document.documentElement.scrollLeft+document.body.scrollLeft - canvasDom.offsetLeft)/cvsW,
178 | y: (event.clientY+document.documentElement.scrollTop+document.body.scrollTop - canvasDom.offsetTop)/cvsH
179 | };
180 | // log('onmousemove endDot ==>', endDot);
181 |
182 | // 直线
183 | if(polygon === 2) {
184 | straightLine.endDot = endDot;
185 | return emit(straightLine);
186 | }
187 | // 矩形
188 | else if(polygon === 4) {
189 | rect.endDot = endDot;
190 | rect.sideLength = {
191 | x: Math.abs(rect.endDot.x - rect.startDot.x),
192 | y: Math.abs(rect.endDot.y - rect.startDot.y)
193 | };
194 | return emit(rect);
195 | }
196 | // 圆形
197 | else if(polygon === 100) {
198 | round.endDot = endDot;
199 | round.sideLength = {
200 | x: Math.abs(endDot.x - startDot.x),
201 | y: Math.abs(endDot.y - startDot.y)
202 | };
203 | round.startDot = {
204 | x: (startDot.x + endDot.x) / 2,
205 | y: (startDot.y + endDot.y) / 2
206 | };
207 | round.r = Math.sqrt( Math.pow(round.sideLength.x*cvsW, 2) + Math.pow(round.sideLength.y*cvsH, 2) )/(cvsW/cvsH)/2;
208 | return emit(round);
209 | }
210 | // 线条
211 | else {
212 | line.points.push(endDot);
213 | emit(line);
214 | return startDot = endDot;
215 | }
216 | };
217 |
218 | // ========================== 在 canvas 内,松开鼠标事件
219 | window.onmouseup = function(event){
220 | if(!drawing) return;
221 | window.onmousemove = null;
222 | };
223 |
224 | }, false);
225 |
226 | // ========================== 移出 canvas 事件
227 | canvasDom.addEventListener('mouseout', function onMouseUp(e){
228 | if (!drawing) { return; }
229 | drawing = false;
230 | }, false);
231 |
232 |
233 | // ========================== 选中
234 | var delet = false,
235 | tempClass = 'canvas-line',
236 | container;
237 | document.getElementById('delete').addEventListener('click', function() {
238 | delet = !delet;
239 | if(delet) {
240 | container = canvas.getContainer();
241 | if(className && className != 'canvas-delete') tempClass = className;
242 | className = 'canvas-delete';
243 | } else {
244 | className = tempClass;
245 | }
246 | updateCursor();
247 | }, false);
248 |
249 | // ========================== 撤销
250 | document.getElementById('undo').addEventListener('click', function() {
251 | var id = canvas.getLastOptId();
252 | if(id) emit(new qbian.Delete(id));
253 | }, false);
254 |
255 | // ========================== 更换背景
256 | var imgDom = document.getElementById('imgs');
257 | document.getElementById('setImage').addEventListener('click', function() {
258 | imgDom.style.display = imgDom.style.display === 'block' ? 'none' : 'block';
259 | filePathDom.focus();
260 | }, false);
261 |
262 | var filePathDom = document.getElementById('filePath');
263 | document.getElementById('uploadBtn').addEventListener('click', function() {
264 | var filePath = filePathDom.value;
265 | if(qbian.isNetWorkPicUrl(filePath)) {
266 | emit(new window.qbian.Image(filePath))
267 | return filePathDom.value = '';
268 | }
269 | alert('添加的网络图片地址不在定义范围(jpg, png, jpeg)内');
270 | }, false);
271 |
272 | function imageDomClick(e) {
273 | log('更换背景图, url=' + e.target.src);
274 | e.stopPropagation();
275 | var src = e.target.src;
276 | if(!src) return;
277 | emit(new qbian.Image(src));
278 | hideAll();
279 | }
280 | function imageDltDomClick(e) {
281 | log('删除背景图, url=' + e.target.dataset.url);
282 | e.stopPropagation();
283 | imageCache.removeImage(new qbian.DeleteImage(e.target.dataset.url));
284 | }
285 | function bindImagesEv() {
286 | var imageDoms = document.getElementsByClassName('image-item');
287 | var imagedltDoms = document.getElementsByClassName('image-item-delete');
288 |
289 | for (var i = 0; i < imageDoms.length; i++){
290 | imageDoms[i].removeEventListener('click', imageDomClick, false);
291 | imageDoms[i].addEventListener('click', imageDomClick, false);
292 |
293 |
294 | (function(i) {
295 | if(imageDoms[i].src === window.qbian.config.whiteBgImgUrl) return; // 默认图片不显示可删除按钮
296 |
297 | imageDoms[i].addEventListener('mouseover', function(e) {
298 | imagedltDoms[i].style.display = 'block';
299 | }, false);
300 |
301 | imageDoms[i].addEventListener('mouseout', function(e) {
302 | imagedltDoms[i].style.display = 'none';
303 | }, false);
304 |
305 | imagedltDoms[i].addEventListener('mouseover', function(e) {
306 | imagedltDoms[i].style.display = 'block';
307 | }, false);
308 |
309 | imagedltDoms[i].addEventListener('mouseout', function(e) {
310 | imagedltDoms[i].style.display = 'none';
311 | }, false);
312 | })(i);
313 |
314 | imagedltDoms[i].removeEventListener('click', imageDltDomClick, false);
315 | imagedltDoms[i].addEventListener('click', imageDltDomClick, false);
316 | }
317 | }
318 | bindImagesEv();
319 | window.qbian.bus.on('updateImages', bindImagesEv);
320 |
321 | // 移动鼠标,选中画布上元素
322 | canvasDom.addEventListener('mousemove', function(event) {
323 | if(!delet) return;
324 | // console.info('选中操作', container);
325 | var x = event.clientX+document.documentElement.scrollLeft+document.body.scrollLeft - canvasDom.offsetLeft;
326 | var y = event.clientY+document.documentElement.scrollTop+document.body.scrollTop - canvasDom.offsetTop;
327 | var mHalf = 5;
328 | var dot = {
329 | x0: x - mHalf,
330 | y0: y - mHalf,
331 | x1: x + mHalf,
332 | y1: y + mHalf
333 | };
334 | var o,has = false;
335 | for(var key in container.session) {
336 | if(has) break;
337 |
338 | o = container.session[key];
339 | switch(o.type) {
340 | case 'line' : { // 线条
341 | for(var i = 0, len = o.points.length; i < len; i ++) {
342 | if(dot.x0 - mHalf < o.points[i].x*cvsW && dot.x1 + mHalf > o.points[i].x*cvsW
343 | && dot.y0 - mHalf < o.points[i].y*cvsH && dot.y1 + mHalf > o.points[i].y*cvsH) {
344 | container.selected.id = o.id;
345 | has = true;
346 | }
347 | }
348 | break;
349 | }
350 | case 'straightLine' : { // 直线
351 | var lineLength = Math.sqrt( Math.pow( Math.abs(o.startDot.x*cvsW - o.endDot.x*cvsW) , 2) + Math.pow( Math.abs(o.startDot.y*cvsH - o.endDot.y*cvsH) , 2) ),
352 | mStartLength = Math.sqrt( Math.pow( Math.abs(x - o.startDot.x*cvsW) , 2) + Math.pow( Math.abs(y - o.startDot.y*cvsH) , 2) ),
353 | mEndLength = Math.sqrt( Math.pow( Math.abs(x - o.endDot.x*cvsW) , 2) + Math.pow( Math.abs(y - o.endDot.y*cvsH) , 2) ),
354 | ratio = 1.01;
355 | if(lineLength < 100) {
356 | ratio = 1.01
357 | } else if(lineLength < 200) {
358 | ratio = 1.005;
359 | } else if(lineLength < 400) {
360 | ratio = 1.001;
361 | } else if(lineLength < 800) {
362 | ratio = 1.0005;
363 | } else {
364 | ratio = 1.0001;
365 | }
366 | if( (mStartLength + mEndLength) / lineLength > 1 && (mStartLength + mEndLength) / lineLength < ratio ) {
367 | container.selected.id = o.id;
368 | has = true;
369 | }
370 | break;
371 | }
372 | case 'round' : { // 圆形
373 | if( Math.sqrt( Math.pow( Math.abs(o.startDot.x*cvsW - x) , 2) + Math.pow( Math.abs(o.startDot.y*cvsH - y) , 2) ) < o.r*(cvsW/cvsH)) {
374 | container.selected.id = o.id;
375 | has = true;
376 | }
377 | break;
378 | }
379 | case 'rect' : { // 矩形
380 | var oHalfWidth = o.sideLength.x*cvsW / 2,
381 | oHalfHeight = o.sideLength.y*cvsH / 2,
382 | oCenter = {
383 | x: o.startDot.x < o.endDot.x ? o.startDot.x*cvsW + oHalfWidth : o.startDot.x*cvsW - oHalfWidth,
384 | y: o.startDot.y < o.endDot.y ? o.startDot.y*cvsH + oHalfHeight : o.startDot.y*cvsH - oHalfHeight
385 | },
386 | distance = {
387 | x: Math.abs(oCenter.x - x),
388 | y: Math.abs(oCenter.y - y)
389 | };
390 | if(distance.x < (oHalfWidth + mHalf) && distance.y < (oHalfHeight + mHalf)) {
391 | container.selected.id = o.id;
392 | has = true;
393 | }
394 | break;
395 | }
396 | case 'text' : { // 文本
397 | if(dot.x0 < o.startDot.x*cvsW && dot.x1+10 > o.startDot.x*cvsW
398 | && dot.y0 < o.startDot.y*cvsH && dot.y1+o.lineWidth*9 > o.startDot.y*cvsH) {
399 | container.selected.id = o.id;
400 | has = true;
401 | }
402 |
403 | break;
404 | }
405 | default: continue;
406 | }
407 | }
408 |
409 | if(has) {
410 | canvas.redraw();
411 | } else if(container.selected.id !== '') {
412 | container.selected.id = '';
413 | canvas.redraw();
414 | }
415 | });
416 |
417 | // ========================== 监听窗口重置大小事件
418 | var wh = 6/3;
419 | window.addEventListener('resize', onResize, false);
420 | function onResize() {
421 | if(window.visualViewport.width / window.innerHeight > wh) { // 太宽了,以高为基准
422 | canvasDom.height = window.innerHeight;
423 | canvasDom.width = window.innerHeight * wh;
424 | } else { // 太高了,以宽为基准
425 | canvasDom.width = window.visualViewport.width;
426 | canvasDom.height = window.visualViewport.width / wh;
427 | }
428 | canvasDom.style.marginLeft = (window.visualViewport.width - canvasDom.width) / 2 +'px';
429 |
430 | log('canvasDom.width='+canvasDom.width,'canvasDom.height='+canvasDom.height);
431 | cvsW = canvasDom.width;
432 | cvsH = canvasDom.height;
433 | canvas.setWH(cvsW, cvsH); // 设置 画布 宽高
434 | canvas.redraw(); // 窗口重置时,画布重新绘制
435 | }
436 | onResize();
437 |
438 | // ========================== 发送数据
439 | function emit(data) {
440 | data.pathname = pathname;
441 |
442 | socket.emit('drawing', data);
443 | }
444 |
445 | // ========================== 更新鼠标样式
446 | var className = '';
447 | function updateCursor(clazzName) {
448 | if(clazzName) {
449 | canvasDom.className = clazzName + ' canvas';
450 | } else {
451 | canvasDom.className = className + ' canvas';
452 | }
453 | }
454 |
455 | // ========================== 隐藏全部弹出框
456 | function hideAll() {
457 | lineWidthDom.style.display = 'none';
458 | colorDom.style.display = 'none';
459 | imgDom.style.display = 'none';
460 | }
461 |
462 |
463 | })(window.qbian);
--------------------------------------------------------------------------------