canvas自定义文本排列方法 + 自定义花字应用案例

一、canvas自定义文本排列方法

其中还添加了自定义文字描边、文字阴影、文字下划线等逻辑功能。

1、竖排

注意点:竖排里的英文和数字的摆放方式是倒立的,跟中文的正立摆放是有区别的。

1.1 自动换行

需要根据传入的文字区域的宽高自动识别文本的长度进行换行

_maxWidth = item.width * devicePixelRatio
_maxHeight = item.height * devicePixelRatio

let _w = 0
let _h = 0
let _p = ''

// 计算所有字的宽度和高度
for(let i = 0; i < item.content.length; i++) {

	// 换行符
	if(item.content[i] === '\n') {
		ARR.push([lineHeight || _w, _h])
		LINEARR.push(_p)

		_w = 0
		_h = 0

		_p = ''

		continue
	}

	let metrics = undefined
	let textWidth = 0
	let textHeight = 0
	if(WORDS[item.content[i]]) {
		metrics = WORDS[item.content[i]]
		textWidth = metrics.w
		
		textHeight = metrics.rH
	} else {

		metrics = measureText(context, item.content[i])

		textWidth = metrics.width
		
		textHeight = metrics.rHeight
		

		WORDS[item.content[i]] = {
			w:  metrics.width,
			h:  metrics.height,
			rW:  metrics.rWidth,
			rH:  metrics.rHeight
		}
	}

	// 英文&数字
	if(!isCstr(item.content[i])) {
		// 超过编辑框高度
		if(_h + textWidth > item.height * devicePixelRatio) {
			ARR.push([lineHeight || _w, _h])
			LINEARR.push(_p)

			_w = textHeight
			_h = textWidth

			_p = item.content[i]


		} else {

			_w = Math.max(textHeight, _w)
			_h += textWidth

			_p += item.content[i]

			// 最后一个字
			if(i === item.content.length -1) {
				ARR.push([_w, lineHeight || _h])
				LINEARR.push(_p)

				_w = 0
				_h = 0

				_p = ''

				continue
			}
		}
	} else {
		// 超过编辑框高度
		if(_h + textHeight > item.height * devicePixelRatio) {
			ARR.push([lineHeight || _w, _h])
			LINEARR.push(_p)

			_w = textWidth
			_h = textHeight

			_p = item.content[i]

		} else {

			_w = Math.max(textWidth, _w)
			_h += textHeight

			_p += item.content[i]

			// 最后一个字
			if(i === item.content.length -1) {
				ARR.push([_w, lineHeight || _h])
				LINEARR.push(_p)

				_w = 0
				_h = 0

				_p = ''
				continue
			}
		}
	}
	
}

1.2 换行符换行

// 根据换行符分段
LINEARR = item.content.split('\n');

// 计算所有段落的宽度和高度
for(let i = 0; i < LINEARR.length; i++) {
	let _w = 0
	let _h = 0

	for(let j = 0; j < LINEARR[i].length; j++) {
		let metrics = undefined
		let textWidth = 0
		let textHeight = 0
		if(WORDS[LINEARR[i][j]]) {
			metrics = WORDS[LINEARR[i][j]]
			textWidth = metrics.w
		
			textHeight = metrics.rH
		} else {
			metrics = measureText(context, LINEARR[i][j])

			textWidth = metrics.width
			
			textHeight = metrics.rHeight

			WORDS[LINEARR[i][j]] = {
				w: metrics.width, 
				h: metrics.height,
				rW: metrics.rWidth,
				rH: metrics.rHeight
			}
		}

		// 英文&数字
		if(!isCstr(LINEARR[i][j])) {
			_w = Math.max(textHeight, _w)
			_h += textWidth
		} else {
			_w = Math.max(textWidth, _w)
			_h += textHeight
		}

	}

	_maxWidth += (lineHeight || _w)
	_maxHeight = Math.max(_maxHeight, _h)

	ARR.push([lineHeight || _w, _h])

}

1.3 绘制逻辑

