小程序 canvas 绘制文本实现换行,设置字距
小程序 canvas 绘制文本实现换行,设置字距
在使用 canvas 绘制文本的过程中,对于很长的文本,canvas 不能自动的进行换行处理,另外小程序无法像 web 端那样很方便的使用 svg,所以在此做一个简单的记录。
浅析
在实现之前简单的分析一下,要实现文本换行功能,在 canvas 中我们使用的是 fillText(text, x, y, maxWidth)
方法,
假设我们绘制的文本有最大的宽度,在超出这个宽度之后就进行换行,所以我们得知道绘制文本的宽度。
设置字距,如果是 css 那么就简单,直接使用 letter-spacing
即可,当然可以给 canvas 直接设置,
但是对于部分场景下可能操作起来不是那么方便,所以还在在绘制上面下文章,既然不直接设置,
那么就只有绘制一个字符就隔开一定间距再绘制另外一个字符,也就是逐字绘制。
实现
<template><view class="content"><view class="ele-container"><canvas canvas-id="nnCanvas">canvas><viewclass="test-text":style="{width: width + 'px',fontSize: fontSize + 'px',lineHeight: fontSize + lineHeight + 'px',letterSpacing: letterSpacing + 'px'}"><text>{{ testText }}text>view><view><button @click="handleFontSize">字号button><button @click="handleLineHeight">行高button><button @click="handleLetterSpacing">字距button>view>view>view>template><script>export default {data() {return {ctx: null,width: 200,fontSize: 14,lineHeight: 0,letterSpacing: 0,testText: ' 这是一段\n很长很长很长\r\n很长很长很\n\n长很长很长很长的 12314223asjedhweihdf hello world xxx文本 '}},mounted() {// // 微信使用 canvas 2d// const _this = this// const query = wx.createSelectorQuery()// query.select('#nnCanvas')// .fields({ node: true, size: true })// .exec((res) => {// let canvas = res[0].node// _this.ctx = canvas.getContext('2d')// _this.handleDrawText()// })// 微信不使用 canvas 2dthis.ctx = uni.createCanvasContext('nnCanvas')this.handleDrawText()},methods: {handleDrawText() {// // 微信使用 canvas 2d// this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)// 多个连续空格只保留一个let tempText = this.testText.replace(/(^\s*)|(\s*$)/g, ' ').replace(/ +/g, ' ')let { fontSize, letterSpacing } = thislet textLen = tempText.lengththis.ctx.font = `${fontSize}px YaHei`// 如果绘制的文本总宽度并没有超过最大宽度,那么直接绘制if (this.ctx.measureText(tempText).width +textLen * letterSpacing < this.width) {this.ctx.fillText(tempText, 0, fontSize)} else {let strArray = []let tempStr = ''for (let i = 0; i < textLen; i++) {let currStr = tempText[i]// 判断当前字符是否是换行符,如果是,那么新增下一行let isWrap = !/\r?\n/.test(currStr)if (isWrap &&(this.ctx.measureText(tempStr + currStr).width +(tempStr.length + 1) * letterSpacing) < this.width) {// 如果不是换行符且当前绘制的文本宽度加字距小于最大宽度,直接当前行字符串直接拼接tempStr += currStr} else {// 否则就是当前行的宽度已经达到极限,进行换行strArray.push(tempStr)if (isWrap) {// 如果不是换行符,直接新的一行开头就是这个字符tempStr = currStr} else {// 否则,新的一行开头将换行符替换为空字符串tempStr = ''}}}// 如果还有剩余结尾的字符串,直接就算作一行if (tempStr != '') {strArray.push(tempStr)tempStr = ''}strArray.forEach((str, index) => {// 逐行绘制,绘制的 y 坐标当前行行数加上字体大小加上行高近似的模拟this.handleDrawOneLineText(str, (index + 1) * (fontSize + this.lineHeight))})// 微信不使用 canvas 2dthis.ctx.draw()}},handleDrawOneLineText(str, y) {let tempStr = ''for (let i = 0, len = str.length; i < len; i++) {// 逐字绘制,每行的y不变,单个字符的x是前面绘制文本的宽度加上每个字符的间距和this.ctx.fillText(str[i],i * this.letterSpacing + (this.ctx.measureText(tempStr).width),y)tempStr += str[i]}},// 测试变化,方便查看效果handleFontSize() {if (this.fontSize < 20) {this.fontSize += 1} else {this.fontSize -= 1}this.handleDrawText()},handleLineHeight() {if (this.lineHeight < 5) {this.lineHeight += 1} else {this.lineHeight -= 1}this.handleDrawText()},handleLetterSpacing() {if (this.letterSpacing < 10) {this.letterSpacing += 1} else {this.letterSpacing -= 1}this.handleDrawText()}}}script><style lang="scss" scoped>.test-text {// 为了让效果看上去一致,所以使用这个属性,// 不然单词和连续数字截断效果就不一样了word-break: break-all}style>
效果
可以看到上面 canvas 绘制的效果和下面标签渲染的效果差不到很多。
最后
实现始终都是按照自己项目的需求来的,不同的项目,近似的一个需求可能实现的方式就稍微不同。
来源地址:https://blog.csdn.net/asdf11111_____/article/details/133794314
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341