├── .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 | ![示例图-1](https://raw.githubusercontent.com/Qbian61/whiteBoard/master/%E6%88%AA%E5%9B%BE/whiteboard-1.jpeg) 24 | 25 | ![示例图-2(有背景图片)](https://raw.githubusercontent.com/Qbian61/whiteBoard/master/%E6%88%AA%E5%9B%BE/whiteboard-2.jpeg) 26 | 27 | ![示例图-3(有背景图片)](https://raw.githubusercontent.com/Qbian61/whiteBoard/master/%E6%88%AA%E5%9B%BE/whiteboard-3.png) 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 | 浏览器不支持canvas 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 |
65 |
66 |
67 |
68 | 69 | 70 |
71 |
72 |
73 |
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); --------------------------------------------------------------------------------