// 计算坐标并绘制文字
let x = (item.left + item.width) * devicePixelRatio 
let y = item.top * devicePixelRatio
for(let i = 0; i < LINEARR.length; i++) {
	x -= ARR[i][0]
	y = item.top * devicePixelRatio

	// 判断对齐方式
	switch(item.align) {
		case 'top':
			break
		case 'middle':
			y += ((item.height * devicePixelRatio - ARR[i][1])/2)
			break
		case 'bottom':
			y += (item.height * devicePixelRatio - ARR[i][1])
			break
	}

	
	for(let j = 0; j < LINEARR[i].length; j++) {	

		// 英文&数字
		if(!isCstr(LINEARR[i][j])) {

			if(j > 0) {
				// 英文&数字
				if(!isCstr(LINEARR[i][j-1])){
					y += WORDS[LINEARR[i][j-1]].w
				} else {
					y += WORDS[LINEARR[i][j-1]].rH
				}
			}

			const _X = x+ARR[i][0]/2-WORDS[LINEARR[i][j]].w/2
			const _Y = y+WORDS[LINEARR[i][j]].w/2-WORDS[LINEARR[i][j]].h/2
			const _D = (WORDS[LINEARR[i][j]].h - lineHeight)/2

			context.save()

			context.translate(x+ARR[i][0]/2, y+WORDS[LINEARR[i][j]].w/2)
				
			context.rotate(Math.PI/2)

			context.translate(-(x+ARR[i][0]/2), -(y+WORDS[LINEARR[i][j]].w/2))

			// 自定义阴影
			if(item?.shadow) {
				context.save()
				context.filter = `blur(${item.shadow.blur})px`
				context.fillStyle = item.shadow.color
				context.strokeStyle = item.shadow.color
				const s = (item.fontSize/120)* devicePixelRatio
				item.stroke && item.stroke.width > 0 && context.strokeText(
					LINEARR[i][j], 
					_X+s*item.shadow.x, 
					_Y+s*item.shadow.y + _D
				);
				context.fillText(
					LINEARR[i][j], 
					_X+s*item.shadow.x,
					_Y+s*item.shadow.y + _D
				);
				context.restore()
			}

			// 主体文字
			if(item.stroke && item.stroke.width > 0) {
				context.save()
				// 渐变
				if(Object.prototype.toString.call(item?.stroke?.color) === '[object Array]') {
					if(WORDS[LINEARR[i][j]]) {

						let gradient = workWithGradient(
							item.stroke?.colorGradientType, 
							context, 
							[...item.stroke.color], 
							[_X, _Y + _D], 
							WORDS[LINEARR[i][j]],
							lineHeight
						)

						context.strokeStyle = gradient // 文字颜色
					}
				} else {
					context.strokeStyle = item?.stroke?.color // 描边颜色
				}
				
				context.strokeText(
					LINEARR[i][j], 
					_X, 
					_Y + _D
				);
				context.restore()
			}
			

			// 绘制文字颜色
			context.save()
			// 渐变
			if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
			
				let gradient = workWithGradient(
					item?.colorGradientType, 
					context, 
					[...item.fontColor], 
					[
						_X, 
						_Y + _D
					], 
					WORDS[LINEARR[i][j]],
					lineHeight
				)

				context.fillStyle = gradient // 文字颜色
				
			} else {
				context.fillStyle = item.fontColor // 文字颜色
			}

			context.fillText(
				LINEARR[i][j], 
				_X, 
				_Y + _D
			);
			context.restore()


			// 下划线
			if(item?.underline) {
				let strokeStyle = item.fontColor
				if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
					strokeStyle = item.fontColor[0]
				}
				context.save()
				context.strokeStyle = strokeStyle; // 描边颜色
				context.lineWidth = Math.max(item.fontSize * devicePixelRatio/12, 1)
				context.beginPath()
				context.moveTo(_X, _Y+_D+lineHeight);
				context.lineTo(_X+WORDS[LINEARR[i][j]].w, _Y+_D+lineHeight);
	
				context.closePath()
				context.stroke();
				context.restore();
			}

			// 绘制文字边框区域
			// context.save()
			// context.strokeStyle = 'red'
			// context.beginPath()
			// context.strokeRect(_X, _Y, WORDS[LINEARR[i][j]].w, WORDS[LINEARR[i][j]].h)
			// context.closePath()
			// context.restore()

			context.restore()

			
		} else {

			if(j > 0) {
				// 英文&数字
				if(!isCstr(LINEARR[i][j-1])){
					y += WORDS[LINEARR[i][j-1]].w
				} else {
					y += WORDS[LINEARR[i][j-1]].rH
				}
			}

			const _X = x+(ARR[i][0]-WORDS[LINEARR[i][j]].w)/2
			
			// 自定义阴影
			if(item?.shadow) {
				context.save()
				context.filter = `blur(${item.shadow.blur})px`
				context.fillStyle = item.shadow.color
				context.strokeStyle = item.shadow.color
				const s = (item.fontSize/120)* devicePixelRatio
				item.stroke && item.stroke.width > 0 && context.strokeText(LINEARR[i][j], _X+s*item.shadow.x, y+s*item.shadow.y);
				context.fillText(LINEARR[i][j], _X+s*item.shadow.x, y+s*item.shadow.y);
				context.restore()
			}

			// 主体文字
			if(item.stroke && item.stroke.width > 0) {
				context.save()
				// 渐变
				if(Object.prototype.toString.call(item?.stroke?.color) === '[object Array]') {
					if(WORDS[LINEARR[i][j]]) {

						let gradient = workWithGradient(
							item.stroke?.colorGradientType, 
							context, 
							[...item.stroke.color], 
							[_X, y], 
							WORDS[LINEARR[i][j]],
							lineHeight
						)

						context.strokeStyle = gradient // 文字颜色
					}
				} else {
					context.strokeStyle = item?.stroke?.color // 描边颜色
				}
				
				context.strokeText(LINEARR[i][j], _X, y);
				context.restore()
			}

			// 绘制文字颜色
			context.save()
			// 渐变
			if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
					
				let gradient = workWithGradient(
					item?.colorGradientType, 
					context, 
					[...item.fontColor], 
					[_X, y], 
					WORDS[LINEARR[i][j]],
					lineHeight
				)

				context.fillStyle = gradient // 文字颜色
				
			} else {
				context.fillStyle = item.fontColor // 文字颜色
			}
			context.fillText(LINEARR[i][j], _X, y);
			context.restore()

			

			// 下划线
			if(item?.underline) {
				let strokeStyle = item.fontColor
				if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
					strokeStyle = item.fontColor[0]
				}
				context.save()
				context.strokeStyle = strokeStyle; // 描边颜色
				context.lineWidth = Math.max(item.fontSize * devicePixelRatio/12, 1)
				context.beginPath()
				context.moveTo(x, y);
				context.lineTo(x, y+WORDS[LINEARR[i][j]].rH);
				context.closePath()
				context.stroke();
				context.restore();
			}

			// 绘制文字边框区域
			// context.save()
			// context.strokeStyle = 'red'
			// context.beginPath()
			// context.strokeRect(_X, y, WORDS[LINEARR[i][j]].w, WORDS[LINEARR[i][j]].h)
			// context.closePath()
			// context.restore()
		}

	}

	
}

2、横排

2.1 自动换行

_maxWidth = item.width * devicePixelRatio
_maxHeight = item.height * devicePixelRatio


let _w = 0
let _h = 0
let _p = ''

// 计算所有字的宽度和高度
for(let i = 0; i < item.content.length; i++) {

	// 换行符
	if(item.content[i] === '\n') {

		ARR.push([_w, lineHeight || _h])
		LINEARR.push(_p)

		_w = 0
		_h = 0

		_p = ''

		continue
	}

	let metrics = undefined
	let textWidth = 0
	let textHeight = 0
	if(WORDS[item.content[i]]) {
		metrics = WORDS[item.content[i]]

		textWidth = metrics.w
		
		textHeight = metrics.h
	} else {

		metrics = measureText(context, item.content[i])

		textWidth = metrics.width
		
		textHeight = metrics.height
		

		WORDS[item.content[i]] = {
			w:  metrics.width,
			h:  metrics.height,
			rW:  metrics.rWidth,
			rH:  metrics.rHeight
		}
	}

	
	
	// 超过编辑框宽度
	if(_w + textWidth > item.width * devicePixelRatio) {
		ARR.push([_w, lineHeight || _h])
		LINEARR.push(_p)


		_w = textWidth
		_h = textHeight

		_p = item.content[i]

	} else {


		_w += textWidth
		_h = Math.max(textHeight, _h)

		_p += item.content[i]
		
		// 最后一个字
		if(i === item.content.length -1) {
			ARR.push([_w, lineHeight || _h])
			LINEARR.push(_p)

			_w = 0
			_h = 0

			_p = ''
		}
	}		

}

2.2 换行符换行

// 根据换行符分段
LINEARR = item.content.split('\n');


// 计算所有段落的宽度和高度
for(let i = 0; i < LINEARR.length; i++) {
	let _w = 0
	let _h = 0

	
	for(let j = 0; j < LINEARR[i].length; j++) {

		/*
		let metrics = null
		let textWidth = 0
		let textHeight = 0
		const key = LINEARR[i] + item.italic + item.weight + item.fontSize + devicePixelRatio + (item?.webFontFamily || item?.fontFamily)

		if(useCommon().words[key]) {
			metrics = useCommon().words[key]
			textWidth = metrics.width
			textHeight = metrics.height
		} else {
			// 计算文字宽高
			metrics = context.measureText(LINEARR[i][j]);

			// 计算文本宽度
			// const textWidth = metrics.actualBoundingBoxRight + metrics.actualBoundingBoxLeft;
			textWidth = metrics.width

			// 计算文本高度
			// 所有字在这个字体下的高度
			textHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent
			// 当前文本字符串在这个字体下用的实际高度
			// const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; 

			useCommon().$patch((state) => {
				state.words[key] = {
					width: textWidth,
					height: textHeight
				}
			})
		}	*/

	
		let metrics = undefined
		let textWidth = 0
		let textHeight = 0
		if(WORDS[LINEARR[i][j]]) {
			metrics = WORDS[LINEARR[i][j]]

			textWidth = metrics.w
		
			textHeight = metrics.h
		} else {
			metrics = measureText(context, LINEARR[i][j])

			textWidth = metrics.width
			
			textHeight = metrics.height

			WORDS[LINEARR[i][j]] = {
				w: metrics.width, 
				h: metrics.height,
				rW: metrics.rWidth,
				rH: metrics.rHeight
			}
		}


		_w += textWidth
		_h = Math.max(textHeight, _h)


	}

	_maxWidth = Math.max(_maxWidth, _w)
	_maxHeight += (lineHeight || _h)

	ARR.push([_w, lineHeight || _h])

}

