赞
踩
在使用 canvas 绘制文本的过程中,对于很长的文本,canvas 不能自动的进行换行处理,另外小程序无法像 web 端那样很方便的使用 svg,所以在此做一个简单的记录。
在实现之前简单的分析一下,要实现文本换行功能,在 canvas 中我们使用的是 fillText(text, x, y, maxWidth)
方法,
假设我们绘制的文本有最大的宽度,在超出这个宽度之后就进行换行,所以我们得知道绘制文本的宽度。
设置字距,如果是 css 那么就简单,直接使用 letter-spacing
即可,当然可以给 canvas 直接设置,
但是对于部分场景下可能操作起来不是那么方便,所以还在在绘制上面下文章,既然不直接设置,
那么就只有绘制一个字符就隔开一定间距再绘制另外一个字符,也就是逐字绘制。
<template> <view class="content"> <view class="ele-container"> <!-- 微信使用 canvas 2d --> <!-- <canvas type="2d" id="nnCanvas"></canvas> --> <!-- 微信不使用 canvas 2d --> <canvas canvas-id="nnCanvas"></canvas> <view class="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 2d this.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 } = this let textLen = tempText.length this.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 2d this.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 绘制的效果和下面标签渲染的效果差不到很多。
实现始终都是按照自己项目的需求来的,不同的项目,近似的一个需求可能实现的方式就稍微不同。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。