msbsdk
Version:
美术宝白板sdk
267 lines (256 loc) • 7.99 kB
JavaScript
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