123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- // 绘制文字
- import DrawText from './methods/text'
- // 绘制线条
- import DrawLine from './methods/line'
- // 绘制矩形
- import DrawRect from './methods/rect'
- // 绘制圆形
- import DrawArc from './methods/arc'
- // 绘制图片
- import DrawImage, { getModeImage, getImageSrc, getImageInfo } from './methods/image'
- // 绘制三角形
- import DrawTriangle from './methods/triangle'
- // 绘制二维码
- import DrawQrcode from './methods/qrcode'
- import Canvas from './methods/canvas'
- // 公共绘制方法 公共工具方法
- import { CommonDrawMethods, CommonUtilMethods, sleep } from './utils/common'
- // 绘制海报方法
- export default class DrawPoster {
- constructor(options) {
- const {
- width = 375.0001, height = 0, canvasId = null, _this = null, background = {},
- type = 'default', unit = 'px', drawDelayTime = 200, exportImageDelayTime = 200,
- showDrawTips = true, drawTipsText = '绘制中...', showExportImagesTips = true, exportImageTips = '导出图片中...',
- fontStyle = {}, exportImageStyle = {}
- } = options
- // 画布背景参数
- this.background = {
- type: 'color',
- w: width,
- h: height,
- color: '#ffffff',
- ...background
- }
- // 画布id
- this.canvasId = canvasId
- // 当前页面vm
- this._this = _this
- // 绘制类型 default: 默认 2d: 2d绘制
- this.type = type
- // h5/APP无论设置什么都为default
- // #ifdef H5 || APP-PLUS
- this.type = 'default'
- // #endif
- // 是否是2d绘制
- this.is2d = this.type === '2d'
- // 绘制单位
- this.unit = unit
- // 绘制时的延迟时间
- this.drawDelayTime = drawDelayTime
- // 导出图片时的延迟时间
- this.exportImageDelayTime = exportImageDelayTime
- // 是否显示绘制时的提示
- this.showDrawTips = showDrawTips
- // 绘制时的提示
- this.drawTipsText = drawTipsText
- // 是否显示导出图片时的提示
- this.showExportImagesTips = showExportImagesTips
- // 导出图片时的提示
- this.exportImageTips = exportImageTips
- // 默认字体样式
- this.fontStyle = {
- size: this.type === 'default' ? 16 : 32,
- family: 'sans-serif',
- style: 'normal',
- variant: 'normal',
- weight: 'normal',
- ...fontStyle
- }
- // 公共方法实例
- this.commonUtilMethods = new CommonUtilMethods({
- unit,
- type: this.type,
- width,
- height,
- fontStyle: this.fontStyle,
- })
- // 设置画布宽高
- this.setCanvasStyle(width, height)
- // 导出图片时的大小/位置, 默认canvas宽高
- this.exportImageStyle = {
- width,
- height,
- x: 0,
- y: 0,
- fileType: 'png',
- quality: 1,
- ...exportImageStyle,
- }
- // 所有的callback回调
- this.allCallback = []
- // 存储所有的监听事件
- this.$eventsMap = new Map()
- // 创建绘制对象-->Context
- this.createContext()
- this.drawCallback = null
- }
- /**
- * 创建绘制对象
- */
- async createContext() {
- const { width, height, canvasId, _this, type, is2d } = this
- const setValue = (Context, canvas = null) => {
- this.Context = Context
- this.canvas = canvas
- // 公共绘制方法
- this.commonDrawMethods = new CommonDrawMethods(Context, type, this.commonUtilMethods)
- this.commonUtilMethods.Context = Context
- this.commonUtilMethods.canvas = canvas
- this.initDrawMethods()
- this.$emit('init')
- }
- const dpr = uni.getSystemInfoSync().pixelRatio
- this.dpr = dpr
- if (!is2d) {
- const Context = uni.createCanvasContext(canvasId, _this)
- await sleep(50)
- setValue(Context)
- } else {
- uni.createSelectorQuery()
- .select(`#${canvasId}`)
- .fields({ node: true, size: true })
- .exec(res => {
- const canvas = res[0].node
- const Context = canvas.getContext(type)
- canvas.width = this.commonUtilMethods.getConvertedValue(width) * dpr
- canvas.height = this.commonUtilMethods.getConvertedValue(height) * dpr
- Context.scale(dpr, dpr)
- setValue(Context, canvas)
- })
- }
- }
- /**
- * 初始化绘制方法
- */
- initDrawMethods() {
- const { Context, commonUtilMethods, commonDrawMethods } = this
- const initParams = {
- Context, commonUtilMethods, commonDrawMethods
- }
- // 创建canvas方法->导出图片之类的
- this.canvasMethods = new Canvas(this)
- // 绘制文字
- const drawText = new DrawText(initParams)
- // 绘制线条
- const drawLine = new DrawLine(initParams)
- // 绘制矩形
- const drawRect = new DrawRect(initParams)
- // 绘制圆形
- const drawArc = new DrawArc(initParams)
- // 绘制图片
- const drawImage = new DrawImage(initParams)
- // 绘制三角形
- const drawTriangle = new DrawTriangle(initParams)
- // 绘制二维码
- const drawQrcode = new DrawQrcode(initParams)
- // 绘制方法集合
- this.commonDrawMethods.drawParams = {
- drawText,
- drawLine,
- drawRect,
- drawArc,
- drawImage,
- drawTriangle,
- drawQrcode
- }
- this.drawText = drawText
- this.drawLine = drawLine
- this.drawRect = drawRect
- this.drawArc = drawArc
- this.drawImage = drawImage
- this.drawTriangle = drawTriangle
- this.drawQrcode = drawQrcode
- }
- /**
- * 设置导出图片的大小
- */
- setExportImageStyle(options) {
- this.exportImageStyle = {
- ...this.exportImageStyle,
- ...options
- }
- }
- /**
- * 设置背景图片样式
- * @param { Object } options
- */
- setBackgroundStyle(options) {
- this.background = {
- ...this.background,
- ...options
- }
- }
- /**
- * 设置画布宽高
- * @param { Number } width 宽度
- * @param { Number } height 高度
- */
- async setCanvasStyle(width, height) {
- this.width = width
- this.height = height
- this.commonUtilMethods.width = width
- this.commonUtilMethods.height = height
- this.commonUtilMethods.setCanvasStyle()
- // 回调
- this.callbackInfo = {
- bgObj: {
- width: this.background.w,
- height: this.background.h
- },
- ctxObj: {
- width,
- height
- },
- }
- this.setExportImageStyle({
- width,
- height
- })
- if (this.canvas) {
- const { dpr, canvas, Context, commonUtilMethods } = this
- // const canvasData = Context.getImageData(0, 0, canvas.width, canvas.height)
- canvas.width = commonUtilMethods.getConvertedValue(width) * dpr
- canvas.height = commonUtilMethods.getConvertedValue(height) * dpr
- Context.scale(dpr, dpr)
- // Context.putImageData(canvasData, 0, 0)
- await this.drawBackground(false)
- if (this.drawArrayFn) {
- await this.drawCanvas(this.drawArrayFn(this.callbackInfo), false)
- }
- }
- }
- /**
- * 监听绘制时/绘制完成的事件
- * @param { String } type
- * @param { Function } callback 回调
- */
- $on(type = '', callback = null) {
- if (!type || !callback) return
- if (typeof callback !== 'function') return
- let eventSet = this.$eventsMap.get(type)
- if (!eventSet) {
- this.$eventsMap.set(type, (eventSet = new Set()))
- }
- eventSet.add(callback)
- }
- /**
- * 触发用户传入的事件
- * @param { String } type 什么类型
- * @param { Array } args 其余参数
- */
- $emit(type = '', ...args) {
- if (!type) return
- const eventSet = this.$eventsMap.get(type)
- if (eventSet) {
- eventSet.forEach(event => {
- event.apply(this, args)
- })
- }
- }
- /**
- * 绘制背景
- * @param { Boolean } isSendEvent 是否发送绘制完成事件
- * @returns
- */
- drawBackground(isSendEvent = true) {
- const { background, width, height, Context, drawRect, drawImage, unit, commonUtilMethods } = this
- // console.log(Context)
- return new Promise(async resolve => {
- const { type, ...params } = background
- // Context.beginPath()
- // Context.save()
- // console.log({
- // ...params,
- // w: params.w ?? width,
- // h: params.h ?? height,
- // })
- if (!isSendEvent) {
- this.canvasMethods.clearCanvas()
- }
- let result = {}
- if (type === 'color') {
- result = drawRect.draw({
- ...params,
- w: params.w ?? width,
- h: params.h ?? height,
- color: params.color ?? '#ffffff'
- })
- } else if (type === 'image') {
- result = await drawImage.draw({
- ...params,
- w: params.w ?? width,
- h: params.h ?? height,
- })
- }
- result.style = {
- width: result.w + unit,
- height: result.h + unit,
- }
- this.background.w = result.w
- this.background.h = result.h
- if (isSendEvent) {
- this.$emit('background', result)
- }
- // Context.clip()
- // Context.restore()
- resolve(result)
- })
- }
- /**
- * 往所有的回调信息里面添加内容
- * @param { Object } params 内容
- */
- async setAllCallBack(params) {
- const { width, commonUtilMethods, commonDrawMethods } = this
- const { canvasWidth } = commonUtilMethods
- let {
- type, x = 0, y = 0, r = 0, w = canvasWidth, h = 0,
- lineWidth = 1, size = 0, name = '', windowAlign = 'none',
- drawType = 'default', mode = 'aspectFill', src = '', offsetRight = 0
- } = params
- let sx = x
- let sy = y
- let ex = x + w
- let ey = y + h
- // 文字
- if (type === 'text') {
- let {
- text, textIndent, lastWidth, font, line, textAlign, windowAlign, color, highlightText
- } = this.drawText.getDrawParams(params, false)
- const textArr = commonDrawMethods.computedFontTextLineHeight({
- x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign, defaultColor: color, offsetRight, highlightText
- })
- const lastText = textArr[textArr.length - 1]
- const firstText = textArr[0]
- ey = lastText.ey
- ex = firstText.tx + firstText.w
- params.textArr = commonUtilMethods.conversionUnit(textArr)
- params.h = ey - sy
- params.tw = firstText.w
- // if (params.name) {
- // console.log(params.text)
- // console.log(font)
- // console.log(textArr)
- // }
- } else if (type === 'arc') {
- ex = x + r * 2
- ey = y + r * 2
- w = r * 2
- h = r * 2
- } else if (type === 'line') {
- ey = y + lineWidth
- h = lineWidth
- } else if (type === 'qrcode') {
- ex = x + size
- ey = y + size
- w = size
- h = size
- } else if (type === 'image') {
- if (windowAlign !== 'none') {
- x = commonDrawMethods.computedCenterX(width, w, windowAlign, offsetRight)
- }
- const srcRes = await getImageSrc(src)
- if (!srcRes.success) {
- return srcRes
- }
- src = srcRes.src
- const imageInfo = await getImageInfo(src)
- if (!imageInfo.success) {
- return Promise.resolve(true)
- }
- const modeImage = getModeImage(Number(imageInfo.width), Number(imageInfo.height), commonUtilMethods.getConvertedValue(x), commonUtilMethods.getConvertedValue(y), commonUtilMethods.getConvertedValue(w), commonUtilMethods.getConvertedValue(h), mode)
- const { dx, dy, dw, dh, sw, sh, sx, sy } = modeImage
- if (mode === 'widthFix') {
- h = this.unit === 'rpx' ? commonUtilMethods.pxToRpx(sh) : sh
- ey = y + h
- } else if (mode === 'heightFix') {
- w = this.unit === 'rpx' ? commonUtilMethods.pxToRpx(sw) : sw
- ex = x + w
- }
- if (drawType === 'arc' && h == 0) {
- h = w
- ey = y + h
- }
- params.drawModeImage = modeImage
- params.drawSrc = src
- params.drawImageInfo = imageInfo
- }
- params.sx = sx
- params.sy = sy
- params.ex = ex
- params.ey = ey
- this.allCallback.push({
- sx,
- sy,
- ex,
- ey,
- w,
- h,
- name,
- before: params.before || {}
- })
- return Promise.resolve(true)
- }
- /**
- * 绘制内容
- * @param { Array } drawArray 绘制数组
- * @param { Boolean } isSendEvent 是否发送事件
- */
- drawCanvas(drawArray, isSendEvent = true) {
- const { Context, showDrawTips, showExportImagesTips } = this
- return new Promise(async resolve => {
- try {
- for (let i of drawArray) {
- if (i.callback && typeof i.callback === 'function' && i.type !== 'custom') {
- const beforeInfo = this.allCallback.length == 0 ? {} : this.allCallback[this.allCallback.length - 1]
- const callBackInfo = i.callback(beforeInfo, this.allCallback) || {}
- const { callback, ...data } = i
- i = { ...data, ...callBackInfo }
- }
- if (i.type !== 'custom' && i.drawType !== 'custom') {
- await this.setAllCallBack(i)
- }
- switch (i.type) {
- // 文字
- case 'text':
- this.drawText.draw(i)
- break
- // 矩形
- case 'rect':
- this.drawRect.draw(i)
- break
- // 图片
- case 'image':
- const image = await this.drawImage.draw(i)
- if (!image.success) {
- return resolve(image)
- }
- break
- // 圆形
- case 'arc':
- this.drawArc.draw(i)
- break
- // 三角形
- case 'triangle':
- this.drawTriangle.draw(i)
- break
- // 线条
- case 'line':
- this.drawLine.draw(i)
- break
- // 二维码
- case 'qrcode':
- await this.drawQrcode.draw(i)
- break
- // 自定义
- case 'custom':
- i.setDraw(Context, this)
- break
- }
- }
- resolve({
- success: true
- })
- if (!isSendEvent) return
- // 绘制完成触发完成事件
- const { width, height, } = this
- const contentHeight = this.allCallback.reduce((a, data) => Math.max(a, data.ey), 0)
- const contentWidth = this.allCallback.reduce((a, data) => Math.max(a, data.ex), 0)
- this.$emit('drawComplete', {
- width, height,
- contentHeight: contentHeight,
- contentWidth: contentWidth
- })
- } catch (e) {
- return resolve({
- success: false,
- msg: '绘制内容失败:' + e
- })
- } finally {
- if (showDrawTips || showExportImagesTips) {
- uni.hideLoading()
- }
- }
- })
- }
- /**
- * 绘制海报
- * @param { Function } drawArrayFn 绘制内容 返回一个数组
- */
- drawPoster(drawArrayFn) {
- const { callbackInfo, showDrawTips, drawTipsText, showExportImagesTips } = this
- return new Promise(async resolve => {
- if (!drawArrayFn || typeof drawArrayFn !== 'function') {
- return resolve({
- success: false,
- msg: 'drawPoster参数错误'
- })
- }
- if (this.drawArrayFn) {
- this.allCallback = []
- this.canvasMethods.clearCanvas()
- await this.drawBackground(false)
- }
- this.drawArrayFn = drawArrayFn
- showDrawTips && uni.showLoading({ title: drawTipsText })
- const result = await this.drawCanvas(drawArrayFn(callbackInfo))
- // 绘制内容
- if (!result.success) {
- return resolve(result)
- }
- resolve(await this.canvasMethods.canvasDraw())
- if (showDrawTips || showExportImagesTips) {
- uni.hideLoading()
- }
- })
- }
- }
|