123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- import toast from '../miniprogram_npm/@vant/weapp/toast/toast';
- import {
- request
- } from "../utils/api"
- import {
- env
- } from "../env"
- import smooth from '../tqlsdk/filter'
- const event = require('../tqlsdk/event')
- const app = getApp()
- class myCanvas {
- constructor(canvasId, canvasWidth, canvasHeight, imgUrl) {
- this.canvas = null
- this.canvasNode = null
- this.canvas = null
- this.canvasNode = null
- this.ctx = null
- this.allImage = {}
- this.force = 0
- this.canvasWidth = canvasWidth
- this.canvasHeight = canvasHeight
- this.mapData = new Map();
- this.x_coordinate = null
- this.y_coordinate = null
- this.color = '#141414'
- this.isDown = false
- this.lastPage = null
- this.isReplay = false
- this.p_index = 0
- this.scale = 1
- this.offsetX = 0
- this.offsetY = 0
- this.lineWidth = 2
- this.penWidth = 1
- this.strokeFlag = false
- this.lastPoint = null
- this.imgUrls = null
- this.offlineFlag = false
- this.readLocalFileFlag = false
- //实际宽高
- this.actualWidth = 182
- this.actualHeight = 256
- this.codePointDistance_x = 1.524
- this.codePointDistance_y = 1.524
- this.initCanvas(canvasId, canvasWidth, canvasHeight, imgUrl)
- }
- //设置实际大小
- setActualSize(width, height) {
- this.actualWidth = width
- this.actualHeight = height
- }
- //设置码点类型 009 011
- setCodePointType(codeType) {
- switch (codeType) {
- case 12:
- this.codePointDistance_x = 1.524
- this.codePointDistance_y = 1.524
- break;
- case 16:
- this.codePointDistance_x = 2.013
- this.codePointDistance_y = 2.013
- break;
- default:
- this.codePointDistance_x = 1.524
- this.codePointDistance_y = 1.524
- break;
- }
- }
- // 开关笔锋
- /**
- *
- * @param {boolean} flag
- */
- changeStroke(flag) {
- this.strokeFlag = flag
- }
- // 改变笔迹宽度
- /**
- *
- * @param {number} width
- */
- changeWidth(width) {
- this.penWidth = width
- }
- // 初始化canvas
- /**
- * @param {string} canvasId
- * @param {number} canvasWidth
- * @param {number} canvasHeight
- * @param {Array} imgUrl
- */
- initCanvas(canvasId, canvasWidth, canvasHeight, imgUrl) {
- console.log(canvasId, canvasWidth, canvasHeight, imgUrl)
- wx.createSelectorQuery().select(canvasId).fields({
- node: true,
- size: true
- }).exec(res => {
- this.canvas = res[0].node
- this.ctx = this.canvas.getContext('2d')
- this.canvas.width = canvasWidth * 2
- this.canvas.height = canvasHeight * 2
- this.imgUrls = imgUrl
- console.log(this.imgUrls)
- this.ctx.lineCap = "round"
- this.ctx.lineJoin = "miter"
- this.ctx.miterLimit = 1
- this.changePage(0)
- })
- }
- saveCanvasImage(homeworkId, studentId, type, subject = '') {
- console.log('保存图片!!');
- setTimeout(() => {
- const base64 = this.canvas.toDataURL("png");
- let body = {}
- body.homeworkId = homeworkId
- body.studentId = studentId
- body.baseStr = base64.substring(22)
- body.subject = subject
- console.log(body)
- if (type === "teacher") {
- wx.request({
- url: env.baseUrl + '/app-api/tutor/h5/teacherUpload',
- method: 'post',
- data: {
- baseStr: base64.substring(22),
- id: homeworkId
- },
- header: {
- 'content-type': 'application/x-www-form-urlencoded' // 默认值
- },
- success(res) {
- console.log(res.data)
- if (res.data.code == 0) {
- toast.success({
- message: "保存成功",
- duration: 1500,
- })
- }
- }
- })
- } else {
- wx.request({
- url: env.baseUrl + '/app-api/tutor/h5/studentUpload',
- method: 'post',
- data: body,
- header: {
- 'content-type': 'application/x-www-form-urlencoded' // 默认值
- },
- success(res) {
- console.log(res.data)
- if (res.data.code == 0) {
- toast.success({
- message: "保存成功",
- duration: 1500,
- })
- }
- }
- })
- }
- }, 200)
- }
- // 改变颜色
- /**
- *
- * @param {string} color
- */
- changeColor(color) {
- this.color = color
- this.ctx.fillStyle = color;
- this.ctx.strokeStyle = color;
- }
- // 绘制图片
- /**
- *
- * @param {string} imgUrl
- * @param {number} x1
- * @param {number} y1
- * @param {number} x2
- * @param {number} y2
- */
- async drawImage(imgUrl, x1, y1, x2, y2) {
- if (!this.allImage[imgUrl]) {
- this.allImage[imgUrl] = await new Promise((resolve) => {
- const img = this.canvas.createImage()
- img.src = imgUrl
- img.onload = () => {
- resolve(img)
- }
- })
- }
- this.ctx.drawImage(this.allImage[imgUrl], x1, y1, x2, y2)
- }
- // 切页
- /**
- *
- * @param {number} index
- */
- async changePage(index) {
- if (index === this.lastPage && !this.isReplay) {
- return
- }
- // 此处切页
- event.emit('changePage', index)
- this.lastPage = index
- await this.drawImage(this.imgUrls[(index & 1 ? 1 : 0)], 0, 0, this.canvasWidth * 2, this.canvasHeight * 2)
- // 切页绘制
- if (!this.isReplay) {
- let pageData = this.mapData.get(index)
- if (!pageData) {
- return
- }
- let pageLength = pageData.length
- for (let i = 0; i < pageLength; i++) {
- this.strokeFlag ? this.strokeType(pageData[i].dotType, pageData[i].xPoint, pageData[i].yPoint, pageData[i].force) : this.penType(pageData[i].dotType, pageData[i].xPoint, pageData[i].yPoint)
- }
- }
- }
- // 清除
- async clear() {
- this.mapData.clear();
- this.ctx.clearRect(0, 0, this.canvasWidth * 2, this.canvasHeight * 2)
- // 清除后重新绘制底图
- this.addPageId(this.lastPage)
- await this.drawImage(this.imgUrls[(this.lastPage & 1 ? 1 : 0)], 0, 0, this.canvasWidth * 2, this.canvasHeight * 2)
- }
- // 回放
- async replay() {
- this.isReplay = true
- this.ctx.clearRect(0, 0, this.canvasWidth * 2, this.canvasHeight * 2)
- // 清除后重新绘制底图
- this.drawImage(this.imgUrls[(this.lastPage & 1 ? 1 : 0)], 0, 0, this.canvasWidth * 2, this.canvasHeight * 2)
- if (!this.mapData.has(this.lastPage) || this.mapData.get(this.lastPage).length === 0) {
- this.isReplay = false
- return
- }
- let currentPageData = this.mapData.get(this.lastPage)
- let dataLength = currentPageData.length
- for (let index = 0; index < dataLength; index++) {
- await sleep(10)
- this.strokeFlag ? this.strokeType(currentPageData[index].dotType, currentPageData[index].xPoint, currentPageData[index].yPoint, currentPageData[index].force) : this.penType(currentPageData[index].dotType, currentPageData[index].xPoint, currentPageData[index].yPoint)
- }
- this.isReplay = false
- }
- // 储存数据
- addPageId(myPageId) {
- if (myPageId === this.lastPage) {
- !this.mapData.has(myPageId) && this.mapData.set(myPageId, [])
- return
- }!this.mapData.has(myPageId) && this.mapData.set(myPageId, [])
- this.changePage(myPageId);
- }
- // 四舍五入算法
- roundNum(number, fractionDigits) {
- return Math.round(number * Math.pow(10, fractionDigits)) / Math.pow(10, fractionDigits);
- }
- // 数据处理
- usbData(data) {
- // 回放时不接收数据
- if (this.isReplay) {
- toast.fail('回放时请不要书写')
- return
- }
- // let newDot = smooth(data)
- let newDot = data
- // console.log(newDot);
- if (newDot.dotType === "PEN_DOWN") {
- // 切页
- this.addPageId(data.pageID)
- }
- // switch (data.bookID) {
- // case value:
- // break;
- // default:
- // break;
- // }
- // console.log((data.x) + ((data.fx) / 100))
- // console.log((data.y) + ((data.fy) / 100))
- // let x = (data.x) + data.fx / 100
- // let y = (data.y) + data.fy / 100
- // 得到的x,y坐标 因为此处canvas为2倍放大 所以图标宽度也需要乘以2
- // let xPoint = this.roundNum(((((data.x) + ((data.fx) / 100)) * this.canvasWidth * 2) / ax), 13);
- // console.log(xPoint);
- // let yPoint = this.roundNum(((((data.y) + ((data.fy) / 100)) * this.canvasHeight * 2) / ay), 13);
- // const x = (_x * 840) / (210 / 1.524)
- // const y = (_y * 1188) / (297 / 1.524)
- let xPoint = this.roundNum(((newDot.ab_x * this.canvasWidth * 2) / (this.actualWidth / this.codePointDistance_x)), 13);
- let yPoint = this.roundNum(((newDot.ab_y * this.canvasHeight * 2) / (this.actualHeight / this.codePointDistance_y)), 13);
- // console.log(yPoint);
- // console.log('转换前的坐标', x, y)
- // console.log('转换后的坐标', xPoint, yPoint)
- this.mapData.get(newDot.pageID).push({
- bookId: newDot.bookID,
- pageID: newDot.pageID,
- timeLog: newDot.timeLong,
- xPoint,
- yPoint,
- dotType: newDot.dotType,
- force: newDot.force,
- color: this.color
- })
- this.strokeFlag ? this.strokeType(newDot.dotType, xPoint, yPoint, data.force) : this.penType(newDot.dotType, xPoint, yPoint)
- }
- // 点分类型(有笔锋)
- strokeType(dotType, x, y, force) {
- // console.log(x, y, force);
- dotType === 'PEN_DOWN' ? this.strokeDown(x, y, force, this.penWidth) : dotType === 'PEN_MOVE' ? this.strokeMove(x, y, force, this.penWidth) : this.strokeUp(x, y, force, this.ctx, this.penWidth)
- }
- // 点分类型(无笔锋)
- penType(dotType, x, y) {
- dotType === 'PEN_DOWN' ? this.penDown(x, y) : dotType === 'PEN_MOVE' ? this.penMove(x, y) : this.penUp(x, y)
- }
- // down点方法(无笔锋)
- penDown(x, y) {
- this.lastPoint = {
- x,
- y
- }
- }
- // move点方法(无笔锋)
- penMove(x, y) {
- let newPoint = {
- x,
- y
- }
- this.drawLine(this.lastPoint.x, this.lastPoint.y, newPoint.x, newPoint.y);
- this.lastPoint = newPoint;
- }
- // up点方法
- penUp(x_0, y_0) {
- this.drawLine(this.lastPoint.x, this.lastPoint.y, x_0, y_0)
- }
- // 绘制方法(无笔锋)
- drawLine(x1, y1, x2, y2) {
- this.ctx.beginPath();
- this.ctx.moveTo(x1, y1);
- this.ctx.lineWidth = this.penWidth * 2
- this.ctx.lineTo(x2, y2);
- this.ctx.fill();
- this.ctx.stroke();
- this.ctx.closePath();
- }
- // down点方法(有笔锋)
- strokeDown(x, y, force, width) {
- // console.log(123)
- penStrokeDown(x, y, force, width)
- }
- // move点方法(有笔锋)
- strokeMove(x, y, force, width) {
- penStrokeMove(x, y, force, width)
- }
- // up点方法(有笔锋)
- strokeUp(x, y, force, ctx, penWidth) {
- penStrokeUp(x, y, force, ctx, penWidth)
- }
- }
- // 控制点类
- class ControlPoint {
- constructor(x, y, width) {
- this.x = x
- this.y = y
- this.width = width
- }
- set(x, y, width) {
- this.x = x
- this.y = y
- this.width = width
- }
- setControlPoint(point) {
- this.x = point.x
- this.y = point.y
- this.width = point.width
- }
- }
- // 贝塞尔类
- class Bezier {
- constructor() {
- // 控制点
- this.mControl = new ControlPoint();
- // 距离
- this.mDestination = new ControlPoint()
- // 下一个需要控制点
- this.mNextControl = new ControlPoint()
- // 资源的点
- this.mSource = new ControlPoint()
- }
- initPoint(last, cur) {
- this.init(last.x, last.y, last.width, cur.x, cur.y, cur.width)
- }
- init(lastX, lastY, lastWidth, x, y, width) {
- //资源点设置 最后的点位资源点
- this.mSource.set(lastX, lastY, lastWidth)
- let xMid = getMid(lastX, x)
- let yMid = getMid(lastY, y)
- let wMid = getMid(lastWidth, width)
- // 距离单为平均点
- this.mDestination.set(xMid, yMid, wMid)
- // 控制点为当前的距离点
- this.mControl.set(getMid(lastX, xMid), getMid(lastY, yMid), getMid(lastWidth, wMid))
- // 下个控制点为当前点
- this.mNextControl.set(x, y, width)
- }
- addNodePoint(cur) {
- this.addNode(cur.x, cur.y, cur.width)
- }
- //替换旧的点 原来的距离点变换为资源点,控制点变为原来的下一个控制点,距离点取原来控制点的和新的的一半 下个控制点为新的点
- addNode(x, y, width) {
- this.mSource.setControlPoint(this.mDestination)
- this.mControl.setControlPoint(this.mNextControl)
- this.mDestination.set(getMid(this.mNextControl.x, x), getMid(this.mNextControl.y, y), getMid(this.mNextControl.width, width))
- this.mNextControl.set(x, y, width);
- }
- //结束时改变点的位置
- penEnd() {
- this.mSource.setControlPoint(this.mDestination)
- this.mControl.set(getMid(this.mNextControl.x, this.mSource.x), getMid(this.mNextControl.y, this.mSource.y), getMid(this.mNextControl.width, this.mSource.width))
- this.mDestination.setControlPoint(this.mNextControl)
- }
- // 获取点的信息
- getPoint(t) {
- let point = new ControlPoint()
- point.set(this.getX(t), this.getY(t), this.getW(t))
- return point
- }
- //三阶曲线控制点
- getValue(p0, p1, p2, t) {
- let a = p2 - 2 * p1 + p0
- let b = 2 * (p1 - p0)
- return a * t * t + b * t + p0
- }
- getX(t) {
- return this.getValue(this.mSource.x, this.mControl.x, this.mDestination.x, t)
- }
- getY(t) {
- return this.getValue(this.mSource.y, this.mControl.y, this.mDestination.y, t)
- }
- getW(t) {
- return getWidth(this.mSource.width, this.mDestination.width, t)
- }
- }
- // 计算压力值
- const calculatePressure = force => force >= 0 && force <= 20 ? 40 : force < 20 && force <= 40 ? 60 : force > 40 && force <= 60 ? 80 : force > 60 && force <= 90 ? 100 : force > 90 && force <= 150 ? 120 : 130;
- // 获取中间值
- const getMid = (x, y) => (x + y) / 2
- // 计算宽度
- const getWidth = (w0, w1, t) => w0 + (w1 - w0) * t
- // 当前点
- let curPoint = null
- // 上一个点
- let mLastPoint = null
- // 计算出来的线段宽度
- let mLastWidth = null
- // 贝塞尔类实例
- let mBezier = new Bezier()
- // 笔画的第一点
- let mFirstPoint = null
- // 点击数
- let pointNum = 0
- //转换参数
- const transFormScale = 80
- // 每笔的数据
- let nowList = []
- // 上一压力值
- let lastForce = null
- // down点(笔锋)
- const penStrokeDown = (x, y, force, width) => {
- let pressure = calculatePressure(force)
- mLastWidth = pressure / transFormScale * width
- pointNum = 1
- // 记录down点信息
- curPoint = new ControlPoint(x, y, mLastWidth)
- mLastPoint = new ControlPoint(x, y, mLastWidth)
- // console.log(mLastPoint)
- mFirstPoint = new ControlPoint(x, y, mLastWidth)
- nowList = []
- nowList.push(curPoint)
- lastForce = force
- }
- // move点方法(笔锋)
- const penStrokeMove = (x, y, force, penWidth) => {
- let pressure = calculatePressure(force)
- let pressureCheck = forceCreck(lastForce, pressure)
- lastForce = pressureCheck
- let curWidth = pressureCheck / transFormScale * penWidth
- curPoint = new ControlPoint(x, y, curWidth)
- // console.log(curPoint, mLastPoint)
- let curDis = Math.hypot(curPoint.x - mLastPoint.x, curPoint.y - mLastPoint.y)
- if (pointNum === 1) {
- pointNum++;
- mBezier.initPoint(mLastPoint, curPoint);
- } else {
- mBezier.addNodePoint(curPoint);
- }
- mLastWidth = curWidth;
- doMove(curDis);
- mLastWidth = new ControlPoint(curPoint.x, curPoint.y, curPoint.width);
- // console.log(mFirstPoint);
- }
- // up点方法(笔锋)
- const penStrokeUp = (x, y, force, context, penWidth) => {
- if (nowList.length === 0) {
- return
- }
- curPoint = new ControlPoint(x, y, 0)
- let deltaX = curPoint.x - mLastPoint.x
- let deltaY = curPoint.y - mLastPoint.y
- let curDis = Math.hypot(deltaX, deltaY)
- mBezier.addNodePoint(curPoint)
- let steps = 1 + Math.floor((curDis / 10))
- let step = 1 / steps
- for (let t = 0; t < 1; t += step) {
- let point = mBezier.getPoint(t)
- nowList.push(point)
- }
- mBezier.penEnd()
- for (let t = 0; t < 1; t += step) {
- let point = mBezier.getPoint(t)
- nowList.push(point)
- }
- draws(context, penWidth);
- nowList = []
- }
- function doMove(curDis) {
- let steps = 1 + Math.floor((curDis / 10));
- let step = 1 / steps
- for (let t = 0; t < 1; t += step) {
- let Point = mBezier.getPoint(t)
- nowList.push(Point)
- }
- }
- function draws(context, penWidth) {
- doPreDraw(context, penWidth);
- }
- function doPreDraw(context, penWidth) {
- let curPoint = nowList[0]
- let length = nowList.length
- for (let i = 1; i < length; i++) {
- drawPoint(curPoint, nowList[i], context, penWidth)
- curPoint = nowList[i]
- }
- }
- function drawPoint(curPoint, point, context, penWidth) {
- // 相同点不绘制
- if (curPoint.x === point.x && curPoint.y === point.y) {
- return
- }
- drawLine(curPoint.x, curPoint.y, curPoint.width, point.x, point.y, point.width, context, penWidth);
- }
- // 绘制方法
- function drawLine(x0, y0, w0, x1, y1, w1, context, penWidth) {
- let curDis = Math.hypot(x1 - x0, y1 - y0)
- let step = 1
- if (penWidth <= 6) {
- step = 1 + Math.floor((curDis))
- } else if (penWidth > 60) {
- step = 1 + Math.floor((curDis / 4))
- } else {
- step = 1 + Math.floor((curDis / 3))
- }
- let deltaX = (x1 - x0) / step
- let deltaY = (y1 - y0) / step
- let deltaW = (w1 - w0) / step
- let x = x0
- let y = y0
- let w = w0
- for (let i = 0; i < step; i++) {
- let left = x + w / 2
- let top = y + w / 2
- let right = x - w / 2
- let bottom = y - w / 2
- let midPointX = (left + right) / 2
- let midPointY = (top + bottom) / 2
- let xRadius = Math.abs((left - right) / 2)
- let yRadius = Math.abs((top - bottom) / 2)
- context.setLineDash([])
- context.beginPath();
- context.ellipse(midPointX * 0.97, midPointY * 0.976, xRadius, yRadius, 0, 0, Math.PI * 2);
- context.stroke();
- context.closePath();
- context.fill();
- x += deltaX
- y += deltaY
- w += deltaW
- }
- }
- // 压力值前后差距过大补正
- const forceCreck = (lastForce, curForce) => {
- if ((curForce - lastForce) > 35) {
- return (lastForce + curForce) / 2
- }
- return curForce
- }
- // 休眠方法
- function sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms))
- }
- export {
- myCanvas
- }
|