1597 lines
42 KiB
JavaScript
1597 lines
42 KiB
JavaScript
import { base64ToPathFn, downloadFile, showLoading, hideLoading,
|
||
countTextLength, getImageInfo, getModeImage, compressImage } from './util'
|
||
|
||
import QRCodeAlg from './qrcode'
|
||
|
||
const TYPES = ['2d', 'webgl']
|
||
|
||
/**
|
||
* 绘制
|
||
*/
|
||
export default class Draw{
|
||
constructor(params = {}) {
|
||
let { width, canvasId, type, height, background, drawDelayTime, delayTime, _this, fileType, quality, isCompressImage } = params
|
||
this.width = width
|
||
this.height = height
|
||
this.canvasId = canvasId || null
|
||
this.background = background || {
|
||
type: 'color',
|
||
w: this.width,
|
||
h: this.height,
|
||
color: '#ffffff'
|
||
}
|
||
this.drawDelayTime = drawDelayTime || 200
|
||
this.delayTime = delayTime || 200
|
||
this._this = _this || null
|
||
// 导出图片的类型
|
||
this.fileType = fileType || 'png'
|
||
// 导出图片的质量
|
||
this.quality = quality || 1
|
||
// 是否压缩图片,填写时绘制图片会进行压缩操作。绘制图片也能填写该参数。层级大于当前
|
||
this.isCompressImage = isCompressImage || false
|
||
this.callBack = {
|
||
bgObj: {
|
||
width: this.background.w,
|
||
height: this.background.h
|
||
},
|
||
ctxObj: {
|
||
width,
|
||
height
|
||
}
|
||
}
|
||
this.drawTipsText = params.drawTipsText || '绘制中...'
|
||
this.allCallBack = []
|
||
this.type = type || ''
|
||
this.getContext()
|
||
}
|
||
|
||
// 获取绘制对象
|
||
getContext() {
|
||
let { canvasId, _this, width, height, type } = this
|
||
if (TYPES.includes(type)) {
|
||
const query = uni.createSelectorQuery().in(_this)
|
||
query.select(`#${canvasId}`)
|
||
.fields({ node: true, size: true })
|
||
.exec(res => {
|
||
const canvas = res[0].node
|
||
canvas.width = width
|
||
canvas.height = height
|
||
this.canvas = canvas
|
||
this.Context = canvas.getContext(type)
|
||
})
|
||
} else {
|
||
this.Context = uni.createCanvasContext(this.canvasId, this._this)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置透明度
|
||
* @param { number } alpha 透明度
|
||
*/
|
||
setAlpha(alpha = 1) {
|
||
let { Context, type } = this
|
||
if (TYPES.includes(type)) {
|
||
Context.globalAlpha = alpha
|
||
} else {
|
||
Context.setGlobalAlpha(alpha)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置填充颜色
|
||
* @param { String } style 填充颜色
|
||
*/
|
||
setFillStyle(style) {
|
||
let { Context, type } = this
|
||
if (TYPES.includes(type)) {
|
||
Context.fillStyle = style
|
||
} else {
|
||
Context.setFillStyle(style)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置宽度大小
|
||
* @param { number } width 宽度
|
||
*/
|
||
setLineWidth(width) {
|
||
let { Context, type } = this
|
||
if (TYPES.includes(type)) {
|
||
Context.lineWidth = width
|
||
} else {
|
||
Context.setLineWidth(width)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置描边颜色
|
||
* @param { String } style 颜色
|
||
*/
|
||
setStrokeStyle(style) {
|
||
let { Context, type } = this
|
||
if (TYPES.includes(type)) {
|
||
Context.strokeStyle = style
|
||
} else {
|
||
Context.setStrokeStyle(style)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取绘制图片的内容
|
||
* @param { String } src 资源目录
|
||
*/
|
||
getImage(src) {
|
||
let { type, canvas } = this
|
||
if (TYPES.includes(type)) {
|
||
const img = canvas.createImage()
|
||
img.src = src
|
||
return img
|
||
} else {
|
||
return src
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 绘制图片内容
|
||
* @returns
|
||
*/
|
||
drawImageContent(mode, img, dx, dy, dw, dh, sx, sy, sw, sh) {
|
||
let { Context } = this
|
||
return new Promise(async resolve => {
|
||
if (mode != 'default') {
|
||
await Context.drawImage(img, dx, dy, dw, dh, sx, sy, sw, sh)
|
||
} else {
|
||
await Context.drawImage(img, dx, dy, dw, dh)
|
||
}
|
||
resolve(true)
|
||
})
|
||
}
|
||
|
||
drawImageByType(params) {
|
||
let {
|
||
imageInfo, r, x, y, w, h, rotate,
|
||
borderWidth, borderColor, color, alpha, borderType, triangle,
|
||
mode, drawType, img
|
||
} = params
|
||
let { Context } = this
|
||
return new Promise(async resolve => {
|
||
// hideLoading()
|
||
let modeImage = getModeImage(Number(imageInfo.width), Number(imageInfo.height), x, y, w, h, mode)
|
||
let { dx, dy, dw, dh, sw, sh, sx, sy } = modeImage
|
||
Context.save()
|
||
Context.beginPath()
|
||
if (drawType == 'default') {
|
||
this.setRotate(x, y, w, h, rotate)
|
||
// this.drawRect({
|
||
// x,
|
||
// y,
|
||
// w,
|
||
// h,
|
||
// alpha,
|
||
// color,
|
||
// drawImage: true
|
||
// })
|
||
this.setAlpha(alpha)
|
||
await this.drawImageContent(mode, img, dx, dy, dw, dh, sx, sy, sw, sh)
|
||
Context.clip()
|
||
Context.restore()
|
||
} else if (drawType == 'arc') {
|
||
// 绘制圆形图片
|
||
this.setRotate(x, y, w, h, rotate)
|
||
this.drawArc({
|
||
x,
|
||
y,
|
||
r: w / 2,
|
||
borderWidth,
|
||
borderColor,
|
||
color,
|
||
}, true)
|
||
Context.clip()
|
||
this.setAlpha(alpha)
|
||
// img.onload = async () => {
|
||
// await Context.drawImage(img, dx, dy, dw, dh, sx, sy, sw, sh)
|
||
// Context.restore()
|
||
// }
|
||
await this.drawImageContent(mode, img, dx, dy, dw, dh, sx, sy, sw, sh)
|
||
Context.restore()
|
||
} else if (drawType == 'rect') {
|
||
// 绘制矩形图片
|
||
this.setRotate(x, y, w, h, rotate)
|
||
this.drawRect({
|
||
x,
|
||
y,
|
||
w,
|
||
h,
|
||
alpha,
|
||
borderWidth,
|
||
borderColor,
|
||
borderType,
|
||
r,
|
||
color,
|
||
drawImage: true
|
||
}, true)
|
||
Context.clip()
|
||
this.setAlpha(alpha)
|
||
await this.drawImageContent(mode, img, dx, dy, dw, dh, sx, sy, sw, sh)
|
||
Context.restore()
|
||
} else if (drawType == 'triangle') {
|
||
// 绘制三角形图片
|
||
let type = triangle.type || 'isosceles'
|
||
let coordinate = triangle.coordinate || []
|
||
let direction = triangle.direction || 'top'
|
||
if (type != 'custom') {
|
||
this.setTriangleRotate(x, y, w, h, rotate, type)
|
||
}
|
||
this.drawTriangle({
|
||
x,
|
||
y,
|
||
w,
|
||
h,
|
||
alpha,
|
||
borderWidth,
|
||
borderColor,
|
||
color,
|
||
coordinate,
|
||
direction,
|
||
drawType: type
|
||
}, true)
|
||
Context.clip()
|
||
this.setAlpha(alpha)
|
||
await this.drawImageContent(mode, img, dx, dy, dw, dh, sx, sy, sw, sh)
|
||
Context.restore()
|
||
}
|
||
this.setAlpha(1)
|
||
return resolve({
|
||
success: true,
|
||
data: img
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 绘制矩形
|
||
* @param { Object } params 绘制内容
|
||
*/
|
||
drawRect(params = {}) {
|
||
let { width, Context } = this
|
||
let { x, y, w, h, r, color, alpha, isFill, lineWidth, windowAlign, rotate, drawImage, borderColor, borderWidth, borderType } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
w: params.w || width,
|
||
h: params.h || 0,
|
||
r: params.r || 0,
|
||
color: params.color || '#000000',
|
||
borderWidth: params.borderWidth || 0,
|
||
borderColor: params.borderColor || '#000000',
|
||
borderType: params.borderType || 'default',
|
||
alpha: params.alpha || 1,
|
||
lineWidth: params.lineWidth || 1,
|
||
isFill: params.isFill == undefined ? true : params.isFill,
|
||
// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none',
|
||
// 旋转
|
||
rotate: params.rotate || {},
|
||
// 是否是在绘制图片,默认不是
|
||
drawImage: params.drawImage == undefined ? false : params.drawImage,
|
||
}
|
||
if (r * 2 > h) {
|
||
r = h / 2
|
||
}
|
||
if (!drawImage && rotate.deg) {
|
||
Context.save()
|
||
this.setRotate(x, y, w, h, rotate)
|
||
}
|
||
Context.beginPath()
|
||
this.setAlpha(alpha)
|
||
if (windowAlign != 'none') {
|
||
x = this.computedCenterX(width, w, windowAlign)
|
||
}
|
||
let tr = 0
|
||
let tl = 0
|
||
let br = 0
|
||
let bl = 0
|
||
if (typeof borderType == 'string') {
|
||
switch(borderType) {
|
||
case 'tr':
|
||
tr = r
|
||
break
|
||
case 'tl':
|
||
tl = r
|
||
break
|
||
case 'br':
|
||
br = r
|
||
break
|
||
case 'bl':
|
||
bl = r
|
||
break
|
||
default:
|
||
tr = r
|
||
tl = r
|
||
br = r
|
||
bl = r
|
||
}
|
||
}
|
||
if (borderType instanceof Array) {
|
||
if (borderType.includes('tr')) {
|
||
tr = r
|
||
}
|
||
if (borderType.includes('tl')) {
|
||
tl = r
|
||
}
|
||
if (borderType.includes('br')) {
|
||
br = r
|
||
}
|
||
if (borderType.includes('bl')) {
|
||
bl = r
|
||
}
|
||
if (borderType.includes('default')) {
|
||
tr = r
|
||
tl = r
|
||
br = r
|
||
bl = r
|
||
}
|
||
}
|
||
// 上右 tr
|
||
Context.lineTo(x + tl, y)
|
||
Context.arc(x + w - tr, y + tr, tr, Math.PI * 1.5, 0, false)
|
||
// 下右 br
|
||
Context.lineTo(x + w, y + h - br)
|
||
Context.arc(x + w - br,y + h - br, br, 0, Math.PI * .5, false)
|
||
// 下左 bl
|
||
Context.lineTo(x + bl, y + h)
|
||
Context.arc(x + bl, y + h - bl, bl, Math.PI * .5, Math.PI, false)
|
||
// 上左 tl
|
||
Context.lineTo(x, y + tl)
|
||
Context.arc(x + tl, y + tl, tl, Math.PI * 1, Math.PI * 1.5, false)
|
||
Context.closePath()
|
||
if (isFill) {
|
||
if (borderWidth != 0) {
|
||
this.setLineWidth(borderWidth)
|
||
this.setStrokeStyle(borderColor)
|
||
Context.stroke()
|
||
}
|
||
this.setFillStyle(color)
|
||
Context.fill()
|
||
} else {
|
||
this.setLineWidth(lineWidth)
|
||
this.setStrokeStyle(color)
|
||
Context.stroke()
|
||
}
|
||
this.setAlpha(1)
|
||
if (!drawImage && rotate.deg) {
|
||
Context.restore()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 绘制圆
|
||
* @param { Object } params 绘制内容
|
||
*/
|
||
drawArc(params = {}) {
|
||
let { width, Context } = this
|
||
let { x, y, r, s, e, d, alpha, isFill, lineWidth, color, windowAlign, borderColor, borderWidth } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
r: params.r || 0,
|
||
s: params.s || 0,
|
||
e: params.e || Math.PI * 2,
|
||
// 可选。指定弧度的方向是逆时针还是顺时针。默认是false,即顺时针。
|
||
d: params.d == undefined ? false : params.d,
|
||
alpha: params.alpha || 1,
|
||
isFill: params.isFill == undefined ? true : params.isFill,
|
||
lineWidth: params.lineWidth || 1,
|
||
borderWidth: params.borderWidth || 0,
|
||
borderColor: params.borderColor || '#000000',
|
||
color: params.color || '#000000',
|
||
// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none'
|
||
}
|
||
Context.beginPath()
|
||
this.setAlpha(alpha)
|
||
x = x + r
|
||
y = y + r
|
||
if (windowAlign != 'none') {
|
||
x = this.computedCenterX(width, r * 2, windowAlign)
|
||
x += r
|
||
}
|
||
Context.arc(x, y, r, s, e, d)
|
||
if (isFill) {
|
||
if (borderWidth != 0) {
|
||
this.setLineWidth(borderWidth)
|
||
this.setStrokeStyle(borderColor)
|
||
Context.stroke()
|
||
}
|
||
this.setFillStyle(color)
|
||
Context.fill()
|
||
} else {
|
||
this.setLineWidth(lineWidth)
|
||
this.setStrokeStyle(color)
|
||
Context.stroke()
|
||
}
|
||
this.setAlpha(1)
|
||
}
|
||
|
||
|
||
/**
|
||
* 绘制三角形
|
||
* @param @param { Object } params 绘制内容
|
||
*/
|
||
drawTriangle(params = {}) {
|
||
let { Context, width } = this
|
||
let { x, y, w, h, color, alpha, isFill, lineWidth, drawType, coordinate, rotate, windowAlign, direction, borderWidth, borderColor } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
w: params.w || 0,
|
||
h: params.h || 0,
|
||
color: params.color || '#000000',
|
||
borderWidth: params.borderWidth || 0,
|
||
borderColor: params.borderColor || '#000000',
|
||
alpha: params.alpha || 1,
|
||
isFill: params.isFill == undefined ? true : params.isFill,
|
||
lineWidth: params.lineWidth || 1,
|
||
// 当绘制类别是自定义的时候需要传递的参数
|
||
coordinate: params.coordinate || [],
|
||
// 绘制三角形的类型
|
||
// right: 直角三角形
|
||
// isosceles: 等腰三角形
|
||
// custom: 自定义时,x, y, 宽, 高都不需要传递。需要传递绘制点的坐标类型是数组(coordinate)
|
||
// [[1, 3], [2, 3], [4, 5]]
|
||
drawType: params.drawType || 'isosceles',
|
||
// 三角形顶点朝向 top, left, right, bottom
|
||
direction: params.direction || 'top',
|
||
// 旋转
|
||
rotate: params.rotate || {},
|
||
// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none'
|
||
}
|
||
if (windowAlign != 'none' && drawType != 'custom') {
|
||
x = this.computedCenterX(width, w, windowAlign)
|
||
}
|
||
if (rotate.deg && drawType != 'custom') {
|
||
Context.save()
|
||
this.setTriangleRotate(x, y, w, h, rotate, drawType)
|
||
}
|
||
Context.beginPath()
|
||
this.setAlpha(alpha)
|
||
if (drawType == 'isosceles') {
|
||
switch (direction) {
|
||
case 'top':
|
||
Context.lineTo(x + w / 2, y)
|
||
Context.lineTo(x, y + h)
|
||
Context.lineTo(x + w, y + h)
|
||
break
|
||
case 'bottom':
|
||
Context.lineTo(x, y)
|
||
Context.lineTo(x + w, y)
|
||
Context.lineTo(x + w / 2, y + h)
|
||
break
|
||
case 'right':
|
||
Context.lineTo(x, y)
|
||
Context.lineTo(x, y + h)
|
||
Context.lineTo(x + w, y + h / 2)
|
||
break
|
||
case 'left':
|
||
Context.lineTo(x + w, y)
|
||
Context.lineTo(x + w, y + h)
|
||
Context.lineTo(x, y + h / 2)
|
||
break
|
||
}
|
||
} else if (drawType == 'right') {
|
||
switch (direction) {
|
||
case 'top':
|
||
Context.lineTo(x, y)
|
||
Context.lineTo(x, y + h)
|
||
Context.lineTo(x + w, y + h)
|
||
break
|
||
case 'bottom':
|
||
Context.lineTo(x, y)
|
||
Context.lineTo(x + w, y)
|
||
Context.lineTo(x, y + h)
|
||
break
|
||
case 'left':
|
||
Context.lineTo(x, y)
|
||
Context.lineTo(x, y + h)
|
||
Context.lineTo(x + w, y)
|
||
break
|
||
case 'right':
|
||
Context.lineTo(x, y + h)
|
||
Context.lineTo(x + w, y + h)
|
||
Context.lineTo(x + w, y)
|
||
break
|
||
}
|
||
|
||
} else if (drawType == 'custom') {
|
||
for (let i of coordinate) {
|
||
Context.lineTo(i[0], i[1])
|
||
}
|
||
}
|
||
Context.closePath()
|
||
if (isFill) {
|
||
if (borderWidth != 0) {
|
||
this.setLineWidth(borderWidth)
|
||
this.setStrokeStyle(borderColor)
|
||
Context.stroke()
|
||
}
|
||
this.setFillStyle(color)
|
||
Context.fill()
|
||
} else {
|
||
this.setLineWidth(lineWidth)
|
||
this.setStrokeStyle(color)
|
||
Context.stroke()
|
||
}
|
||
this.setAlpha(1)
|
||
if (rotate.deg && drawType != 'custom') {
|
||
Context.restore()
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 绘制图片
|
||
* @param { Object } params 绘制内容
|
||
*/
|
||
drawImage(params = {}) {
|
||
let { width, Context } = this
|
||
return new Promise(async resolve => {
|
||
try {
|
||
let { x, y, w, h, r, src, alpha, drawType, borderWidth, windowAlign, color, mode, rotate, triangle, isCompressImage, quality, borderColor, borderType } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
w: params.w || width,
|
||
h: params.h || 0,
|
||
r: params.r || 0,
|
||
src: params.src || '',
|
||
alpha: params.alpha || 1,
|
||
mode: params.mode || 'aspectFill',
|
||
// default: 默认,rect: 圆角矩形, arc: 圆形 triangle: 三角形
|
||
drawType: params.drawType || 'default',
|
||
borderWidth: params.borderWidth || 0,
|
||
borderColor: params.borderColor || '#000000',
|
||
borderType: params.borderType || 'default',
|
||
color: params.color || '#ffffff',
|
||
// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none',
|
||
// 旋转
|
||
rotate: params.rotate || {},
|
||
// 绘制三角形图片时三角形的内容
|
||
// triangle.type 三角形的类型 right: 直角三角形 isosceles: 等腰三角形 custom: 自定义三角形(不支持旋转)
|
||
// triangle.coordinate 自定义三角形时传递的坐标
|
||
// triangle.direction 三角形顶点朝向
|
||
triangle: params.triangle || {},
|
||
// 是否压缩图片
|
||
isCompressImage: params.isCompressImage != undefined ? params.isCompressImage : this.isCompressImage,
|
||
// 压缩图片时图片的质量只对jpg类型的图片生效
|
||
quality: params.quality || 100
|
||
}
|
||
if (!/\S/.test(src)) {
|
||
return resolve({
|
||
success: false,
|
||
message: '图片路径为空'
|
||
})
|
||
}
|
||
src = await base64ToPathFn(src)
|
||
// #ifndef MP-TOUTIAO
|
||
if (src.includes('http')) {
|
||
let downlaod = await downloadFile(src)
|
||
if (downlaod.data.statusCode != 200) {
|
||
hideLoading()
|
||
return resolve({
|
||
success: false,
|
||
msg: `图片路径为:${src}的文件下载失败`
|
||
})
|
||
}
|
||
if (!downlaod.success) {
|
||
hideLoading()
|
||
return resolve({
|
||
success: false,
|
||
msg: '下载图片失败'
|
||
})
|
||
}
|
||
src = downlaod.data.tempFilePath
|
||
}
|
||
// #endif
|
||
if (windowAlign != 'none') {
|
||
x = this.computedCenterX(width, w, windowAlign)
|
||
}
|
||
// #ifndef H5
|
||
// 压缩图片(不支持H5)
|
||
// if (isCompressImage) {
|
||
// let compressRes = await compressImage({
|
||
// src,
|
||
// quality
|
||
// })
|
||
// if (!compressRes.success) {
|
||
// return resolve(compressRes)
|
||
// }
|
||
// src = compressRes.src
|
||
// }
|
||
// #endif
|
||
// showLoading('获取图片信息中....')
|
||
// console.log('src', src)
|
||
let imageInfo = await getImageInfo(src)
|
||
if (!imageInfo.success) {
|
||
hideLoading()
|
||
return resolve(imageInfo)
|
||
}
|
||
const img = this.getImage(src)
|
||
if (TYPES.includes(this.type)) {
|
||
img.onload = async () => {
|
||
return resolve(await this.drawImageByType({
|
||
imageInfo, r, x, y, w, h, rotate,
|
||
borderWidth, borderColor, color, alpha, borderType, triangle,
|
||
mode, drawType, img
|
||
}))
|
||
}
|
||
} else {
|
||
return resolve(await this.drawImageByType({
|
||
imageInfo, r, x, y, w, h, rotate,
|
||
borderWidth, borderColor, color, alpha, borderType, triangle,
|
||
mode, drawType, img
|
||
}))
|
||
}
|
||
} catch(e) {
|
||
return resolve({
|
||
success: false,
|
||
msg: '绘制图片出错' + e
|
||
})
|
||
}
|
||
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 绘制文字
|
||
* @param { Object } params 绘制内容
|
||
*/
|
||
drawText(params = {}) {
|
||
let { width, Context } = this
|
||
let { x, y, w, text, textIndent, lastWidth, font, color, alpha, isFill, line, windowAlign, textAlign, baseline } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
w: params.w || width - params.x,
|
||
text: String(params.text) || '',
|
||
textIndent: params.textIndent || 0,
|
||
lastWidth: params.lastWidth || 0,
|
||
font: this.getFont(params.font),
|
||
color: params.color || '#000000',
|
||
alpha: params.alpha || 1,
|
||
isFill: params.isFill == undefined ? true : params.isFill,
|
||
// 文字在窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none',
|
||
// 文字的对齐方式(在容器里面) none 默认 center: 居中 right: 右边
|
||
textAlign: params.textAlign || 'none',
|
||
// 水平对齐方式
|
||
baseline: params.baseline || 'top',
|
||
line: this.getTextLine(params.line)
|
||
}
|
||
Context.save()
|
||
Context.beginPath()
|
||
this.setAlpha(alpha)
|
||
Context.font = font.style
|
||
if (!TYPES.includes(this.type)) {
|
||
Context.setTextBaseline(baseline)
|
||
}
|
||
if (typeof text != 'string') {
|
||
text += ''
|
||
}
|
||
let textArr = params.textArr
|
||
if (!textArr) {
|
||
textArr = this.computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign)
|
||
}
|
||
if (isFill) {
|
||
this.setFillStyle(color)
|
||
for (let i of textArr) {
|
||
let { text, x, y, tx, ty, tw } = i
|
||
Context.fillText(text, x, y)
|
||
if (line.lineStyle != 'none') {
|
||
this.drawLine({
|
||
x: tx,
|
||
y: ty,
|
||
w: tw,
|
||
color,
|
||
lineType: line.lineType,
|
||
lineWidth: line.lineWidth
|
||
}, true)
|
||
}
|
||
}
|
||
} else {
|
||
this.setStrokeStyle(color)
|
||
for (let i of textArr) {
|
||
let { text, x, y, tx, ty, tw } = i
|
||
Context.strokeText(text, x, y)
|
||
if (line.lineStyle != 'none') {
|
||
this.drawLine({
|
||
x: tx,
|
||
y: ty,
|
||
w: tw,
|
||
color,
|
||
lineType: line.lineType,
|
||
lineWidth: line.lineWidth
|
||
}, true)
|
||
}
|
||
}
|
||
}
|
||
Context.restore()
|
||
this.setAlpha(1)
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取字体样式
|
||
* @param { Object } font 字体
|
||
*/
|
||
getFont(font = {}) {
|
||
let { fontSize, fontFamily, fontStyle, fontVariant, fontWeight } = {
|
||
fontSize: font.size || 12,
|
||
fontFamily: font.family || 'sans-serif',
|
||
// 斜体: italic, 倾斜体:oblique
|
||
fontStyle: font.style || 'normal',
|
||
fontVariant: font.variant || 'normal',
|
||
// 粗体
|
||
fontWeight: font.weight || 'normal'
|
||
}
|
||
return {
|
||
fontSize, fontFamily, fontStyle, fontVariant, fontWeight,
|
||
style: `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}px ${fontFamily}`
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取文字line样式
|
||
* @param { Object } line 行高
|
||
*/
|
||
getTextLine(line = {}) {
|
||
return {
|
||
// 显示的行数 -1 不限制
|
||
lineNum: line.num || -1,
|
||
// 行高
|
||
lineHeight: line.height || 16,
|
||
// none: 不需要 underline: 下划线, lineThrough 删除线
|
||
lineStyle: line.style || 'none',
|
||
// 线的类型 dashed 虚线 solid 实线
|
||
lineType: line.type || 'solid',
|
||
// 线宽度
|
||
lineWidth: line.width || 1
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 画线
|
||
* @param { Object } params 绘制内容
|
||
*/
|
||
drawLine(params = {}) {
|
||
let { width, Context } = this
|
||
let { x, y, color, w, algin, alpha, lineType, pattern, offset, lineCap, lineWidth, windowAlign } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
w: params.w || width - params.x,
|
||
color: params.color || '#000000',
|
||
algin: params.algin || 'right',
|
||
alpha: params.alpha || 1,
|
||
// dashed 虚线 solid 实线
|
||
lineType: params.lineType || 'solid',
|
||
// 详看CanvasContext.setLineDash文档
|
||
// 一组描述交替绘制线段和间距(坐标空间单位)长度的数字
|
||
pattern: params.pattern || [5, 5],
|
||
// 虚线偏移量
|
||
offset: params.offset || 5,
|
||
lineWidth: params.lineWidth || 1,
|
||
lineCap: params.lineCap || 'butt',
|
||
// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none'
|
||
}
|
||
Context.beginPath()
|
||
this.setAlpha(alpha)
|
||
if (lineType == 'dashed') {
|
||
if (!TYPES.includes(this.type)) {
|
||
Context.setLineDash(pattern, offset)
|
||
} else {
|
||
Context.setLineDash(pattern)
|
||
Context.lineDashOffset = offset
|
||
}
|
||
}
|
||
if (!TYPES.includes(this.type)) {
|
||
Context.setLineCap(lineCap)
|
||
} else {
|
||
Context.lineCap = lineCap
|
||
}
|
||
this.setLineWidth(lineWidth)
|
||
this.setStrokeStyle(color)
|
||
switch (algin) {
|
||
case 'right':
|
||
if (windowAlign != 'none') {
|
||
x = this.computedCenterX(width, w, windowAlign)
|
||
}
|
||
Context.moveTo(x, y)
|
||
Context.lineTo(w + x, y)
|
||
break
|
||
case 'left':
|
||
if (windowAlign != 'none') {
|
||
x = this.computedCenterX(width, w, windowAlign)
|
||
}
|
||
Context.moveTo(x, y)
|
||
Context.lineTo(windowAlign == 'none' ? x - w : x + w, y)
|
||
break
|
||
case 'top':
|
||
Context.moveTo(x, y)
|
||
Context.lineTo(x, -(y + w))
|
||
break
|
||
case 'bottom':
|
||
Context.moveTo(x, y)
|
||
Context.lineTo(x, y + w)
|
||
break
|
||
}
|
||
Context.stroke()
|
||
Context.closePath()
|
||
if (!TYPES.includes(this.type)) {
|
||
Context.setLineDash()
|
||
} else {
|
||
Context.setLineDash([0, 0])
|
||
Context.lineDashOffset = 0
|
||
}
|
||
this.setAlpha(1)
|
||
}
|
||
|
||
|
||
/**
|
||
* 绘制二维码
|
||
* @param { Object } params 二维码参数
|
||
*/
|
||
drawQrCode(params = {}) {
|
||
let { Context, width } = this
|
||
return new Promise(async resolve => {
|
||
let { x, y, image, windowAlign, ...options } = {
|
||
x: params.x || 0,
|
||
y: params.y || 0,
|
||
text: String(params.text) || '',
|
||
size: params.size || 100,
|
||
// 容错级别 默认3
|
||
correctLevel: params.lv || 3,
|
||
// 二维码背景色
|
||
background: params.background || '#000000',
|
||
// 二维码前景色
|
||
foreground: params.foreground || '#ffffff',
|
||
// 二维码角标色
|
||
pdground: params.pdground || '#ffffff',
|
||
image: params.image || {},
|
||
// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
|
||
windowAlign: params.windowAlign || 'none'
|
||
}
|
||
if (windowAlign != 'none') {
|
||
x = this.computedCenterX(width, options.size, windowAlign)
|
||
}
|
||
//使用QRCodeAlg创建二维码结构
|
||
let qrcodeAlgObjCache = []
|
||
let qrCodeAlg = null
|
||
let l = qrcodeAlgObjCache.length
|
||
let d = 0
|
||
for (let i = 0;i < l; i++) {
|
||
d = i
|
||
if (qrcodeAlgObjCache[i].text == options.text && qrcodeAlgObjCache[i].text.correctLevel == options.correctLevel) {
|
||
qrCodeAlg = qrcodeAlgObjCache[i].obj
|
||
break
|
||
}
|
||
}
|
||
if (d == l) {
|
||
qrCodeAlg = new QRCodeAlg(options.text, options.correctLevel)
|
||
qrcodeAlgObjCache.push({
|
||
text: options.text,
|
||
correctLevel: options.correctLevel,
|
||
obj: qrCodeAlg
|
||
})
|
||
}
|
||
/**
|
||
* 计算矩阵点的前景色
|
||
* @param {Obj} config
|
||
* @param {Number} config.row 点x坐标
|
||
* @param {Number} config.col 点y坐标
|
||
* @param {Number} config.count 矩阵大小
|
||
* @param {Number} config.options 组件的options
|
||
* @return {String}
|
||
*/
|
||
let getForeGround = function (config) {
|
||
let options = config.options
|
||
if (options.pdground && (
|
||
(config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
|
||
(config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
|
||
(config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
|
||
)) {
|
||
return options.pdground
|
||
}
|
||
return options.foreground
|
||
}
|
||
let count = qrCodeAlg.getModuleCount()
|
||
let ratioSize = options.size
|
||
let ratioImgSize = image.size || 30
|
||
//计算每个点的长宽
|
||
let tileW = (ratioSize / count).toPrecision(4)
|
||
let tileH = (ratioSize / count).toPrecision(4)
|
||
//绘制
|
||
for (let row = 0; row < count; row++) {
|
||
for (let col = 0; col < count; col++) {
|
||
let w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW))
|
||
let h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW))
|
||
let foreground = getForeGround({
|
||
row: row,
|
||
col: col,
|
||
count: count,
|
||
options: options
|
||
})
|
||
this.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background)
|
||
Context.fillRect(x + Math.round(col * tileW), y + Math.round(row * tileH), w, h)
|
||
}
|
||
}
|
||
if (image.src) {
|
||
let { src, r, color, borderWidth, borderColor } = image
|
||
let dx = x + Number(((ratioSize - ratioImgSize) / 2).toFixed(2))
|
||
let dy = y + Number(((ratioSize - ratioImgSize) / 2).toFixed(2))
|
||
let drawImage = await this.drawImage({
|
||
x: dx,
|
||
y: dy,
|
||
w: ratioImgSize,
|
||
h: ratioImgSize,
|
||
src,
|
||
r,
|
||
color,
|
||
borderWidth,
|
||
borderColor,
|
||
drawType: 'rect',
|
||
isCompressImage: false,
|
||
}, true)
|
||
if (!drawImage.success) {
|
||
return resolve(drawImage)
|
||
}
|
||
}
|
||
return resolve({
|
||
success: true
|
||
})
|
||
})
|
||
}
|
||
|
||
|
||
/**
|
||
* 计算出文字一共有多少列,渲染位置之类
|
||
* @param { Number } x x轴
|
||
* @param { Number } y y轴
|
||
* @param { Number } w 文字宽度
|
||
* @param { String } text 文字内容
|
||
* @param { Number } textIndent 首行缩进
|
||
* @param { Number } lastWidth 最后一行的宽度
|
||
* @param { Object } font 字体
|
||
* @param { Object } line 行高
|
||
* @param { String } textAlign 文字对齐方式
|
||
* @param { String } windowAlign 窗口对齐方式
|
||
* @returns
|
||
*/
|
||
computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign) {
|
||
let { Context, width } = this
|
||
Context.font = font.style
|
||
let { fontSize } = font
|
||
let { lineNum, lineHeight, lineStyle } = line
|
||
// 文字的总长度
|
||
let textLength = countTextLength(Context, text, fontSize)
|
||
let temp = ''
|
||
let row = []
|
||
if (text.includes('\n')) {
|
||
let texts = text.split('\n')
|
||
for (let text of texts) {
|
||
computedTextLength(text)
|
||
}
|
||
} else {
|
||
computedTextLength(text)
|
||
}
|
||
function computedTextLength(text) {
|
||
for (let i in text) {
|
||
let tempLength = countTextLength(Context, temp, fontSize)
|
||
if (row.length == 0 && textIndent != 0 && textAlign == 'none' && windowAlign == 'none') {
|
||
tempLength += textIndent * fontSize
|
||
}
|
||
if (tempLength <= w && countTextLength(Context, (temp + text[i]), fontSize) <= w) {
|
||
temp += text[i]
|
||
} else {
|
||
row.push(temp)
|
||
temp = text[i]
|
||
}
|
||
if (i == text.length - 1) {
|
||
row.push(temp)
|
||
temp = ''
|
||
}
|
||
}
|
||
}
|
||
if (lineNum != -1 && lastWidth != 0 && row.length != 0 && row.length > lineNum) {
|
||
let lastText = row[lineNum - 1]
|
||
let temp = ''
|
||
for (let i in lastText) {
|
||
let tempLength = countTextLength(Context, temp, fontSize)
|
||
if (tempLength <= lastWidth && countTextLength(Context, (temp + lastText[i]), fontSize) <= lastWidth) {
|
||
temp += lastText[i]
|
||
continue
|
||
}
|
||
break
|
||
}
|
||
row[lineNum - 1] = temp
|
||
}
|
||
lineHeight = lineHeight == 1 ? fontSize + 2 : lineHeight
|
||
// 获取一共有多少列
|
||
let lineSize = Math.ceil(textLength / w)
|
||
if (text.includes('\n') && lineNum == -1) {
|
||
lineNum = row.length
|
||
} else if (text.includes('\n') && lineNum != -1) {
|
||
lineSize = row.length
|
||
lineNum = lineNum > lineSize ? lineSize : lineNum
|
||
} else if (lineNum != -1) {
|
||
lineNum = lineNum > lineSize ? lineSize : lineNum
|
||
}
|
||
let size = lineNum != -1 ? lineNum : lineSize
|
||
let textArr = []
|
||
for (let i = 0; i < size; i++) {
|
||
let obj = {}
|
||
let text = row[i]
|
||
let textLength = countTextLength(Context, text, fontSize)
|
||
let wx = x
|
||
let tx = x
|
||
if (i == 0 && textIndent != 0 && textAlign == 'none' && windowAlign == 'none') {
|
||
textLength += textIndent * fontSize
|
||
wx += textIndent * fontSize
|
||
tx = wx
|
||
}
|
||
if (textAlign != 'none' && textLength < w) {
|
||
tx = this.computedCenterX(w, textLength, textAlign)
|
||
wx = tx
|
||
}
|
||
if (windowAlign != 'none' && textAlign != 'none') {
|
||
wx = this.computedCenterX(width, w, windowAlign)
|
||
wx += tx
|
||
tx = wx
|
||
} else if (windowAlign != 'none') {
|
||
wx = this.computedCenterX(width, w, windowAlign)
|
||
tx = wx
|
||
}
|
||
if (text && lineNum != -1 && i == lineNum - 1) {
|
||
if ((textLength + fontSize) >= w) {
|
||
text = text.substring(0, text.length - 1) + '...'
|
||
} else if (lastWidth != 0 && (textLength + fontSize) >= lastWidth) {
|
||
text = text.substring(0, text.length - 1) + '...'
|
||
}
|
||
}
|
||
if (lineStyle != 'none') {
|
||
obj.tx = tx
|
||
obj.tw = textLength
|
||
if (lineStyle == 'underline') {
|
||
obj.ty = y + (i * lineHeight) + fontSize
|
||
}
|
||
if (lineStyle == 'lineThrough') {
|
||
obj.ty = y + (i * lineHeight) + fontSize / 2
|
||
}
|
||
}
|
||
obj.text = text
|
||
obj.x = wx
|
||
obj.y = y + (i * lineHeight)
|
||
text && textArr.push(obj)
|
||
}
|
||
return textArr
|
||
}
|
||
|
||
/**
|
||
* 计算内容需要显示在画布中间的x轴的位置
|
||
* @param { Number | String } boxWidth 容器的宽度
|
||
* @param { Number | String } contentWidth 内容宽度
|
||
* @param { String } type 类型 center: 水平 right: 右边
|
||
* @returns
|
||
*/
|
||
computedCenterX(boxWidth, contentWidth, type = 'center') {
|
||
if (type == 'center') {
|
||
return (boxWidth - contentWidth) / 2
|
||
}
|
||
if (type == 'right') {
|
||
return boxWidth - contentWidth
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置旋转角度
|
||
* @param { String } x x轴
|
||
* @param { String } y y轴
|
||
* @param { String } w 宽度
|
||
* @param { String } h 高度
|
||
* @param { Object } rotate 旋转内容
|
||
* @param { String } rotate.deg 旋转角度
|
||
* @param { String } rotate.type 类型 topLeft: 中心点在上左 topMiddle 中心点在上中 topRight 中心点在上右
|
||
* middleLeft: 中心点在中左 bottomMiddle 中心点在正中间 middleRight 中心点在中右
|
||
* bottomLeft: 中心点在下左 bottomMiddle 中心点在下中 middleRight 中心点在下右
|
||
*/
|
||
setRotate(x, y, w, h, rotate) {
|
||
let { Context } = this
|
||
let deg = rotate.deg || 0
|
||
let type = rotate.type || 'middle'
|
||
let rx = x
|
||
let ry = y
|
||
switch (type) {
|
||
case 'topLeft':
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'topMiddle':
|
||
rx = x + (w / 2)
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'topRight':
|
||
rx = x + w
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'bottomLeft':
|
||
ry = y + h
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'bottomMiddle':
|
||
rx = x + (w / 2)
|
||
ry = y + h
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'bottomRight':
|
||
rx = x + w
|
||
ry = y + h
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'middleLeft':
|
||
ry = y + (h / 2)
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'middleRight':
|
||
rx = x + w
|
||
ry = y + (h / 2)
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'middle':
|
||
rx = x + (w / 2)
|
||
ry = y + (h / 2)
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置三角形旋转角度
|
||
* @param { String } x x轴
|
||
* @param { String } y y轴
|
||
* @param { String } w 宽度
|
||
* @param { String } h 高度
|
||
* @param { Object } rotate 旋转内容
|
||
* @param { String } rotate.deg 旋转角度
|
||
* @param { String } rotate.type 类型 top: 上 left: 左 right: 右 middle: 中心
|
||
* @param { String } triangType 三角形类型(不支持自定义的三角形 ) right: 直角三角形 isosceles: 等腰三角形
|
||
*/
|
||
setTriangleRotate(x, y, w, h, rotate, triangType) {
|
||
let { Context } = this
|
||
let deg = rotate.deg || 0
|
||
let type = rotate.type || 'top'
|
||
let rx = x
|
||
let ry = y
|
||
switch (type) {
|
||
case 'top':
|
||
if (triangType == 'right') {
|
||
rx = x
|
||
ry = y
|
||
} else {
|
||
rx = x + w / 2
|
||
ry = y
|
||
}
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'left':
|
||
rx = x
|
||
ry = y + h
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'right':
|
||
rx = x + w
|
||
ry = y + h
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
case 'middle':
|
||
rx = x + w / 2
|
||
ry = y + h / 2
|
||
Context.translate(rx, ry)
|
||
Context.rotate(deg * Math.PI / 180)
|
||
Context.translate(-rx, -ry)
|
||
break
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 排序drawArray 根据数据的zIndex进行排序,修改渲染顺序
|
||
* @param { Array } drawArray 绘制内容
|
||
*/
|
||
sortDrawArray(drawArray) {
|
||
function compare() {
|
||
return function(after, current) {
|
||
let aZIndex = after.zIndex || 0
|
||
let cZIndex = current.zIndex || 0
|
||
return aZIndex - cZIndex
|
||
}
|
||
}
|
||
drawArray.sort(compare())
|
||
return drawArray
|
||
}
|
||
|
||
/**
|
||
* 往所有的回调信息里面添加内容
|
||
* @param { Object } params 内容
|
||
*/
|
||
setAllCallBack(params) {
|
||
let { width } = this
|
||
let { type, x, y, r, w, h, lineWidth, size, name } = params
|
||
w = w || width
|
||
h = h || 0
|
||
x = x || 0
|
||
y = y || 0
|
||
r = r || 0
|
||
lineWidth = lineWidth || 1
|
||
size = size || 0
|
||
name = name || ''
|
||
let sx = x
|
||
let sy = y
|
||
let ex = x + w
|
||
let ey = y + h
|
||
// 圆形
|
||
if (type == 'arc') {
|
||
ex = x + r * 2
|
||
ey = y + r * 2
|
||
w = r * 2
|
||
h = r * 2
|
||
}
|
||
// 文字
|
||
if (type == 'text') {
|
||
let { text, textIndent, lastWidth, font, line, textAlign, windowAlign } = {
|
||
text: String(params.text) || '',
|
||
textIndent: params.textIndent || 0,
|
||
lastWidth: params.lastWidth || 0,
|
||
font: this.getFont(params.font),
|
||
line: this.getTextLine(params.line),
|
||
textAlign: params.textAlign || 'none',
|
||
windowAlign: params.windowAlign || 'none',
|
||
}
|
||
if (w == width) {
|
||
w -= x
|
||
}
|
||
let textArr = this.computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign)
|
||
let lastText = textArr[textArr.length - 1]
|
||
ex = (lastText?.x || 0) + (font.fontSize * (lastText?.text.length || 0))
|
||
ey = (lastText?.y || 0) + font.fontSize
|
||
params.textArr = textArr
|
||
h = ey - sy
|
||
}
|
||
// 线
|
||
if (type == 'line') {
|
||
ey = y + lineWidth
|
||
h = lineWidth
|
||
}
|
||
// 二维码
|
||
if (type == 'qrcode') {
|
||
ex = x + size
|
||
ey = y + size
|
||
w = size
|
||
h = size
|
||
}
|
||
this.allCallBack.push({
|
||
sx,
|
||
sy,
|
||
ex,
|
||
ey,
|
||
w,
|
||
h,
|
||
name
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 绘制内容
|
||
* @returns
|
||
*/
|
||
drawCanvas(drawArray) {
|
||
let { Context } = this
|
||
return new Promise(async resolve => {
|
||
try {
|
||
for (let i of drawArray) {
|
||
if (i.callBack && typeof i.callBack == 'function' && i.type != 'custom') {
|
||
let beforeInfo = this.allCallBack.length == 0 ? {} : this.allCallBack[this.allCallBack.length - 1]
|
||
let callBackInfo = i.callBack(beforeInfo, this.allCallBack) || {}
|
||
let { callBack, ...data } = i
|
||
i = { ...data, ...callBackInfo }
|
||
}
|
||
if (i.type != 'custom' && i.drawType != 'custom') {
|
||
this.setAllCallBack(i)
|
||
}
|
||
switch (i.type) {
|
||
// 文字
|
||
case 'text':
|
||
this.drawText(i)
|
||
break
|
||
// 矩形
|
||
case 'rect':
|
||
this.drawRect(i)
|
||
break
|
||
// 图片
|
||
case 'image':
|
||
let image = await this.drawImage(i)
|
||
if (!image.success) {
|
||
return resolve(image)
|
||
}
|
||
break
|
||
// 圆形
|
||
case 'arc':
|
||
this.drawArc(i)
|
||
break
|
||
// 三角形
|
||
case 'triangle':
|
||
this.drawTriangle(i)
|
||
break
|
||
// 线条
|
||
case 'line':
|
||
this.drawLine(i)
|
||
break
|
||
// 二维码
|
||
case 'qrcode':
|
||
await this.drawQrCode(i)
|
||
break
|
||
// 自定义
|
||
case 'custom':
|
||
i.setDarw(Context, this)
|
||
break
|
||
}
|
||
}
|
||
resolve({
|
||
success: true
|
||
})
|
||
} catch(e) {
|
||
hideLoading()
|
||
return resolve({
|
||
success: false,
|
||
msg: '绘制内容失败:' + e
|
||
})
|
||
}
|
||
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 绘制背景
|
||
* @returns
|
||
*/
|
||
drawBackground() {
|
||
let { background, width, height, Context } = this
|
||
return new Promise(async resolve => {
|
||
let { type, ...params } = background
|
||
Context.beginPath()
|
||
Context.save()
|
||
// 绘制背景色
|
||
if (type == 'color') {
|
||
this.drawRect({
|
||
...params,
|
||
w: params.w || width,
|
||
h: params.h || height,
|
||
color: params.color || '#ffffff'
|
||
}, true)
|
||
}
|
||
// 绘制背景图
|
||
if (type == 'image') {
|
||
await this.drawImage({
|
||
...params,
|
||
w: params.w || width,
|
||
h: params.h || height,
|
||
}, true)
|
||
}
|
||
Context.clip()
|
||
Context.restore()
|
||
resolve({
|
||
success: true
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 创建canvas导出文件
|
||
* @returns
|
||
*/
|
||
createdCanvasFilePath() {
|
||
let { canvas, canvasId, width, height, _this, fileType, quality } = this
|
||
return new Promise(resolve => {
|
||
try {
|
||
uni.canvasToTempFilePath({
|
||
canvasId,
|
||
canvas,
|
||
x: 0,
|
||
y: 0,
|
||
width,
|
||
height,
|
||
quality,
|
||
fileType,
|
||
success: res => {
|
||
resolve({
|
||
success: true,
|
||
data: res.tempFilePath,
|
||
msg: '绘画成功'
|
||
})
|
||
},
|
||
fail: err => {
|
||
resolve({
|
||
success: false,
|
||
msg: `导出图片失败: ${JSON.stringify(err)}`
|
||
})
|
||
hideLoading()
|
||
}
|
||
}, _this || null)
|
||
} catch (e) {
|
||
hideLoading()
|
||
resolve({
|
||
success: false,
|
||
msg: '导出图片失败: 绘画错误' + e
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 预绘制背景
|
||
* @returns
|
||
*/
|
||
preDrawBackground() {
|
||
return new Promise(async resolve => {
|
||
try {
|
||
showLoading('初始化中...')
|
||
// 绘制背景
|
||
const drawBackground = await this.drawBackground()
|
||
if (!drawBackground.success) {
|
||
hideLoading()
|
||
return resolve({
|
||
success: false,
|
||
msg: '初始化失败,绘制背景图失败'
|
||
})
|
||
}
|
||
hideLoading()
|
||
return resolve({
|
||
success: true,
|
||
msg: '初始化成功'
|
||
})
|
||
} catch(e) {
|
||
hideLoading()
|
||
resolve({
|
||
success: false,
|
||
msg: e
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 将绘制的内容绘制到画布上
|
||
* @param { Boolean } complete 是否完成,如果为true会绘制图片并返回图片路径
|
||
* @returns
|
||
*/
|
||
canvasDraw(complete = true) {
|
||
let { Context, drawDelayTime, type } = this
|
||
return new Promise(async resolve => {
|
||
setTimeout(async () => {
|
||
hideLoading()
|
||
if (TYPES.includes(type)) {
|
||
if (complete) {
|
||
return resolve(await this.exportImage())
|
||
}
|
||
return resolve({
|
||
success: true,
|
||
msg: '成功'
|
||
})
|
||
} else {
|
||
await Context.draw(true, async () => {
|
||
if (complete) {
|
||
return resolve(await this.exportImage())
|
||
}
|
||
return resolve({
|
||
success: true,
|
||
msg: '成功'
|
||
})
|
||
})
|
||
}
|
||
}, drawDelayTime || 200)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 预绘画
|
||
* @param { Function } drawArray 绘制内容 返回一个数组
|
||
* @param { Boolean } complete 是否完成,如果为true会绘制图片
|
||
* @returns
|
||
*/
|
||
preDraw(drawArray, complete = false) {
|
||
return new Promise(async resolve => {
|
||
try {
|
||
let { callBack, drawTipsText } = this
|
||
showLoading(drawTipsText)
|
||
let drawCanvas = await this.drawCanvas(this.sortDrawArray(drawArray(callBack)))
|
||
// 绘制内容
|
||
if (!drawCanvas.success) {
|
||
hideLoading()
|
||
return resolve(drawCanvas)
|
||
}
|
||
return resolve(await this.canvasDraw(complete))
|
||
} catch(e) {
|
||
hideLoading()
|
||
resolve({
|
||
success: false,
|
||
msg: e
|
||
})
|
||
}
|
||
|
||
})
|
||
}
|
||
|
||
|
||
/**
|
||
* 导出图片
|
||
* @returns
|
||
*/
|
||
exportImage() {
|
||
let { canvasId, width, height, _this, delayTime } = this
|
||
return new Promise(resolve => {
|
||
showLoading('导出图片中...')
|
||
// 绘制图片
|
||
setTimeout(async () => {
|
||
resolve(await this.createdCanvasFilePath(canvasId, width, height, _this))
|
||
hideLoading()
|
||
}, delayTime || 200)
|
||
})
|
||
}
|
||
|
||
|
||
/**
|
||
* 创建绘制海报
|
||
* @param { Function } drawArray 绘制内容 返回一个数组
|
||
* @returns
|
||
*/
|
||
createdSharePoster(drawArray) {
|
||
let { callBack } = this
|
||
return new Promise(async resolve => {
|
||
if (drawArray == null || drawArray == undefined) {
|
||
return resolve({
|
||
success: false,
|
||
msg: '请传递绘制内容'
|
||
})
|
||
}
|
||
let backgroundRes = await this.preDrawBackground()
|
||
if (!backgroundRes.success) {
|
||
return resolve(backgroundRes)
|
||
}
|
||
showLoading('绘制中...')
|
||
let drawRes = await this.drawCanvas(this.sortDrawArray(await drawArray(callBack)))
|
||
// 绘制内容
|
||
if (!drawRes.success) {
|
||
hideLoading()
|
||
return resolve(drawRes)
|
||
}
|
||
return resolve(await this.canvasDraw())
|
||
})
|
||
}
|
||
} |