2.3 绘制逻辑

// 计算坐标并绘制文字
let x = item.left * devicePixelRatio
let y = item.top * devicePixelRatio

for(let i = 0; i < LINEARR.length; i++) {
	
	x = item.left * devicePixelRatio

	// 判断对齐方式
	switch(item.align) {
		case 'left':
			break
		case 'center':
			x += ((item.width * devicePixelRatio - ARR[i][0])/2)
			break
		case 'right':
			x += (item.width * devicePixelRatio - ARR[i][0])
			break
	}

	if(i > 0) {
		y += ARR[i-1][1]
	}

	for(let j = 0; j < LINEARR[i].length; j++) {	

		if(j > 0) {
			
			x += WORDS[LINEARR[i][j-1]].w
		}

		
		// 自定义阴影
		if(item?.shadow) {
			context.save()
			context.filter = `blur(${item.shadow.blur}px)`
			context.fillStyle = item.shadow.color
			context.strokeStyle = item.shadow.color
			const s = (item.fontSize/120) * devicePixelRatio
			item.stroke && item.stroke.width > 0 && context.strokeText(LINEARR[i][j], x+s*item.shadow.x, y+s*item.shadow.y);
			context.fillText(LINEARR[i][j], x+s*item.shadow.x, y+s*item.shadow.y);
			context.restore()
		}


		// 主体文字
		if(item.stroke && item.stroke.width > 0) {
			context.save()
			// 渐变
			if(Object.prototype.toString.call(item?.stroke?.color) === '[object Array]') {
				if(WORDS[LINEARR[i][j]]) {

					let gradient = workWithGradient(
						item.stroke?.colorGradientType, 
						context, 
						[...item.stroke.color], 
						[x, y], 
						WORDS[LINEARR[i][j]],
						lineHeight
					)

					context.strokeStyle = gradient // 文字颜色
				}
			} else {
				context.strokeStyle = item?.stroke?.color // 描边颜色
			}
			
			context.strokeText(LINEARR[i][j], x, y);

			context.restore()
		}

		// 绘制文字颜色
		context.save()
		// 渐变
		if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
				
			if(WORDS[LINEARR[i][j]]) {

				let gradient = workWithGradient(
					item?.colorGradientType, 
					context, 
					[...item.fontColor], 
					[x, y],
					WORDS[LINEARR[i][j]],
					lineHeight
				)

				context.fillStyle = gradient // 文字颜色

			}
			
		} else {
			context.fillStyle = item.fontColor // 文字颜色
		}
		context.fillText(LINEARR[i][j], x, y);
		context.restore()
		

		// 下划线
		if(item?.underline) {
			let strokeStyle = item.fontColor
			if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
				strokeStyle = item.fontColor[0]
			}
			context.save()
			context.strokeStyle = strokeStyle; // 描边颜色
			context.lineWidth = Math.max(item.fontSize * devicePixelRatio/12, 1)
			context.beginPath()
			context.moveTo(x, y + ARR[i][1]);
			context.lineTo(x + WORDS[LINEARR[i][j]].w, y+ ARR[i][1]);
			context.closePath()
			context.stroke();
			context.restore();
		}

		// 绘制文字边框区域
		// context.save()
		// context.lineWidth = 2
		// context.strokeStyle = 'red'
		// context.beginPath()
		// context.strokeRect(x, y, WORDS[LINEARR[i][j]].rW, WORDS[LINEARR[i][j]].rH)
		// context.closePath()
		// context.strokeStyle = 'black'
		// context.beginPath()
		// context.strokeRect(x, y, WORDS[LINEARR[i][j]].w, WORDS[LINEARR[i][j]].h)
		// context.closePath()
		// context.restore()

	}

	
}

3、整体基本逻辑代码

