一、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
}