小程序 画布 文本绘制
2020-09-07 本文已影响0人
IamaStupid
情景:有很多文本,有的是一行,有的是多行的段落,这么多文本,需要一个方法来处理。
代码:
onload () {
......
// CanvasContext.setTextAlign() 是根据坐标来展示文字的,具体用法去官网
this.drawText(ctx, {
textAlign: 'right',
fontColor: '#f00',
fontSize: 30,
text: 'Hello',
x: 300,
y: 30
});
this.drawText(ctx, {
textAlign: 'left',
text: 'Hello 中国!Hello word! Hello Everyone!',
x: 20,
y: 80,
maxWidth: 100
});
this.drawText(ctx, {
textAlign: 'center',
text: 'Hello 中国! Hello word! Hello Everyone! ',
x: 20 + 50,
y: 150,
maxWidth: 100,
isWrap: true,
charDistace: 2,
wrapLineHeight: 40
});
......
},
......
// charDistace 这个属性的设置,主要是为了换行的时候,减少循环的次数
// 换行的原理:一个一个文字叠加,然后测量它们的宽度,然后和最大的宽度值进行对比
// 于是设置了charDistace 属性,自定义一次叠加的字数,默认一个,也可以设置为多个
drawText (ctx, options) {
// textAlign type: string
if (options.textAlign) {
ctx.setTextAlign(options.textAlign)
}
//fontColor type: string
if (options.fontColor) {
ctx.setFillStyle(options.fontColor)
}
// fontSize type: number
if (options.fontSize) {
ctx.setFontSize(options.fontSize)
}
if (!options.isWrap) {
ctx.fillText(options.text, options.x, options.y, options.maxWidth);
} else {
// options.isWrap 是否换行
// options.charDistace 默认1,表示一次循环一个字符, 这个值在isWrap为true时才有效
// options.wrapLineHeight 行的高度,这个值在isWrap为true时才有效
let charArr = options.text.split('');
let index = 0;
let end = 0;
let tempStr = '';
let metrics = null;
let d = options.charDistace || 1;
let i = d;
let tempI = 0;
let gap1 = 2; // 表示 maxw -xw <= 2 都可以当做相等
let gap2 = 2; // 如果xw - maxw < = 2 ,那么也当做相等
let isTextBigger = false;
let textBiggerIndex = 0;
let wrapNum = 0;
// 一次循环5个字符,并将循环的字符叠加,比如 i = 15, 测量它们的宽度xw15
// 如果xw15比最大的maxw大,则退回到 i = 11的状态,即11个字符长度 xw11
// 如果xw11 > maxw, 那么可以确定这行最多只能放10个字符,
// 因为xw10的时候,发生的下一次循环,说明xw10 < maxw
// 如果xw < maxw, 则开始一个一个字符循环,不再是5个5个字符循环
while (i < charArr.length) {
end = i;
// 不包含end
tempStr = charArr.slice(index, end).join('');
metrics = ctx.measureText(tempStr);
if ((options.maxWidth > metrics.width && options.maxWidth - metrics.width <= gap1) ||
(metrics.width > options.maxWidth && metrics.width - options.maxWidth <= gap2) ||
(metrics.width < options.maxWidth && isTextBigger && textBiggerIndex - i === 1 ) ||
(metrics.width < options.maxWidth && i === (charArr.length - 1))) {
// 字符宽度刚好符合最大宽度要求
// 另一种情况:加一个字符又太宽 > gap2; 减一个字符又太小 < gap1
// 另一种情况:最后一行
wrapNum++;
ctx.fillText(tempStr, options.x, options.y + (options.wrapLineHeight * wrapNum -1), options.maxWidth);
tempI = end;
index = end;
i = i + d;
isTextBigger = false;
} else {
// 字符宽度不符合最大宽度
if (metrics.width < options.maxWidth) {
// 字符宽度太小
tempI = i;
if (isTextBigger) {
i++
} else {
if ((i + d) > charArr.length) {
i++
} else {
i += d
}
}
} else {
// 字符宽度太大
isTextBigger = true;
textBiggerIndex = i;
i = tempI;
}
}
// console.log(tempStr, metrics)
}
}
},