;(function(global) {

	global.OffscreenCanvasRenderingContext2D.prototype.wrapText = global.CanvasRenderingContext2D.prototype.wrapText = function(item, e) {

		// console.error(item, e)

		// 默认参数
		const context = this;
    // const canvas = context.canvas;
		// 文本对齐方式
		context.textAlign = 'left'
		// 文本基线
		context.textBaseline = 'top'


		// 可配置参数
		let noDraw = e?.noDraw || '0' // 不绘制,主要用于获取实际宽高
		const devicePixelRatio = noDraw === '1' ? 1 : (e?.devicePixelRatio || 1) // 控制缩放倍数
		let wrapLine = e?.wrapLine || 0 // 自动换行
		let lineHeightType = e?.lineHeightType || 1 // 行高类型
		let lineHeight = 0 // 使用行高参数
		if(typeof lineHeightType === 'number') {
			
			lineHeight = item.fontSize * devicePixelRatio * lineHeightType
				
		} else if(lineHeightType === 'normal') {

			lineHeight = measureText(context, '字').height
			
		}


		// 返回的参数
		let _maxWidth = 0
		let _maxHeight = 0

		
		
		// 计算参数	
		const ARR = [] // 存储每行的宽高
		const WORDS = {} // 存储每个字的宽高
		let LINEARR = [] // 片段数组

		// function wrapLineCallback() {

		// }

		// 竖排
		if(item.direction === "vertical") {
			// console.error('竖排')

			// 自动换行
			if(wrapLine) {
				// console.error('自动换行')

				_maxWidth = item.width * devicePixelRatio
				_maxHeight = item.height * devicePixelRatio

				let _w = 0
				let _h = 0
				let _p = ''

				// 计算所有字的宽度和高度
				for(let i = 0; i < item.content.length; i++) {

					// 换行符
					if(item.content[i] === '\n') {
						ARR.push([lineHeight || _w, _h])
						LINEARR.push(_p)

						_w = 0
						_h = 0

						_p = ''

						continue
					}

					let metrics = undefined
					let textWidth = 0
					let textHeight = 0
					if(WORDS[item.content[i]]) {
						metrics = WORDS[item.content[i]]
						textWidth = metrics.w
						
						textHeight = metrics.rH
					} else {

						metrics = measureText(context, item.content[i])

						textWidth = metrics.width
						
						textHeight = metrics.rHeight
						

						WORDS[item.content[i]] = {
							w:  metrics.width,
							h:  metrics.height,
							rW:  metrics.rWidth,
							rH:  metrics.rHeight
						}
					}

					// 英文&数字
          if(!isCstr(item.content[i])) {
						// 超过编辑框高度
						if(_h + textWidth > item.height * devicePixelRatio) {
							ARR.push([lineHeight || _w, _h])
							LINEARR.push(_p)

							_w = textHeight
							_h = textWidth

							_p = item.content[i]


						} else {

							_w = Math.max(textHeight, _w)
							_h += textWidth

							_p += item.content[i]

							// 最后一个字
							if(i === item.content.length -1) {
								ARR.push([_w, lineHeight || _h])
								LINEARR.push(_p)

								_w = 0
								_h = 0

								_p = ''

								continue
							}
						}
					} else {
						// 超过编辑框高度
						if(_h + textHeight > item.height * devicePixelRatio) {
							ARR.push([lineHeight || _w, _h])
							LINEARR.push(_p)

							_w = textWidth
							_h = textHeight

							_p = item.content[i]

						} else {

							_w = Math.max(textWidth, _w)
							_h += textHeight

							_p += item.content[i]

							// 最后一个字
							if(i === item.content.length -1) {
								ARR.push([_w, lineHeight || _h])
								LINEARR.push(_p)

								_w = 0
								_h = 0

								_p = ''
								continue
							}
						}
					}
					
				}
			} else {
				// 根据换行符分段
				LINEARR = item.content.split('\n');

				// 计算所有段落的宽度和高度
				for(let i = 0; i < LINEARR.length; i++) {
					let _w = 0
					let _h = 0

					for(let j = 0; j < LINEARR[i].length; j++) {
						let metrics = undefined
						let textWidth = 0
						let textHeight = 0
						if(WORDS[LINEARR[i][j]]) {
							metrics = WORDS[LINEARR[i][j]]
							textWidth = metrics.w
						
							textHeight = metrics.rH
						} else {
							metrics = measureText(context, LINEARR[i][j])

							textWidth = metrics.width
							
							textHeight = metrics.rHeight

							WORDS[LINEARR[i][j]] = {
								w: metrics.width, 
								h: metrics.height,
								rW: metrics.rWidth,
								rH: metrics.rHeight
							}
						}

						// 英文&数字
						if(!isCstr(LINEARR[i][j])) {
							_w = Math.max(textHeight, _w)
							_h += textWidth
						} else {
							_w = Math.max(textWidth, _w)
							_h += textHeight
						}

					}

					_maxWidth += (lineHeight || _w)
					_maxHeight = Math.max(_maxHeight, _h)

					ARR.push([lineHeight || _w, _h])

				}
			}

			if(noDraw === '0') {

				// 计算坐标并绘制文字
				let x = (item.left + item.width) * devicePixelRatio 
				let y = item.top * devicePixelRatio
				for(let i = 0; i < LINEARR.length; i++) {
					x -= ARR[i][0]
					y = item.top * devicePixelRatio
	
					// 判断对齐方式
					switch(item.align) {
						case 'top':
							break
						case 'middle':
							y += ((item.height * devicePixelRatio - ARR[i][1])/2)
							break
						case 'bottom':
							y += (item.height * devicePixelRatio - ARR[i][1])
							break
					}
	
					
					for(let j = 0; j < LINEARR[i].length; j++) {	

						// 英文&数字
						if(!isCstr(LINEARR[i][j])) {

							if(j > 0) {
								// 英文&数字
								if(!isCstr(LINEARR[i][j-1])){
									y += WORDS[LINEARR[i][j-1]].w
								} else {
									y += WORDS[LINEARR[i][j-1]].rH
								}
							}

							const _X = x+ARR[i][0]/2-WORDS[LINEARR[i][j]].w/2
							const _Y = y+WORDS[LINEARR[i][j]].w/2-WORDS[LINEARR[i][j]].h/2
							const _D = (WORDS[LINEARR[i][j]].h - lineHeight)/2

							context.save()
				
							context.translate(x+ARR[i][0]/2, y+WORDS[LINEARR[i][j]].w/2)
								
							context.rotate(Math.PI/2)
	
							context.translate(-(x+ARR[i][0]/2), -(y+WORDS[LINEARR[i][j]].w/2))

							// 自定义阴影
							if(item?.shadow) {
								context.save()
								context.filter = `blur(${item.shadow.blur})px`
								context.fillStyle = item.shadow.color
								context.strokeStyle = item.shadow.color
								const s = (item.fontSize/120)* devicePixelRatio
								item.stroke && item.stroke.width > 0 && context.strokeText(
									LINEARR[i][j], 
									_X+s*item.shadow.x, 
									_Y+s*item.shadow.y + _D
								);
								context.fillText(
									LINEARR[i][j], 
									_X+s*item.shadow.x,
									_Y+s*item.shadow.y + _D
								);
								context.restore()
							}
	
							// 主体文字
							if(item.stroke && item.stroke.width > 0) {
								context.save()
								// 渐变
								if(Object.prototype.toString.call(item?.stroke?.color) === '[object Array]') {
									if(WORDS[LINEARR[i][j]]) {
		
										let gradient = workWithGradient(
											item.stroke?.colorGradientType, 
											context, 
											[...item.stroke.color], 
											[_X, _Y + _D], 
											WORDS[LINEARR[i][j]],
											lineHeight
										)
			
										context.strokeStyle = gradient // 文字颜色
									}
								} else {
									context.strokeStyle = item?.stroke?.color // 描边颜色
								}
								
								context.strokeText(
									LINEARR[i][j], 
									_X, 
									_Y + _D
								);
								context.restore()
							}
							
	
							// 绘制文字颜色
							context.save()
							// 渐变
							if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
							
								let gradient = workWithGradient(
									item?.colorGradientType, 
									context, 
									[...item.fontColor], 
									[
										_X, 
										_Y + _D
									], 
									WORDS[LINEARR[i][j]],
									lineHeight
								)
	
								context.fillStyle = gradient // 文字颜色
								
							} else {
								context.fillStyle = item.fontColor // 文字颜色
							}

							context.fillText(
								LINEARR[i][j], 
								_X, 
								_Y + _D
							);
							context.restore()


							// 下划线
							if(item?.underline) {
								let strokeStyle = item.fontColor
								if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
									strokeStyle = item.fontColor[0]
								}
								context.save()
								context.strokeStyle = strokeStyle; // 描边颜色
								context.lineWidth = Math.max(item.fontSize * devicePixelRatio/12, 1)
								context.beginPath()
								context.moveTo(_X, _Y+_D+lineHeight);
								context.lineTo(_X+WORDS[LINEARR[i][j]].w, _Y+_D+lineHeight);
					
								context.closePath()
								context.stroke();
								context.restore();
							}

							// 绘制文字边框区域
							// context.save()
							// context.strokeStyle = 'red'
							// context.beginPath()
							// context.strokeRect(_X, _Y, WORDS[LINEARR[i][j]].w, WORDS[LINEARR[i][j]].h)
							// context.closePath()
							// context.restore()

							context.restore()

							
						} else {
	
							if(j > 0) {
								// 英文&数字
								if(!isCstr(LINEARR[i][j-1])){
									y += WORDS[LINEARR[i][j-1]].w
								} else {
									y += WORDS[LINEARR[i][j-1]].rH
								}
							}

							const _X = x+(ARR[i][0]-WORDS[LINEARR[i][j]].w)/2
							
							// 自定义阴影
							if(item?.shadow) {
								context.save()
								context.filter = `blur(${item.shadow.blur})px`
								context.fillStyle = item.shadow.color
								context.strokeStyle = item.shadow.color
								const s = (item.fontSize/120)* devicePixelRatio
								item.stroke && item.stroke.width > 0 && context.strokeText(LINEARR[i][j], _X+s*item.shadow.x, y+s*item.shadow.y);
								context.fillText(LINEARR[i][j], _X+s*item.shadow.x, y+s*item.shadow.y);
								context.restore()
							}
	
							// 主体文字
							if(item.stroke && item.stroke.width > 0) {
								context.save()
								// 渐变
								if(Object.prototype.toString.call(item?.stroke?.color) === '[object Array]') {
									if(WORDS[LINEARR[i][j]]) {
		
										let gradient = workWithGradient(
											item.stroke?.colorGradientType, 
											context, 
											[...item.stroke.color], 
											[_X, y], 
											WORDS[LINEARR[i][j]],
											lineHeight
										)
			
										context.strokeStyle = gradient // 文字颜色
									}
								} else {
									context.strokeStyle = item?.stroke?.color // 描边颜色
								}
								
								context.strokeText(LINEARR[i][j], _X, y);
								context.restore()
							}

							// 绘制文字颜色
							context.save()
							// 渐变
							if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
									
								let gradient = workWithGradient(
									item?.colorGradientType, 
									context, 
									[...item.fontColor], 
									[_X, y], 
									WORDS[LINEARR[i][j]],
									lineHeight
								)
	
								context.fillStyle = gradient // 文字颜色
								
							} else {
								context.fillStyle = item.fontColor // 文字颜色
							}
							context.fillText(LINEARR[i][j], _X, y);
							context.restore()

							

							// 下划线
							if(item?.underline) {
								let strokeStyle = item.fontColor
								if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
									strokeStyle = item.fontColor[0]
								}
								context.save()
								context.strokeStyle = strokeStyle; // 描边颜色
								context.lineWidth = Math.max(item.fontSize * devicePixelRatio/12, 1)
								context.beginPath()
								context.moveTo(x, y);
								context.lineTo(x, y+WORDS[LINEARR[i][j]].rH);
								context.closePath()
								context.stroke();
								context.restore();
							}

							// 绘制文字边框区域
							// context.save()
							// context.strokeStyle = 'red'
							// context.beginPath()
							// context.strokeRect(_X, y, WORDS[LINEARR[i][j]].w, WORDS[LINEARR[i][j]].h)
							// context.closePath()
							// context.restore()
						}
	
					}

					
				}
			} 

		} 
		// 横排
		else {
			// console.error('横排')

			// 自动换行
			if(wrapLine) {
				// console.error('自动换行')

				_maxWidth = item.width * devicePixelRatio
				_maxHeight = item.height * devicePixelRatio
	

				let _w = 0
				let _h = 0
				let _p = ''

				// 计算所有字的宽度和高度
				for(let i = 0; i < item.content.length; i++) {

					// 换行符
					if(item.content[i] === '\n') {

						ARR.push([_w, lineHeight || _h])
						LINEARR.push(_p)

						_w = 0
						_h = 0

						_p = ''

						continue
					}

					let metrics = undefined
					let textWidth = 0
					let textHeight = 0
					if(WORDS[item.content[i]]) {
						metrics = WORDS[item.content[i]]

						textWidth = metrics.w
						
						textHeight = metrics.h
					} else {

						metrics = measureText(context, item.content[i])

						textWidth = metrics.width
						
						textHeight = metrics.height
						

						WORDS[item.content[i]] = {
							w:  metrics.width,
							h:  metrics.height,
							rW:  metrics.rWidth,
							rH:  metrics.rHeight
						}
					}

					
					
					// 超过编辑框宽度
					if(_w + textWidth > item.width * devicePixelRatio) {
						ARR.push([_w, lineHeight || _h])
						LINEARR.push(_p)


						_w = textWidth
						_h = textHeight

						_p = item.content[i]

					} else {


						_w += textWidth
						_h = Math.max(textHeight, _h)

						_p += item.content[i]
						
						// 最后一个字
						if(i === item.content.length -1) {
							ARR.push([_w, lineHeight || _h])
							LINEARR.push(_p)

							_w = 0
							_h = 0

							_p = ''
						}
					}		

				}

			} else {

				// 根据换行符分段
				LINEARR = item.content.split('\n');


				// 计算所有段落的宽度和高度
				for(let i = 0; i < LINEARR.length; i++) {
					let _w = 0
					let _h = 0

					
					for(let j = 0; j < LINEARR[i].length; j++) {

						/*
						let metrics = null
						let textWidth = 0
						let textHeight = 0
						const key = LINEARR[i] + item.italic + item.weight + item.fontSize + devicePixelRatio + (item?.webFontFamily || item?.fontFamily)

						if(useCommon().words[key]) {
							metrics = useCommon().words[key]
							textWidth = metrics.width
							textHeight = metrics.height
						} else {
							// 计算文字宽高
							metrics = context.measureText(LINEARR[i][j]);

							// 计算文本宽度
							// const textWidth = metrics.actualBoundingBoxRight + metrics.actualBoundingBoxLeft;
							textWidth = metrics.width
			
							// 计算文本高度
							// 所有字在这个字体下的高度
							textHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent
							// 当前文本字符串在这个字体下用的实际高度
							// const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; 

							useCommon().$patch((state) => {
								state.words[key] = {
									width: textWidth,
									height: textHeight
								}
							})
						}	*/

					
						let metrics = undefined
						let textWidth = 0
						let textHeight = 0
						if(WORDS[LINEARR[i][j]]) {
							metrics = WORDS[LINEARR[i][j]]

							textWidth = metrics.w
						
							textHeight = metrics.h
						} else {
							metrics = measureText(context, LINEARR[i][j])

							textWidth = metrics.width
							
							textHeight = metrics.height

							WORDS[LINEARR[i][j]] = {
								w: metrics.width, 
								h: metrics.height,
								rW: metrics.rWidth,
								rH: metrics.rHeight
							}
						}


						_w += textWidth
						_h = Math.max(textHeight, _h)


					}

					_maxWidth = Math.max(_maxWidth, _w)
					_maxHeight += (lineHeight || _h)

					ARR.push([_w, lineHeight || _h])

				}

			}

			if(noDraw === '0') {

				// 计算坐标并绘制文字
				let x = item.left * devicePixelRatio
				let y = item.top * devicePixelRatio

				for(let i = 0; i < LINEARR.length; i++) {
					
					x = item.left * devicePixelRatio

					// 判断对齐方式
					switch(item.align) {
						case 'left':
							break
						case 'center':
							x += ((item.width * devicePixelRatio - ARR[i][0])/2)
							break
						case 'right':
							x += (item.width * devicePixelRatio - ARR[i][0])
							break
					}

					if(i > 0) {
						y += ARR[i-1][1]
					}

					for(let j = 0; j < LINEARR[i].length; j++) {	

						if(j > 0) {
							
							x += WORDS[LINEARR[i][j-1]].w
						}

						
						// 自定义阴影
						if(item?.shadow) {
							context.save()
							context.filter = `blur(${item.shadow.blur}px)`
							context.fillStyle = item.shadow.color
							context.strokeStyle = item.shadow.color
							const s = (item.fontSize/120) * devicePixelRatio
							item.stroke && item.stroke.width > 0 && context.strokeText(LINEARR[i][j], x+s*item.shadow.x, y+s*item.shadow.y);
							context.fillText(LINEARR[i][j], x+s*item.shadow.x, y+s*item.shadow.y);
							context.restore()
						}


						// 主体文字
						if(item.stroke && item.stroke.width > 0) {
							context.save()
							// 渐变
							if(Object.prototype.toString.call(item?.stroke?.color) === '[object Array]') {
								if(WORDS[LINEARR[i][j]]) {
	
									let gradient = workWithGradient(
										item.stroke?.colorGradientType, 
										context, 
										[...item.stroke.color], 
										[x, y], 
										WORDS[LINEARR[i][j]],
										lineHeight
									)
		
									context.strokeStyle = gradient // 文字颜色
								}
							} else {
								context.strokeStyle = item?.stroke?.color // 描边颜色
							}
							
							context.strokeText(LINEARR[i][j], x, y);

							context.restore()
						}

						// 绘制文字颜色
						context.save()
						// 渐变
						if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
								
							if(WORDS[LINEARR[i][j]]) {

								let gradient = workWithGradient(
									item?.colorGradientType, 
									context, 
									[...item.fontColor], 
									[x, y],
									WORDS[LINEARR[i][j]],
									lineHeight
								)
	
								context.fillStyle = gradient // 文字颜色

							}
							
						} else {
							context.fillStyle = item.fontColor // 文字颜色
						}
						context.fillText(LINEARR[i][j], x, y);
						context.restore()
						

						// 下划线
						if(item?.underline) {
							let strokeStyle = item.fontColor
							if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
								strokeStyle = item.fontColor[0]
							}
							context.save()
							context.strokeStyle = strokeStyle; // 描边颜色
							context.lineWidth = Math.max(item.fontSize * devicePixelRatio/12, 1)
							context.beginPath()
							context.moveTo(x, y + ARR[i][1]);
							context.lineTo(x + WORDS[LINEARR[i][j]].w, y+ ARR[i][1]);
							context.closePath()
							context.stroke();
							context.restore();
						}

						// 绘制文字边框区域
						// context.save()
						// context.lineWidth = 2
						// context.strokeStyle = 'red'
						// context.beginPath()
						// context.strokeRect(x, y, WORDS[LINEARR[i][j]].rW, WORDS[LINEARR[i][j]].rH)
						// context.closePath()
						// context.strokeStyle = 'black'
						// context.beginPath()
						// context.strokeRect(x, y, WORDS[LINEARR[i][j]].w, WORDS[LINEARR[i][j]].h)
						// context.closePath()
						// context.restore()

					}

					
				}
			}
		}


		return {
			line: ARR.length,
			maxWidth: _maxWidth,
			maxHeight: _maxHeight
		}
	}

})(window);

