一:业务代码
1:小程序(微信/支付宝小程序)版
步骤详解:1:使用canvas,画出所需;2:根据canvas生成url,绑定到image上
import { useEffect } from 'react'
import { useSelector } from 'react-redux'
import Taro from '@tarojs/taro'
import { View, Text, Image, Canvas } from '@tarojs/components'
import { useAsyncCallback } from '@/hooks'
import { classNames, authSetting, showToast, isWeixin, isAlipay } from '@/utils'
import GoodsDetailPoster from './dw-goodsdetail'
import './index.scss'
const initialState = {
poster: null,
pxWidth: 200,
pxHeight: 200,
factor: 1,
eleId: 'poster-canvas',
ctx: null
}
function SpPoster(props) {
const { info, type, onClose = () => { } } = props
const { userInfo } = useSelector((state) => state.user)
const { userInfo: guideInfo } = useSelector((state) => state.guide)
const [state, setState] = useAsyncCallback(initialState)
const { poster, pxWidth, pxHeight, eleId, ctx } = state
useEffect(() => {
handleCreatePoster()
}, [])
/**
* @description rpx => px 基础方法
* @param { number } rpx - 需要转换的数值
* @param { boolean} int - 是否为 int
* @param { number } [factor] - 转化因子
* @returns { number }
*/
const toPx = (rpx, int, factor = 1) => {
if (int) {
return parseInt(rpx * factor)
}
return rpx * factor
}
/**
* @description px => rpx
* @param { number } px - 需要转换的数值
* @param { boolean} int - 是否为 int
* @param { number } [factor] - 转化因子
* @returns { number }
*/
const toRpx = (px, int, factor = 1) => {
if (int) {
return parseInt(px / factor)
}
return px / factor
}
const handleCreatePoster = async () => {
Taro.showLoading({
title: '海报生成中...'
})
const ctx = Taro.createCanvasContext(eleId, Taro.getCurrentInstance().page)
let canvasObj
switch (type) {
case 'goodsDetial':
canvasObj = new GoodsDetailPoster({
ctx,
info,
userInfo,
toPx,
toRpx
})
break
default:
break
}
const { canvasWidth, canvasHeight } = canvasObj.getCanvasSize()
setState(
(draft) => {
draft.pxWidth = canvasWidth
draft.pxHeight = canvasHeight
draft.ctx = ctx
},
async (_state) => {
await canvasObj.drawPoster()
const poster = await getPoster(_state)
console.log('handleCreatePoster:getPoster', poster)
Taro.hideLoading()
setState((draft) => {
draft.poster = poster
})
}
)
}
const getPoster = ({ ctx, pxWidth, pxHeight, eleId }) => {
return new Promise((resolve, reject) => {
try {
ctx.draw(false, async () => {
if (isWeixin) {
const { tempFilePath } = await Taro.canvasToTempFilePath(
{
x: 0,
y: 0,
width: pxWidth,
height: pxHeight,
canvasId: eleId
},
Taro.getCurrentInstance().page
)
resolve(tempFilePath)
} else if(isAlipay) {
my.canvasToTempFilePath({
canvasId: eleId,
success: ({ apFilePath }) => {
console.log('success', apFilePath);
resolve(apFilePath)
}
});
}
})
} catch (e) {
console.error(e)
reject(e)
}
})
}
const saveToAlbum = () => {
authSetting(
'writePhotosAlbum',
() => {
savePoster()
},
() => {
showToast('请设置允许保存到相册')
}
)
}
const savePoster = () => {
Taro.saveImageToPhotosAlbum({
filePath: poster,
success: (res) => {
showToast('保存成功')
},
fail: (res) => {
console.log(res)
showToast('保存失败')
}
})
}
return (
<View className={classNames('sp-poster')}>
<View className='share-panel__overlay'></View>
{poster && (
<View className='share-panel__poster'>
<View className='poster-container'>
<Image className='poster' src={poster} mode='scaleToFill' />
</View>
<View className='poster-ft'>
<Text className='iconfont icon-guanbi' onClick={onClose}></Text>
<View className='poster-save' onClick={saveToAlbum}>
<Text className='iconfont icon-download'></Text>
保存图片
</View>
</View>
</View>
)}
<Canvas
className='canvasbox'
canvasId='poster-canvas'
id='poster-canvas'
width={pxWidth}
height={pxHeight}
style={`width:${pxWidth}px; height:${pxHeight}px;`}
/>
</View>
)
}
export default SpPoster
import Taro, { Component } from '@tarojs/taro'
import api from '@/api'
import { getExtConfigData, isAlipay, } from '@/utils'
import { drawText, drawImage, drawBlock } from './helper'
//计算canvas画布尺寸
let canvasWidth = 600
let canvasHeight = 960
class GoodsDetailPoster {
constructor(props) {
const { ctx, info, userInfo, toPx, toRpx, canvas } = props
this.ctx = ctx
this.info = info
this.userInfo = userInfo
this.toPx = toPx
this.toRpx = toRpx
// ctx.scale(dpr, dpr)
// // alipay2.0 兼容
// this.canvas = canvas
}
getCanvasSize() {
return {
canvasWidth: canvasWidth,
canvasHeight: canvasHeight
}
}
async drawPoster() {
const host = process.env.APP_BASE_URL.replace('/api/h5app/wxapp', '')
const { appid, company_id } = getExtConfigData()
const { itemId, imgs, price } = this.info
const { user_id, avatar } = this.userInfo
let wxappCode
// TODO 获取微信二维码的接口,需要换alipay https://ecshopx1.shopex123.com/api/h5app/alipaymini/qrcode.png?company_id=1&page=page/index
// const res = await api.alipay.alipay_qrcode(`page=${`pages/item/espier-detail`}&appid=${appid}&company_id=${company_id}&id=${itemId}&uid=${user_id}`)
const res = await Taro.request({
url: `${host}/api/h5app/alipaymini/qrcode.png?page=${`pages/item/espier-detail`}&appid=${appid}&company_id=${company_id}&id=${itemId}&uid=${user_id}`, //仅为示例,并非真实的接口地址
header: {
'content-type': 'application/json' // 默认值
},
})
wxappCode = res.data.data.qr_code_url
const pic = imgs[0].replace('http:', 'https:')
// 商品图片
this.goodsImg = await Taro.getImageInfo({ src: pic })
// 太阳码
this.codeImg = await Taro.getImageInfo({ src: wxappCode })
// 头像
const _avatar = avatar || `${process.env.APP_IMAGE_CDN}/user_icon.png`
this.avatar = await Taro.getImageInfo({ src: _avatar })
const drawOptions = {
ctx: this.ctx,
toPx: this.toPx,
toRpx: this.toRpx
}
this.drawOptions = drawOptions
const { username } = this.userInfo
drawBlock(
{
x: 0,
y: 0,
width: canvasWidth,
height: canvasHeight,
backgroundColor: '#fff'
},
drawOptions
)
console.log('海报商品图:', this.goodsImg)
console.log('太阳码:', this.codeImg)
console.log('头像:', this.avatar, avatar)
// 海报商品图
drawImage(
{
imgPath: this.goodsImg.path,
x: 0,
y: 0,
w: canvasWidth,
h: canvasWidth,
sx: 0,
sy: 0,
sw: this.goodsImg.width,
sh: this.goodsImg.height
},
drawOptions
)
// 头像背景
drawBlock(
{
x: 24,
y: 624,
width: 312,
height: 40 * 2,
backgroundColor: '#efefef',
borderRadius: 80
},
drawOptions
)
// 头像
drawImage(
{
imgPath: this.avatar.path,
x: 24,
y: 624,
w: 80,
h: 80,
sx: 0,
sy: 0,
sw: this.avatar.width,
sh: this.avatar.height,
borderRadius: 80
},
drawOptions,
)
// 姓名
drawText(
{
x: 112,
y: 656,
fontSize: 24,
color: '#000',
text: username
},
drawOptions
)
//
drawText(
{
x: 112,
y: 688,
fontSize: 22,
color: '#999',
text: '推荐一个好物给你'
},
drawOptions
)
// 商品金额
const initPrice = price.toFixed(2).split('.')[0]
const floatPrice = `.${price.toFixed(2).split('.')[1]}`
drawText(
{
x: 24,
y: 815,
color: '#222',
text: [
{
text: '¥',
fontSize: 28,
color: '#222'
},
{
text: initPrice,
fontSize: 46,
color: '#222'
},
{
text: floatPrice,
fontSize: 32,
color: '#222'
}
]
},
drawOptions
)
// 商品名称
drawText(
{
x: 24,
y: 887,
fontSize: 24,
width: 312,
color: '#666',
text: this.info.itemName,
lineNum: 2
},
drawOptions
)
// 太阳码
drawImage(
{
imgPath: this.codeImg.path,
x: 416,
y: 742,
w: 160,
h: 195,
sx: 0,
sy: 0,
sw: this.codeImg.width,
sh: this.codeImg.height
},
drawOptions,
)
}
}
export default GoodsDetailPoster
2:h5
import { useEffect, useRef, useDidShow } from 'react'
import { SpImage } from '@/components'
import Taro, { useReady } from '@tarojs/taro'
import { Image, Canvas, Text, View } from '@tarojs/components'
import { useImmer } from 'use-immer'
import { drawText, drawImage, drawBlock, drawLine } from './helper'
import './comgm.scss'
//h5图片
const imgurl = new window.Image()
imgurl.setAttribute('crossOrigin', 'anonymous')
imgurl.src =
'https://ecshopx1.yuanyuanke.cn/image/34/2023/08/05/536550ac2434d3a8f0f883018ea4d302aKxnpYwnUYFgTrOPhf66Q5CA1vA4xoXd'
const initState = {
logoImg: ''
}
function Test(props) {
const canvasWidth = 610
const canvasHeight = 1085
const { show, handleHidden = () => {} } = props
const [state, setState] = useImmer(initState)
const {logoImg} = state
useEffect(() => {
if (show) getDraw()
return () => {
setState((draft) => {
draft.logoImg = null
})
}
}, [show])
const canvasRef = useRef(null)
/**
* @description rpx => px 基础方法
* @param { number } rpx - 需要转换的数值
* @param { boolean} int - 是否为 int
* @param { number } [factor] - 转化因子
* @returns { number }
*/
const toPx = (rpx, int, factor = 1) => {
if (int) {
return parseInt(rpx * factor)
}
return rpx * factor
}
/**
* @description px => rpx
* @param { number } px - 需要转换的数值
* @param { boolean} int - 是否为 int
* @param { number } [factor] - 转化因子
* @returns { number }
*/
const toRpx = (px, int, factor = 1) => {
if (int) {
return parseInt(px / factor)
}
return px / factor
}
const getDraw = async () => {
const ctx = Taro.createCanvasContext('order1')
const dom = await document.getElementsByTagName('canvas')[0]
dom.width = canvasWidth
dom.height = canvasHeight
const drawOptions = {
ctx: ctx,
toPx: toPx,
toRpx: toRpx
}
drawBlock(
{
x: 0,
y: 0,
width: canvasWidth,
height: canvasHeight,
backgroundColor: '#FFFFFF',
borderRadius: 20
},
drawOptions
)
drawBlock(
{
x: 32,
y: 16,
width: 546,
height: 120,
backgroundColor: '#f5f5f5',
borderRadius: 0
},
drawOptions
)
drawText(
{
x: 124,
width: 600,
y: 80,
fontSize: 32,
color: '#000',
text: 'name:'
},
drawOptions
)
drawText(
{
x: 250,
width: 120,
y: 80,
fontSize: 32,
color: '#000',
text: '小明'
},
drawOptions
)
drawLine(
{
startX: 32,
startY: 140,
endX: 546,
endY: 140,
color: '#ddd',
width: 1
},
drawOptions
)
drawImage(
{
imgPath: imgurl,
x: 116,
y: 325,
w: 380,
h: 380,
sx: 0,
sy: 0,
sw: imgurl.width,
sh: imgurl.height,
hasWidth: true
},
drawOptions
)
ctx.draw()
setTimeout(() => {
handleDown()
})
}
const handleDown = async () => {
const canvas = await document.getElementsByTagName('canvas')[0]
const dataURL = canvas.toDataURL('image/png')
setState((draft) => {
draft.logoImg = dataURL
})
}
return (
<View
className='order-poster'
style={show ? { top: '0px', left: '0px' } : {}}
onClick={handleHidden}
>
{logoImg && (
<SpImage onClick={e=>e.stopPropagation()} className='order-canvas' src={logoImg} height={canvasHeight} width={canvasWidth} />
)}
{!logoImg && (
<Canvas
canvasId='order1'
className='order-canvas'
style={{
width: `${Taro.pxTransform(canvasWidth)}`,
height: `${Taro.pxTransform(canvasHeight)}`
}}
ref={canvasRef}
></Canvas>
)}
</View>
)
}
export default Test
二:utils function
直接使用即可
/**
* @description 绘制圆角矩形
* @param { object } drawData - 绘制数据
* @param { number } drawData.x - 左上角x坐标
* @param { number } drawData.y - 左上角y坐标
* @param { number } drawData.w - 矩形的宽
* @param { number } drawData.h - 矩形的高
* @param { number } drawData.r - 圆角半径
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function _drawRadiusRect(drawData, drawOptions) {
const { x, y, w, h, r } = drawData
const {
ctx,
toPx
// toRpx,
} = drawOptions
const br = r / 2
ctx.beginPath()
ctx.moveTo(toPx(x + br), toPx(y)) // 移动到左上角的点
ctx.lineTo(toPx(x + w - br), toPx(y))
ctx.arc(toPx(x + w - br), toPx(y + br), toPx(br), 2 * Math.PI * (3 / 4), 2 * Math.PI * (4 / 4))
ctx.lineTo(toPx(x + w), toPx(y + h - br))
ctx.arc(toPx(x + w - br), toPx(y + h - br), toPx(br), 0, 2 * Math.PI * (1 / 4))
ctx.lineTo(toPx(x + br), toPx(y + h))
ctx.arc(toPx(x + br), toPx(y + h - br), toPx(br), 2 * Math.PI * (1 / 4), 2 * Math.PI * (2 / 4))
ctx.lineTo(toPx(x), toPx(y + br))
ctx.arc(toPx(x + br), toPx(y + br), toPx(br), 2 * Math.PI * (2 / 4), 2 * Math.PI * (3 / 4))
}
/**
* @description 计算文本长度
* @param { Array | Object } text 数组 或者 对象
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function _getTextWidth(text, drawOptions) {
const { ctx, toPx, toRpx } = drawOptions
let texts = []
if (Object.prototype.toString.call(text) === '[object Object]') {
texts.push(text)
} else {
texts = text
}
let width = 0
// eslint-disable-next-line no-shadow
texts.forEach(({ fontSize, text, marginLeft = 0, marginRight = 0 }) => {
ctx.setFontSize(toPx(fontSize))
width += ctx.measureText(text).width + marginLeft + marginRight
})
return toRpx(width)
}
/**
* @description 渲染一段文字
* @param { object } drawData - 绘制数据
* @param { number } drawData.x - x坐标 rpx
* @param { number } drawData.y - y坐标 rpx
* @param { number } drawData.fontSize - 文字大小 rpx
* @param { number } [drawData.color] - 颜色
* @param { string } [drawData.baseLine] - 基线对齐方式 top| middle|bottom
* @param { string } [drawData.textAlign='left'] - 对齐方式 left|center|right
* @param { string } drawData.text - 当Object类型时,参数为 text 字段的参数,marginLeft、marginRight这两个字段可用
* @param { number } [drawData.opacity=1] - 1为不透明,0为透明
* @param { string } [drawData.textDecoration='none']
* @param { number } [drawData.width] - 文字宽度 没有指定为画布宽度
* @param { number } [drawData.lineNum=1] - 根据宽度换行,最多的行数
* @param { number } [drawData.lineHeight=0] - 行高
* @param { string } [drawData.fontWeight='normal'] - 'bold' 加粗字体,目前小程序不支持 100 - 900 加粗
* @param { string } [drawData.fontStyle='normal'] - 'italic' 倾斜字体
* @param { string } [drawData.fontFamily="sans-serif"] - 小程序默认字体为 'sans-serif', 请输入小程序支持的字体
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function _drawSingleText(drawData, drawOptions) {
const {
x,
y,
fontSize,
color,
baseLine,
textAlign = 'left',
text,
opacity = 1,
textDecoration = 'none',
width,
lineNum = 1,
lineHeight = 0,
fontWeight = 'normal',
fontStyle = 'normal',
fontFamily = 'sans-serif'
} = drawData
// console.log(drawData)
const { ctx, toPx, toRpx } = drawOptions
ctx.save()
ctx.beginPath()
ctx.font = fontStyle + ' ' + fontWeight + ' ' + toPx(fontSize, true) + 'px ' + fontFamily
ctx.setGlobalAlpha(opacity)
// ctx.setFontSize(toPx(fontSize));
ctx.setFillStyle(color)
ctx.setTextBaseline(baseLine)
ctx.setTextAlign(textAlign)
let textWidth = toRpx(ctx.measureText(text).width)
const textArr = []
if (textWidth > width) {
// 文本宽度 大于 渲染宽度
let fillText = ''
let line = 1
for (let i = 0; i <= text.length - 1; i++) {
// 将文字转为数组,一行文字一个元素
fillText = fillText + text[i]
if (toRpx(ctx.measureText(fillText).width) >= width) {
if (line === lineNum) {
if (i !== text.length - 1) {
fillText = fillText.substring(0, fillText.length - 1) + '...'
}
}
if (line <= lineNum) {
textArr.push(fillText)
}
fillText = ''
line++
} else {
if (line <= lineNum) {
if (i === text.length - 1) {
textArr.push(fillText)
}
}
}
}
textWidth = width
} else {
textArr.push(text)
}
textArr.forEach((item, index) => {
ctx.fillText(item, toPx(x), toPx(y + (lineHeight || fontSize) * index))
})
ctx.restore()
// textDecoration
if (textDecoration !== 'none') {
let lineY = y
if (textDecoration === 'line-through') {
// 目前只支持贯穿线
lineY = y - fontSize * 0.4
}
ctx.save()
ctx.moveTo(toPx(x), toPx(lineY))
ctx.lineTo(toPx(x) + toPx(textWidth), toPx(lineY))
ctx.setStrokeStyle(color)
ctx.stroke()
ctx.restore()
}
return textWidth
}
/**
* 渲染文字
* @param { object } params - 绘制数据
* @param { number } params.x - x坐标 rpx
* @param { number } params.y - y坐标 rpx
* @param { number } params.fontSize - 文字大小 rpx
* @param { number } [params.color] - 颜色
* @param { string } [params.baseLine] - 基线对齐方式 top| middle|bottom
* @param { string } [params.textAlign='left'] - 对齐方式 left|center|right
* @param { string } params.text - 当Object类型时,参数为 text 字段的参数,marginLeft、marginRight这两个字段可用
* @param { number } [params.opacity=1] - 1为不透明,0为透明
* @param { string } [params.textDecoration='none']
* @param { number } [params.width] - 文字宽度 没有指定为画布宽度
* @param { number } [params.lineNum=1] - 根据宽度换行,最多的行数
* @param { number } [params.lineHeight=0] - 行高
* @param { string } [params.fontWeight='normal'] - 'bold' 加粗字体,目前小程序不支持 100 - 900 加粗
* @param { string } [params.fontStyle='normal'] - 'italic' 倾斜字体
* @param { string } [params.fontFamily="sans-serif"] - 小程序默认字体为 'sans-serif', 请输入小程序支持的字体
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function drawText(params, drawOptions) {
// const { ctx, toPx, toRpx } = drawOptions;
const {
x,
y,
text,
baseLine
// fontSize,
// color,
// textAlign,
// opacity = 1,
// width,
// lineNum,
// lineHeight
} = params
if (Object.prototype.toString.call(text) === '[object Array]') {
let preText = { x, y, baseLine }
text.forEach((item) => {
preText.x += item.marginLeft || 0
const textWidth = _drawSingleText(
Object.assign(item, {
...preText
}),
drawOptions
)
preText.x += textWidth + (item.marginRight || 0) // 下一段字的 x 轴为上一段字 x + 上一段字宽度
})
} else {
_drawSingleText(params, drawOptions)
}
}
/**
* @description 渲染图片
* @param { object } data
* @param { number } x - 图像的左上角在目标 canvas 上 x 轴的位置
* @param { number } y - 图像的左上角在目标 canvas 上 y 轴的位置
* @param { number } w - 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
* @param { number } h - 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
* @param { number } sx - 源图像的矩形选择框的左上角 x 坐标
* @param { number } sy - 源图像的矩形选择框的左上角 y 坐标
* @param { number } sw - 源图像的矩形选择框的宽度
* @param { number } sh - 源图像的矩形选择框的高度
* @param { number } [borderRadius=0] - 圆角
* @param { number } [borderWidth=0] - 边框
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function drawImage(data, drawOptions) {
const { ctx, toPx } = drawOptions
let {
imgPath,
x,
y,
w,
h,
sx,
sy,
sw,
sh,
borderRadius = 0,
borderWidth = 0,
borderColor,
} = data
ctx.save()
if (borderRadius > 0) {
let drawData = {
x,
y,
w,
h,
r: borderRadius
}
_drawRadiusRect(drawData, drawOptions)
ctx.strokeStyle = 'rgba(255,255,255,0)'
ctx.stroke()
ctx.clip()
ctx.drawImage(
imgPath,
toPx(sx),
toPx(sy),
toPx(sw),
toPx(sh),
toPx(x),
toPx(y),
toPx(w),
toPx(h)
)
if (borderWidth > 0) {
ctx.setStrokeStyle(borderColor)
ctx.setLineWidth(toPx(borderWidth))
ctx.stroke()
}
} else {
ctx.drawImage(
imgPath,
toPx(sx),
toPx(sy),
toPx(sw),
toPx(sh),
toPx(x),
toPx(y),
toPx(w),
toPx(h)
)
}
ctx.restore()
}
/**
* @description 渲染线
* @param { number } startX - 起始坐标
* @param { number } startY - 起始坐标
* @param { number } endX - 终结坐标
* @param { number } endY - 终结坐标
* @param { number } width - 线的宽度
* @param { string } [color] - 线的颜色
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function drawLine(drawData, drawOptions) {
const { startX, startY, endX, endY, color, width } = drawData
const { ctx, toPx } = drawOptions
ctx.save()
ctx.beginPath()
ctx.setStrokeStyle(color)
ctx.setLineWidth(toPx(width))
ctx.moveTo(toPx(startX), toPx(startY))
ctx.lineTo(toPx(endX), toPx(endY))
ctx.stroke()
ctx.closePath()
ctx.restore()
}
/**
* @description 渲染块
* @param { number } x - x坐标
* @param { number } y - y坐标
* @param { number } height -高
* @param { string|object } [text] - 块里面可以填充文字,参考texts字段
* @param { number } [width=0] - 宽 如果内部有文字,由文字宽度和内边距决定
* @param { number } [paddingLeft=0] - 内左边距
* @param { number } [paddingRight=0] - 内右边距
* @param { number } [borderWidth] - 边框宽度
* @param { string } [backgroundColor] - 背景颜色
* @param { string } [borderColor] - 边框颜色
* @param { number } [borderRadius=0] - 圆角
* @param { number } [opacity=1] - 透明度
*
* @param { object } drawOptions - 绘制对象
* @param { object } drawOptions.ctx - ctx对象
* @param { function } drawOptions.toPx - toPx方法
* @param { function } drawOptions.toRpx - toRpx方法
*/
export function drawBlock(
{
text,
width = 0,
height,
x,
y,
paddingLeft = 0,
paddingRight = 0,
borderWidth,
backgroundColor,
borderColor,
borderRadius = 0,
opacity = 1
},
drawOptions
) {
const { ctx, toPx } = drawOptions
// 判断是否块内有文字
let blockWidth = 0 // 块的宽度
let textX = 0
let textY = 0
if (typeof text !== 'undefined') {
// 如果有文字并且块的宽度小于文字宽度,块的宽度为 文字的宽度 + 内边距
const textWidth = _getTextWidth(typeof text.text === 'string' ? text : text.text, drawOptions)
blockWidth = textWidth > width ? textWidth : width
blockWidth += paddingLeft + paddingLeft
const {
textAlign = 'left'
// text: textCon,
} = text
textY = height / 2 + y // 文字的y轴坐标在块中线
if (textAlign === 'left') {
// 如果是右对齐,那x轴在块的最左边
textX = x + paddingLeft
} else if (textAlign === 'center') {
textX = blockWidth / 2 + x
} else {
textX = x + blockWidth - paddingRight
}
} else {
blockWidth = width
}
if (backgroundColor) {
// 画面
ctx.save()
ctx.setGlobalAlpha(opacity)
ctx.setFillStyle(backgroundColor)
if (borderRadius > 0) {
// 画圆角矩形
let drawData = {
x,
y,
w: blockWidth,
h: height,
r: borderRadius
}
_drawRadiusRect(drawData, drawOptions)
ctx.fill()
} else {
ctx.fillRect(toPx(x), toPx(y), toPx(blockWidth), toPx(height))
}
ctx.restore()
}
if (borderWidth) {
// 画线
ctx.save()
ctx.setGlobalAlpha(opacity)
ctx.setStrokeStyle(borderColor)
ctx.setLineWidth(toPx(borderWidth))
if (borderRadius > 0) {
// 画圆角矩形边框
let drawData = {
x,
y,
w: blockWidth,
h: height,
r: borderRadius
}
_drawRadiusRect(drawData, drawOptions)
ctx.stroke()
} else {
ctx.strokeRect(toPx(x), toPx(y), toPx(blockWidth), toPx(height))
}
ctx.restore()
}
if (text) {
drawText(Object.assign(text, { x: textX, y: textY }), drawOptions)
}
}