UNPKG

msbsdk

Version:

美术宝白板sdk

267 lines (256 loc) 7.99 kB
class MSBWhiteBoard { constructor() { // 画布元素 this.canvas = null // 上下文 this.ctx = null // 绘画步骤的状态 0: 按下时 1: 移动时 2: 抬起时 this.step = [false, false, false] /** * point: {tool: 1、画笔 2、橡皮, x: 横坐标, y: 纵坐标, state: DOWN | MOVE | END} * line: [point, point, ...] * lines: [line, line, ...] * index: Number 每一页白板的数据的索引 * pagesData: {index: lines, index: lines, ...} */ this.pagesData = {} // 代理配置项 this.options = null // 配置项 this._options = { // 画布id canvasId: 'defaultDrawing', // 画布宽度 width: 0, // 画布高度 height: 0, // 线条颜色 lineColor: '#f02233', // 线条宽度 lineWidth: 2, // 橡皮宽度 eraserWidth: 20, // 橡皮高度 eraserHeight: 40, // 总页数 pages: 0, // 当前页数 index: 0, // 画布状态 0、鼠标 1、画笔 2、橡皮 tool: 0 } this._proxyOptions() } // 初始化画布 init(rootId, options) { if (!rootId) throw new Error({ message: 'rootId is necessary option' }) if (typeof rootId !== 'string') throw new Error({ message: 'rootId type errors must be string types' }) this.setOptions(options) // 白板父元素 let rootEle = document.getElementById(rootId) // 创建白板DOM元素 this.canvas = document.createElement('canvas') // 白板默认ID this.canvas.id = this.options.canvasId || 'defaultWhiteBoard' this.canvas.width = this.options.width this.canvas.height = this.options.height rootEle.appendChild(this.canvas) this.ctx = this.canvas.getContext('2d') window.addEventListener('mousedown', this._draw.bind(this)) window.addEventListener('mousemove', this._draw.bind(this)) window.addEventListener('mouseup', this._draw.bind(this)) } // 绘画 _draw(e) { if (this.options.tool === 0 || !this.canvas) return if (this._isConfine(e.clientX, e.clientY)) return let { type, target } = e let index = this.options.index // 如果没有数据则创建一个空数组存储线条 let curPageLines = this.pagesData[index] || (this.pagesData[index] = []) let x = e.clientX - this.canvas.getBoundingClientRect().left let y = e.clientY - this.canvas.getBoundingClientRect().top let point = this._reckonRelativePoint({ x, y }) if (type === 'mousedown') { if (target !== this.canvas) return curPageLines.push([{tool: this.options.tool, x: point.x, y: point.y, state: 'DOWN'}]) this._drawStart(x, y) this.step[0] = true } if (type === 'mousemove' && this.step[0]) { curPageLines[curPageLines.length - 1].push({x: point.x, y: point.y, state: 'MOVE'}) this._drawPath(x, y, this.options.tool) this.step[1] = true } if (type === 'mouseup' && this.step[0]) { curPageLines[curPageLines.length - 1].push({x: point.x, y: point.y, state: 'UP'}) this._drawEnd() this.step.fill(false) this.step[2] = true } } // 绘制当前页的所有线条 _drawLines() { let allLine = this.pagesData[this.options.index] || (this.pagesData[this.options.index] = []) // 清除画布 this.ctx.clearRect(0, 0, this.options.width, this.options.height) // 遍历线 for (let i = 0; i < allLine.length; i++) { let { tool } = allLine[i][0] // 遍历点 for (let j = 0; j < allLine[i].length; j++) { let { x, y, state } = allLine[i][j] let point = this._reckonAbsolutePoint({x, y}) if (state === 'DOWN') { this._drawStart(point.x, point.y) } else if (state === 'MOVE') { this._drawPath(point.x, point.y, tool) } else if (state === 'UP') { this._drawEnd() } } } } // 绘画步骤一: 绘制起点 _drawStart(x, y) { this.ctx.beginPath() // 画笔起点 this.ctx.moveTo(x, y) // 设置画笔属性 this.ctx.lineCap = 'round' this.ctx.lineJoin = 'round' this.ctx.lineWidth = this.options.lineWidth this.ctx.strokeStyle = this.options.lineColor } // 绘画步骤二: 绘制路径 type: 1、画笔 2、橡皮 _drawPath(x, y, type) { if (type === 1) { this.ctx.lineTo(x, y) this.ctx.stroke() } if (type === 2) { this.ctx.clearRect(x, y, this.options.eraserWidth, this.options.eraserHeight) } } // 绘画步骤三: 绘制结束 _drawEnd() { this.ctx.closePath() } // 计算相对坐标 _reckonRelativePoint(point) { let x = Number(point.x / this.options.width) let y = Number(point.y / this.options.height) return Object.assign({}, point, {x, y}) } // 计算绝对坐标 _reckonAbsolutePoint(point) { let x = Number(point.x * this.options.width) let y = Number(point.y * this.options.height) return Object.assign({}, point, {x, y}) } // 是否超出白板边界 _isConfine(x, y) { let left = x - this.canvas.getBoundingClientRect().left let top = y - this.canvas.getBoundingClientRect().top if (left < 0 || top < 0 || left > this.options.width || top > this.options.height) { if (this.step[0]) { return false } else { return true } } else { return false } } // 代理配置项 _proxyOptions() { let _this = this this.options = new Proxy(this._options, { set: (target, key, value, recive) => { if (_this.canvas) { switch (key) { case 'canvasId': _this.canvas.setAttribute('id', value) break case 'width': _this.canvas.setAttribute('width', value) _this._drawLines() break case 'height': _this.canvas.setAttribute('height', value) _this._drawLines() break case 'lineColor': _this.ctx.strokeStyle = value break case 'lineWidth': _this.ctx.lineWidth = value break case 'eraserWidth': _this._options.eraserWidth = value break case 'eraserHeight': _this._options.eraserHeight = value break case 'pages': _this._options.pages = value this.step.fill(false) break case 'index': _this._options.index = value _this._drawLines() this.step.fill(false) break case 'tool': _this.step.fill(false) _this._options.tool = value break case 'clear': } } return Reflect.set(target, key, value, recive) } }) } // 获取图片宽高 getImgWH(imgEleId, url, call) { let img = document.getElementById(imgEleId) img.src = url if (img.complete) { if (typeof call === 'function') { call(img.clientWidth, img.clientHeight) } } img.onload = () => { if (typeof call === 'function') { call(img.clientWidth, img.clientHeight) } } } // 清除画布数据 clear(page) { // 清屏的页面为当前页时清空画布,否则只清空数据 if (page === this.options.index) { this.ctx.clearRect(0, 0, this.options.width, this.options.height) } this.pagesData[page] = [] } // 更改白板配置 setOptions(obj) { Object.assign(this.options, obj) } // 处理命令 ['CLEAR_DRAW'] action(cmd) { switch (cmd) { case 'CLEAR_DRAW': // 清除画布 this.ctx.clearRect(0, 0, this.options.width, this.options.height) // 清除数据 this.pagesData[this.options.index] = [] // 步骤状态重置 this.step.fill(false) break } } } export default MSBWhiteBoard