function measureText(context, txt) {
	// 计算文字宽高
	const metrics = context.measureText(txt);
	
	// 计算文本宽度
	const textWidth = metrics.width
	// 当前文本字符串在这个字体下用的实际宽度
	const rTextWidth = metrics.actualBoundingBoxRight + metrics.actualBoundingBoxLeft;

	// 计算文本高度
	// 所有字在这个字体下的高度
	const textHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent
	// 当前文本字符串在这个字体下用的实际高度
	const rTextHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; 

	return {
		width: textWidth,
		height: textHeight,
		rWidth: rTextWidth,
		rHeight: rTextHeight
	}
}
// 判断中英文字符
export function isCstr(val) {
  const _Cstr = "【〗〈){」】《〉〔}『〖》(〕「』‹»||›…¦—‥、«–﹒。′~ˉ•ˋ‵ˆ―ˇ´_, L'【"

  // 英文
  if(escape(val).indexOf("%u")<0) {
    return false
  } else {
    if(_Cstr.indexOf(val) != -1) {
      return false
    }
    return true
  }
}
export function workWithGradient(
	type: string, 
	ctx: any, 
	colorList: Array<string>, 
	coordinate: Array<number>, 
	word: Object<any>,
	lineHeight: number
) {
	let gradient: any = transparentColor
	const [x, y] = [...coordinate]
	if(word) {

		switch(type) {
			// 上下线性
			case '01':
				// gradient = ctx.createLinearGradient(x, y, x, y+word.rH);			
				gradient = ctx.createLinearGradient(x, y, x, y+lineHeight);			
				break
			// 下上线性
			case '02':
				// gradient = ctx.createLinearGradient(x, y+word.rH, x, y);
				gradient = ctx.createLinearGradient(x, y+lineHeight, x, y);
				break
			// 左右线性
			case '03':
				gradient = ctx.createLinearGradient(x, y, x+word.rW, y);
				break
			// 右左线性
			case '04':
				gradient = ctx.createLinearGradient(x+word.rW, y, x, y);		
				break
			// 左上到右下
			case '05':
				// gradient = ctx.createLinearGradient(x, y, x+word.rW, y+word.rH);	
				gradient = ctx.createLinearGradient(x, y, x+word.rW, y+lineHeight);	
				break
			// 右下到左上
			case '06':
				gradient = ctx.createLinearGradient(x+word.rW, y+word.rH, x, y);	
				break
			default:
				gradient = ctx.createLinearGradient(x, y, x, y+word.rH);		
				break
		}
	
		colorList.map((it, i) => {
			gradient.addColorStop(i/colorList.length, it);
		})
	}


	return gradient
}

二、自定义花字应用案例

/**
 *  绘制文本
 * @param {*} item 
 * @param {*} ctx 
 * @param {*} e 
 * @returns 
 */
export function drawText(item, ctx, e) {

	let res = null

	ctx.save()

	const noDraw = e?.noDraw || '0'
	const devicePixelRatio = e?.devicePixelRatio || 1

	ctx.lineJoin  = 'round' // 圆角

	// 字体样式 normal oblique
	// 字体粗细 normal bold lighter

	// 是否斜体
	let _italic = item.italic ? 'oblique ' : 'normal '
	// 字重
	let _weight = item.weight ? item.weight + ' ' : ''


	ctx.font = _italic + _weight + (item.fontSize * devicePixelRatio) + 'px ' + (item?.webFontFamily || item?.fontFamily)

	// 下划线
	// let underline = item.underline

	// 是否竖排
	let vertical = item.direction === 'vertical'
	// 文本对齐方式
	ctx.textAlign = vertical ? 'left' : item.align
	// 文本基线
	ctx.textBaseline = 'middle'

	let lineNum = 1
	if(item.type == '_zm') {
		lineNum = ctx.countLineNum(
			{
				italic: _italic,
				vertical,
				fontFamily: item.webFontFamily || item.fontFamily
			},

			item.content?.replace(/(\[[\=\#]([^\[\]]*)\])/g, ''),
			(item.left + item.width / 2) * devicePixelRatio,
			item.top * devicePixelRatio,
			item.width * devicePixelRatio,
			item.height * devicePixelRatio,
			item.fontSize * devicePixelRatio,
			item
		)

		item.height *= lineNum 
	}
	

	// 绘制文字背景色
	ctx.fillStyle = item.fontBgColor
	ctx.fillRect(item.left * devicePixelRatio, item.top * devicePixelRatio, item.width * devicePixelRatio, item.height * devicePixelRatio)


	// 阴影
	// if (item.shadow && item.shadow.blur) {
	// 	ctx.shadowColor = item.shadow.color
	// 	ctx.shadowBlur = item.shadow.blur * devicePixelRatio // 模糊级别

	// 	ctx.shadowOffsetX = item.shadow.x * devicePixelRatio // 阴影与形状的水平距离
	// 	ctx.shadowOffsetY = item.shadow.y * devicePixelRatio // 阴影与形状的垂直距离

	// 	// if(item.rotate == 0) {
	// 	// 	ctx.shadowOffsetX = item.shadow.x * devicePixelRatio // 阴影与形状的水平距离
	// 	// 	ctx.shadowOffsetY = item.shadow.y * devicePixelRatio // 阴影与形状的垂直距离
	// 	// }
	// 	// else if(item.rotate > 0) {
	// 	// 	ctx.shadowOffsetX = -item.shadow.x * devicePixelRatio // 阴影与形状的水平距离
	// 	// 	ctx.shadowOffsetY = item.shadow.y * devicePixelRatio // 阴影与形状的垂直距离
	// 	// } 
	// 	// else if(item.rotate < 0) {
	// 	// 	ctx.shadowOffsetX = item.shadow.x * devicePixelRatio // 阴影与形状的水平距离
	// 	// 	ctx.shadowOffsetY = -item.shadow.y * devicePixelRatio // 阴影与形状的垂直距离
	// 	// }
		
		
	// }

	// 绘制描边颜色
	// 渐变
	if (item.stroke && item.stroke.width > 0) {
		
		if(Object.prototype.toString.call(item.stroke.color) === '[object Array]') {
			let gradient = '#00000000'
			if(noDraw === '0') {

				gradient = workWithGradient(item.stroke?.colorGradientType, ctx, [...item.stroke.color], [item.left* devicePixelRatio, item.top* devicePixelRatio, item.left* devicePixelRatio, (item.top+item.height)* devicePixelRatio])

			}


			ctx.strokeStyle = gradient // 描边颜色
		} else {
			ctx.strokeStyle = item.stroke.color // 描边颜色
		}

		ctx.lineWidth = (item.fontSize/120)*item.stroke.width * devicePixelRatio // 描边大小
	}

	// 绘制文字颜色
	// 渐变
	// if(Object.prototype.toString.call(item.fontColor) === '[object Array]') {
	// 	let gradient = '#00000000'
		
	// 	if(noDraw === '0') {

	// 		gradient = workWithGradient(item?.colorGradientType, ctx, [...item.fontColor], [item.left* devicePixelRatio, item.top* devicePixelRatio, item.left* devicePixelRatio, (item.top+item.height)* devicePixelRatio])
			
	// 	}

	// 	ctx.fillStyle = gradient // 文字颜色
	// } else {
	// 	ctx.fillStyle = item.fontColor // 文字颜色
	// }
	


	const _content = item.type == 'zm' ? '智能字幕' : item.content?.replace(/(\[[\=\#]([^\[\]]*)\])/g, '')
	item.content = _content

	// 绘制花字
	if(item?.floweredFont && noDraw === '0') {
		res = drawFlowerWord(item, ctx, {...e})
	} else {
		res = ctx.wrapText(item, e)
	}


	ctx.restore()

	return res
}
/**
 * 绘制花字
 * @param {*} _item 
 * @param {*} ctx 
 * @param {*} e 
 */
export function drawFlowerWord(item, ctx, e) {
	// console.error('绘制花字:', item, e)

	// ctx.canvas.style.fontSmoothing = 'antialiased';

	const devicePixelRatio = e?.devicePixelRatio || 1
	let res = null

	// 绘制发光阴影
	if(item.floweredFont?.glow?.size) {
		ctx.save()
		let _item = _.cloneDeep(item)
		
		// 绘制发光阴影
		_item.shadow = {
			x: 0, // 阴影距离文字的x轴距离
			y: 0, // 阴影距离文字的y轴距离
			color: item.floweredFont.glow.color, // 阴影颜色
			width: 0, // 阴影宽度
			blur: item.floweredFont.glow.size // 模糊程度
		}
		delete _item.stroke
		res = ctx.wrapText(_item, e)

		ctx.restore()
	}

	// 绘制外大描边
	if(item.floweredFont?.outerStroke?.size) {
		ctx.save()
		let _item = _.cloneDeep(item)

		const s = (_item.fontSize/120) * devicePixelRatio
		_item.top = _item.top + s * _item.floweredFont.outerStroke.y
		_item.left = _item.left + s * _item.floweredFont.outerStroke.x

		_item.stroke = {
			color: _item.floweredFont.outerStroke.color,
			width: _item.floweredFont.outerStroke.size
		}
		_item.fontColor = _item.floweredFont.outerStroke.color

		ctx.strokeStyle = _item.stroke.color // 描边颜色
		ctx.lineWidth = (_item.fontSize/120) * _item.stroke.width * devicePixelRatio // 描边大小

		ctx.fillStyle = _item.floweredFont.outerStroke.color


		delete _item.shadow

		res = ctx.wrapText(_item, e)
		ctx.restore()
	}

	ctx.save()
	res = ctx.wrapText(item, e)
	ctx.restore()



	// 	// 背景图花字
	// 		// ctx.save()

	// 		// const drawImg = e.drawImg

	// 		// const pattern = ctx.createPattern(drawImg, 'repeat')

	// 		// drawText(item, ctx, {...e, pattern})

	// 		// ctx.restore()
	// }

	return res

}
import img01 from '@/assets/flowered-font/Frame 1.png'
import img02 from '@/assets/flowered-font/Frame 2.png'
import img03 from '@/assets/flowered-font/Frame 3.png'
import img04 from '@/assets/flowered-font/Frame 4.png'
import img05 from '@/assets/flowered-font/Frame 5.png'
import img06 from '@/assets/flowered-font/Frame 6.png'
import img07 from '@/assets/flowered-font/Frame 7.png'
import img08 from '@/assets/flowered-font/Frame 8.png'
import img09 from '@/assets/flowered-font/Frame 9.png'
import img10 from '@/assets/flowered-font/Frame 10.png'
import img11 from '@/assets/flowered-font/Frame 11.png'

const colorGradientType = '01' // 01 上下线性 02 下上线性 03 左右线性 04 右左线性
const outerStrokeColor = '#ffffffff' // 外部描边颜色
const outerStrokeSize = 40 // 外部描边大小
const glowSize = 50 // 发光阴影大小
const glowColor = '#ffb800ff' // 发光颜色
const transparentColor = '#00000000'

export const floweredFont: any = {
	'花字01': {
		icon: img01,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: '#ffffffff', // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#000000ff',
				width: 20
			},

			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: outerStrokeColor, // 外部描边颜色
					size: outerStrokeSize, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字02': {
		icon: img02,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: '#ffffffff', // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			// shadow: {
			// 	x: 6, // 阴影距离文字的x轴距离
			// 	y: 0, // 阴影距离文字的y轴距离
			// 	color: "#000000ff", // 阴影颜色
			// 	width: 0, // 阴影宽度
			// 	blur: 0 // 模糊程度
			// },
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#000000ff',
				width: 8
			},

			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 6,
					y: 2,
					color: "#000000ff", // 外部描边颜色
					size: 16, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字03': {
		icon: img03,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: ['#fbe966ff', '#fbe966ff', '#ffdb5bff', '#ffffffff'], // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: ['#742a0fff', '#904017ff', '#410805ff'],
				width: 20,
				colorGradientType, // 01 上下线性 02 下上线性 03 左右线性 04 右左线性
			},

			colorGradientType, // 01 上下线性 02 下上线性 03 左右线性 04 右左线性
			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: '#fef7c5ff', // 外部描边颜色
					size: outerStrokeSize, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字04': {
		icon: img04,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: ['#fff7adff', '#fff7adff','#ffa800ff'], // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 8, // 阴影距离文字的x轴距离
				y: 8, // 阴影距离文字的y轴距离
				color: "#511308ff", // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: ['#742a0fff', '#904017ff', '#410805ff'],
				width: 10,
				colorGradientType
			},
			
			colorGradientType: '01',
			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: outerStrokeColor, // 外部描边颜色
					size: 0, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: glowSize // 发光大小
				}
			} 
		}
	},

	'花字05': {
		icon: img05,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: ['#73c1faff', '#ffffffff'], // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#225487ff',
				width: 20
			},

			colorGradientType: '02',
			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: outerStrokeColor, // 外部描边颜色
					size: outerStrokeSize, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}

			} 
		}
	},

	'花字06': {
		icon: img06,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: ['#ffb0f7ff', '#ffffffff'], // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#4e0046',
				width: 20
			},

			colorGradientType: '02',
			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: outerStrokeColor, // 外部描边颜色
					size: outerStrokeSize, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字07': {
		icon: img07,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: '#97fff3ff', // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 6, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: "#000000ff", // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#000000ff',
				width: 10
			},

			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 12,
					y: 6,
					color: '#d3ffb1ff', // 外部描边颜色
					size: 1, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字08': {
		icon: img08,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: '#408770ff', // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: transparentColor,
				width: 0
			},

			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: outerStrokeColor, // 外部描边颜色
					size: 80, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字09': {
		icon: img09,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: '#dc9f9dff', // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 6, // 阴影距离文字的x轴距离
				y: 6, // 阴影距离文字的y轴距离
				color: "#fae297ff", // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#ffffffff',
				width: 25
			},

			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: outerStrokeColor, // 外部描边颜色
					size: 0, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字10': {
		icon: img10,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: '#ffffffff', // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: transparentColor,
				width: 0
			},

			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 4,
					y: 4,
					color: '#6d92ecff', // 外部描边颜色
					size: 12, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},

	'花字11': {
		icon: img11,
		data: {
			// iobsKey: 'eccb7a74720fc377b60d6b2110530fd9', // 字体文件
			fontFamily: 'HarmonyOS Sans', // 字体
			webFontFamily: 'HarmonyOS Sans', // 本地字体
			fontSize: 110, // 字号大小
			content: '花字', // 显示内容
			align: "left", // 文本对齐方式
			fontColor: ['#742a0fff','#904017ff','#410805ff', '#410805ff'], // 字体颜色
			fontBgColor: transparentColor, // 文本背景颜色
			weight: 900, // 字重,100:THIN;200:ULTRALIGHT;300:LIGHT;350:SEMILIGHT;380:BOOK;400:NORMAL;500:MEDIUM;600:SEMIBOLD;700:BOLD;800:ULTRABOLD;900:HEAVY;1000:ULTRAHEAVY
			italic: false, // 是否斜体,默认为false
			underline: false, // 是否下划线,默认为false
			direction: "horizontal", // 文字方向:vertical垂直,horizontal水平,默认为水平
			// 阴影,如果不存在,则没有阴影
			shadow: {
				x: 0, // 阴影距离文字的x轴距离
				y: 0, // 阴影距离文字的y轴距离
				color: transparentColor, // 阴影颜色
				width: 0, // 阴影宽度
				blur: 0 // 模糊程度
			},
			// 描边,如果不存在,则不描边
			stroke: {
				color: '#ffeed3ff',
				width: 12
			},

			colorGradientType: '05',
			// 花字数据
			floweredFont: {
				outerStroke: {
					x: 0,
					y: 0,
					color: '#b58b49ff', // 外部描边颜色
					size: 24, // 外部描边大小
				},
				glow: {
					color: glowColor, // 发光颜色
					size: 0 // 发光大小
				}
			} 
		}
	},
}

export function workWithGradient(
	type: string, 
	ctx: any, 
	colorList: Array<string>, 
	coordinate: Array<number>, 
	word: Object<any>,
	lineHeight: number
) {
	let gradient: any = transparentColor
	const [x, y] = [...coordinate]
	if(word) {

		switch(type) {
			// 上下线性
			case '01':
				// gradient = ctx.createLinearGradient(x, y, x, y+word.rH);			
				gradient = ctx.createLinearGradient(x, y, x, y+lineHeight);			
				break
			// 下上线性
			case '02':
				// gradient = ctx.createLinearGradient(x, y+word.rH, x, y);
				gradient = ctx.createLinearGradient(x, y+lineHeight, x, y);
				break
			// 左右线性
			case '03':
				gradient = ctx.createLinearGradient(x, y, x+word.rW, y);
				break
			// 右左线性
			case '04':
				gradient = ctx.createLinearGradient(x+word.rW, y, x, y);		
				break
			// 左上到右下
			case '05':
				// gradient = ctx.createLinearGradient(x, y, x+word.rW, y+word.rH);	
				gradient = ctx.createLinearGradient(x, y, x+word.rW, y+lineHeight);	
				break
			// 右下到左上
			case '06':
				gradient = ctx.createLinearGradient(x+word.rW, y+word.rH, x, y);	
				break
			default:
				gradient = ctx.createLinearGradient(x, y, x, y+word.rH);		
				break
		}
	
		colorList.map((it, i) => {
			gradient.addColorStop(i/colorList.length, it);
		})
	}


	return gradient
}
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值