nvue中 实现文本两端对齐效果 的 js解决方案

一、 前言

在你因为搜索关键词来到我这篇文章时,相信你已经深深的感受到nvue格式对样式编写的局限性了。
比如,在nvue中不能用text-align:justify设置文本两端对齐。

在这里插入图片描述

本人发现这个样式不能使用后,思考了许多代替解决的方法。但最终却发现,这个nvue吧,简直是把css两端对齐的方法全堵死了!text-align:justify不能用,letter-spacing 改字间距也不能用。

可是吧,需求又有这个需求,也不能不做不是?

毕竟俺们上面某位对这种小细节很有强迫症,我们之前也都简简单单解决了,所以这次,人家不是技术哪管咱们到这个nvue格式下是真的干啥啥不行了。

在这里插入图片描述

因为在网上也找不到什么解决的方法,所以本着没有粮食就要自己动手才能丰衣足食的观念,本人根据nvue能使用的css条件,用js写了一个算法,从理清思路,到编写逻辑代码,这篇代码用了一天多。虽然这件事很吃力不讨好,代码写得也比较稚嫩,但是本人在这个过程中还是感觉收获良多的。(一定程度锻炼了思维逻辑能力)

而且这样也算有了个备选方案,不至于感觉无计可施。

以下会记录本人写下这篇代码的所有思路历程。有兴趣的同学可以看完我这些思路历程,一起进步~~ 比较赶时间的同学,就跳到结尾看代码和使用方法吧。

二、思路历程

1.确定可用条件并构思

首先,我们知道nvue格式是只能使用flex布局,即display属性仅能为flex,且默认 flex-direction: column。text-align:justify、letter-spacing不能使用,但是flex布局的justify-content: space-between确定能使用。

justify-content: space-between 可以均匀排列每个元素,首个元素放置于起点,末尾元素放置于终点 。 根据属性描述,这不就是有 '两端对齐’的概念在吗?

所以我就想到,能不能根据用这个属性去写一下算法呢?

根据条件,我的脑子生成了一个基本构思。

核心逻辑其实比较简单,也就是 将文本里的字符拆分成独立的块 用justify-content: space-between 让字符在父盒子里自适应两端对齐。

可以用这段简单的试验代码理解。

// template
		<view class="box" >
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
				<text>这</text>
		</view>
// style
.box {
		flex-direction: row;// nvue 默认 lex-direction: column,所以一定要改方向
		flex-wrap: wrap;
		width: 100rpx;
		justify-content: space-between;
		background-color: red;
	}

确定思路,开始延展。

2.延展思路,编写逻辑,逐步优化

①将所有获取的文本拆分 splitLine()

//template
<view class="body">
		<view class="box" v-for="(item,index) in newText">
			<view v-for="it in item">
				<text>{{it}}</text>
			</view>
		</view>
	</view>
// style
.box{
		flex-direction: row;
		flex-wrap: wrap;
		width: 200rpx;//需要手动调试,匹配宽度和合适的字符数
		justify-content: space-between;
	}
// script
	export default {
		data() {
			return {
				dataText: [
					'这是什么呀?我完全看不到',
					'这是什么呀?我完全看不到,好奇怪啊qwqwqwqwqwqwqwqwqwqwq',
					'这是什么呀?我完全看不到,好奇怪啊qwqwqwqwqwqwqwqwqwqwq,老师你手机啊喜欢过的卡号',
					'这是什么呀?我完全看不到,好奇怪啊qwqwqwqwqwqwqwqwqwqwq,老师你手机啊喜欢过的卡号11111',
				],
				newText: []
			}
		},
		onLoad() {
			this.splitLine()
		},
		methods: {
			splitLine() {
				var that = this
				this.dataText.forEach((v, i) => {
				//设定一行的字符数line_num=10,定该文本的字符数为 word_num    
				//(需要手动调出合适当前父文本宽度的字符数)
					var word = v.split('')
					var word_num = v.split('').length
					var line_num=10
					var newWord=[]
					if (word_num < line_num) {
					//1.字符内容不到一行字符串不拆分
						newWord[0]=v
						that.newText[i] =newWord
					} else if ((word_num % line_num) < line_num) {
					//2.字符内容大于一行,但末行的字符少于一行字符 则需要截取前面字符串拆分,将多余字符截取为数组最后元素
						var newWord = v.substring(0, word_num - (word_num % line_num)).split('')
						var endWord = v.substring(word_num - (word_num % line_num), word_num)
						newWord.push(endWord)
						that.newText[i] = newWord
					} else {
					//3.字符内容大于一行且末行字符等于一行,全字符拆分即可
						that.newText[i] = word
					}
				})
			},
		}
	}			

但是,实践后发现,只判断字符数来拆分,会因为中文与英文和数字的字符位置占据大小不一样,导致出现每行字间距之间差异很大的情况,所以我们应该是去判断字节数而不是字符数

②优化判断思路,用 <计算字节> 替代 <计算字符> 判断

一段文本里可能会包含英文、数字、中文等,要根据文本内容去计算字节数,首先要理解字节概念。

前端主要为UTF-8编码,则字符串中的字节数,单个的字符,包括英文字符,数字,特殊字符(不包含emoji)等是一个字节,中文的汉字是两个字节。

//将是中文的部分,替换成两个英文,这样字节就全部都是普通的字节了,此时字节的长度就是字符串的长度。
getBytesLength(str) {
		return str.replace(/[^\x00-\xff]/g, 'xx').length;
},

所以我将之前的代码用字节思维再优化下,写了第一版的 splitBytesLine()

(需要手动调出合适当前父文本宽度的字节数 byte ,就是合适的中文字符数 * 2)

			splitBytesLine(arr,byte) {
			//参数说明:arr为原文本数组,byte设定每行的合适字节数
 
				var that = this
				var newTextArr=[]
				arr.forEach((v, i) => {
					//计算字符串的字节数
					var bytes_num = this.getBytesLength(v)
					//将字符串拆分成数组
					var word = v.split('')
					//定义处理后的数组存放容器
					var newWord=[]
					//根据文本字节对于单行字节数的情况处理
					if (bytes_num < byte) {
						// 字节不到一行情况,不拆分,字符串全部存入数组
						console.log('字节不到一行情况')
						newWord[0]=v
						newTextArr[i] = newWord
					} else if ((bytes_num % byte) < byte) {
						// 字节有多行,并且最后一行不足一行字节
						console.log('字节有多行,并且最后一行不足一行字节')
						var word_num = v.split('').length
						// 先假设字节余数ys为偶数6 (带上具体数据更好理解,小括号有写上公式)
						
						// 截取最后同余数ys同数的(6)字符计算字节,设为e
						
						// 1.if e=6 (e=ys),即最后字符皆为非中文字符,则截取文本 0至 word_num-ys【6】 拆分为数组,剩余字符串(6个字符)不拆分push进数组
						// 2.if e=12 (e=ys*2),即最后字符皆为中文字符,则截取文本 0至 word_num-(ys/2) 【3】 拆分为数组,剩余字符串(3个字符)不拆分push进数组
						// 3.if 6<ys<12 (ys<e<ys*2),即最后字符有中文字符和非中文字符,
						// ① e%2为 0是偶数(8),截取文本 最后 e/2 的字符计算字节e2,if e2=ys,则截取文本0至 最后e/2; if e2<ys,截取word_num-(e/2 + 1) 至 word_num-(e/2 )字符计算字节k ==>> 如果k+e2>= ys,最终就截取文本 0至 word_num-(e/2 + 1) 拆分为数组,剩余字符串(e/2 + 1个字符)不拆分push进数组;
						// 如果k+e2<ys,则继续截取word_num-(e/2 + 2) 至 word_num-(e/2 + 1)字符计算字节k2,直到(k+k2+...+kn)+e/2 >= ys,停止循环;
						// if e2>ys,就截取 word_num-e/2至 word_num-(e/2 - 1)字符计算字节k ==> 如果 e2-k<=ys,就截取文本 0 至 word_num-(e/2 - 1),如果e2-k>ys 就继续截取word_num-(e/2 -1) 至 word_num-(e/2 - 2)字符计算字节k2,直到e/2 -(k+k2+...+kn) <= ys,停止循环;
						// ② e%2为 1是偶数(9),截取文本 最后 e/2 +1 的字符计算字节e2,重复操作.

						// 计算字节余数
						var ys = bytes_num % byte
						// 截取同字节余数数的最后字符
						var jq = v.substring(word_num - ys, word_num)
						// 计算最后字符的字节
						var e = this.getBytesLength(jq)
						
						if (e == ys) {
							// 最后字符全是非中文字符情况
							console.log('最后字符全是非中文字符情况')
							var newWord = v.substring(0, word_num - ys).split('')
							var endWord = v.substring(word_num - ys, word_num)
							newWord.push(endWord)
							newTextArr[i] = newWord
						} else if (e == (ys * 2) ) {
							// 最后字符全是中文字符情况
							console.log('最后字符全是中文字符情况')
							var newWord = v.substring(0, word_num - (ys / 2)).split('')
							var endWord = v.substring(word_num - (ys / 2), word_num)
							newWord.push(endWord)
							newTextArr[i] = newWord
						} else {
							// 最后字符有中文字符和非中文字符情况
							console.log('最后字符有中文字符和非中文字符情况')
							if (e % 2) {
								console.log('字节数为偶数情况')
								// 字节数为偶数情况 	// 目的:获取匹配余数ys的实际最后字符数截取文本,为优化效率,从中间截取左右加减,直到 ±k+e/2=ys
								let jq2 = v.substring(word_num - (e / 2), word_num)
								console.log(jq2)
								var e2 = that.getBytesLength(jq2)

							} else {
								// 字节数为奇数情况
								console.log('字节数为奇数情况')
								let jq2 = v.substring(word_num - (e / 2)-1, word_num)
								console.log(jq2)
								var e2 = that.getBytesLength(jq2)
							}

							// 匹配最后字节的字符情况
							if (e2 < ys) {
								// 中位数值小于目标余数ys值,需要向左做加法

								// 计算左边第一个单字符的字节
								let jq3 = v.substring(word_num - (e / 2) - 1, word_num - (e / 2))
								var k = that.getBytesLength(jq3)
								// 逐步循环左边单字符并加上其字节数,直到字节数=余数ys,停止循环
								for (let i = 1; i < (e / 2) + 1; i++) {
									if (k + e2 >= ys) {
										var newWord = v.substring(0, word_num - (e / 2) - i + 1).split('')
										var endWord = v.substring(word_num - (e / 2 + i - 1), word_num)
										break
									} else {
										let jq4 = v.substring(word_num - (e / 2) - i, word_num - (e / 2) - i + 1)
										k = k + that.getBytesLength(jq4)
									}
								}
							} else if (e2 > ys) {
								// 中位数值大于目标余数ys值,需要向右做减法
								
								// 计算右边第一个单字符的字节
								let jq3 = v.substring(word_num - (e / 2), word_num - (e / 2) + 1)
								var k = that.getBytesLength(jq3)
								// 逐步循环右边单字符并减去其字节数,直到字节数=余数ys,停止循环
								for (let i = 1; i < (e / 2) + 1; i++) {
									if (e2 - k >= ys) {
										var newWord = v.substring(0, word_num - (e / 2) + i + 1).split('')
										var endWord = v.substring(word_num - (e / 2) + i + 1, word_num)
										break
									} else {
										let jq4 = v.substring(word_num - (e / 2) + i, word_num - (e / 2) + i + 1)
										k = k - that.getBytesLength(jq4)
									}
								}
							} else {
								var newWord = v.substring(0, word_num - (e / 2)).split('')
								var endWord = v.substring(word_num - (e / 2), word_num)
							}
							newWord.push(endWord)
							newTextArr[i] = newWord
						}
					} else {
						// 字节有多行,且最后一行字节正好满一行,直接拆分成数组
						console.log('字节有多行,且最后一行字节正好满一行,直接拆分成数组')
						newTextArr[i] = word
					}
				})
				return newTextArr
			}

目前的算法可实现包含中文、英文、数字和*&%等特殊字符的文本两端对齐效果。

三、第一版算法的使用指南

①复制上面 splitBytesLine(),getBytesLength()方法到methods里。

②dataText 存放原文本,一个段落是一个数组元素。newTextDate 存放处理后的数组。调用方法,转换数据。

//template
data() {
			return {
				dataText: [
					'这是什么呀?我完全看不到',
					'这是什么呀?我完全看不到,好奇怪啊qwqwqwqwqwqwqwqwqwqwq',
					'这是什么呀?我完全看不到,好奇怪啊qwqwqwqwqwqwqwqwqwqwq,老师你手机啊喜欢过的卡号',
					'这是什么呀?我完全看不到,好奇怪啊qwqwqwqwqwqwqwqwqwqwq,老师你手机啊喜欢过的卡号11111',
				],
				newTextDate: []
			}
		},
		onLoad() {
			this.newTextDate=this.splitBytesLine(this.dataText,34)
			//参数说明:(原文本数组,每行的合适字节数)
		},

③html嵌套两层for,外层循环段落,内层循环段落里的每一个文字。

//template
<view class="body">
		<view class="box" v-for="(item,index) in newText">
		//循环段落
			<view v-for="it in item">
			//循环文字
				<text>{{it}}</text>
			</view>
		</view>
	</view>

④写上必填样式

// style
.box{
		flex-direction: row;
		flex-wrap: wrap;
		width: 298rpx;//需要手动调试,匹配宽度和合适的字符数
		justify-content: space-between;
	}

因为写的文章跨了几天时间,写法很稚嫩,文章中的一些代码可能大概会出现匹配不上还是啥bug的情况,如果发现文章有问题或者有大佬想指导建议俺都非常欢迎嗷~

然后其实我已经写了第二版的算法,代码也越来越长了 ,可以兼容多了些情况 ,并封装起来用了,但是写成文章真的太麻烦了 ,不过后续我还是会继续更新的~

在这里插入图片描述

————————————————————————————————————————————————————————

第二版本算法

来了来了,我终于来更新了

第二版算法,新增可支持第一行字数不同的情况(用于第一行缩进两格之类的需求)

function splitBytesLine(arr, byte_one, byte) {
	//参数说明:arr为原文本数组,byte_one设定第一行字节数,byte设定每行的合适字节数
	var newText = []
	arr.forEach((v, i) => {
		var bytes_num = v.replace(/[^\x00-\xff]/g, 'xx').length
		var word = v.split('')
		var middle = 0
		var newWord = []
		var startWord = []
		var word_num = v.split('').length
		if (bytes_num > byte_one) {
			// 兼容第一行特殊化情况
			console.log("兼容第一行特殊化情况")

			var jq = v.substring(0, byte_one)
			// 计算最前byte_one个字符的字节
			var e = jq.replace(/[^\x00-\xff]/g, 'xx').length
			console.log(e)
			console.log(jq)
			if (e == byte_one) {
				// 最前字符全是非中文字符情况
				console.log("最前字符全是非中文字符情况")
				startWord[0] = v.substring(0, byte_one - 1)
				startWord[1] = v.substring(byte_one - 1, byte_one)
				var middle = byte_one
			} else if (e / 2 == byte_one) {
				// 最前字符全是中文字符情况
				console.log("最前字符全是中文字符情况")
				startWord[0] = v.substring(0, byte_one / 2 - 1)
				startWord[1] = v.substring(byte_one / 2 - 1, byte_one / 2)
				var middle = byte_one / 2
			} else {
				console.log("最前字符有中文字符和非中文字符情况")
				// 最前字符有中文字符和非中文字符情况
				// 前byte_one个字符的字节大于byte_one,需要向左做减法

				// 计算左边第一个单字符的字节
				let jq3 = v.substring(byte_one - 1, byte_one)
				var k = jq3.replace(/[^\x00-\xff]/g, 'xx').length

				for (let i = 1; i < byte_one + 1; i++) {
					if ((e - k) <= byte_one) {
						startWord[0] = v.substring(0, byte_one - i - 1)
						// startWord[1] = ''
						startWord[1] = v.substring(byte_one - i - 1, byte_one - i)
						var middle = byte_one - i
						break
					} else {
						let jq4 = v.substring(byte_one - i - 1, byte_one - i)
						k = k + jq4.replace(/[^\x00-\xff]/g, 'xx').length
					}
				}
			}
		}
		console.log(startWord)
		if (startWord.length) {
			newWord = newWord.concat(startWord)
		}
		console.log(newWord)
		var middleWord = []
		var endWord = ''
		if (bytes_num <= byte_one) {
			console.log('字节不到一行情况')
			newWord[0] = v
			newText[i] = newWord
		} else if (bytes_num < (byte_one + byte)) {
			// 字节不到两行情况
			console.log("字节不到两行情况")
			endWord = v.substring(middle, word_num)
			newWord.push(endWord)
			newText[i] = newWord
		} else if (((bytes_num - byte_one) % byte) < byte) {
			// 字节有多行,并且最后一行不足一行字节
			console.log("字节有多行,并且最后一行不足一行字节")
			// 计算字节余数
			var ys = (bytes_num - byte_one) % byte
			console.log("余数", ys)
			// 截取同字节余数数的最后字符
			var jq = v.substring(word_num - ys, word_num)
			// 计算最后字符的字节
			var e = jq.replace(/[^\x00-\xff]/g, 'xx').length
			if (e == ys) {
				// 最后字符全是非中文字符情况
				console.log("最后字符全是非中文字符情况")
				var middleWord = v.substring(middle, word_num - ys).split('')
				var endWord = v.substring(word_num - ys, word_num)
			} else if (e == (ys * 2)) {
				// 最后字符全是中文字符情况
				console.log("最后字符全是中文字符情况")
				var middleWord = v.substring(middle, word_num - (ys / 2)).split('')
				var endWord = v.substring(word_num - (ys / 2), word_num)
			} else {
				// 最后字符有中文字符和非中文字符情况
				console.log("最后字符有中文字符和非中文字符情况")
				if (e % 2) {
					console.log("字节数为偶数情况")
					// 字节数为偶数情况 	// 目的:获取匹配余数ys的实际最后字符数截取文本,为优化效率,从中间截取左右加减,直到 ±k+e/2=ys
					let jq2 = v.substring(word_num - (e / 2), word_num)
					var e2 = jq2.replace(/[^\x00-\xff]/g, 'xx').length

				} else {
					// 字节数为奇数情况
					console.log("字节数为奇数情况")
					let jq2 = v.substring(word_num - (e / 2) - 1, word_num)
					var e2 = jq2.replace(/[^\x00-\xff]/g, 'xx').length
				}
				// 匹配最后字节的字符情况
				if (e2 < ys) {
					// 中位数值小于目标余数ys值,需要向左做加法

					// 计算左边第一个单字符的字节
					let jq3 = v.substring(word_num - (e / 2) - 1, word_num - (e / 2))
					var k = jq3.replace(/[^\x00-\xff]/g, 'xx').length
					// 逐步循环左边单字符并加上其字节数,直到字节数=余数ys,停止循环
					for (let i = 1; i < (e / 2) + 1; i++) {
						if (k + e2 >= ys) {
							var middleWord = v.substring(middle, word_num - (e / 2) - i + 1).split('')
							var endWord = v.substring(word_num - (e / 2 + i - 1), word_num)
							break
						} else {
							let jq4 = v.substring(word_num - (e / 2) - i, word_num - (e / 2) - i + 1)
							k = k + jq4.replace(/[^\x00-\xff]/g, 'xx').length
						}
					}
				} else if (e2 > ys) {
					// 中位数值大于目标余数ys值,需要向右做减法
					// 计算右边第一个单字符的字节
					let jq3 = v.substring(word_num - (e / 2), word_num - (e / 2) + 1)
					var k = jq3.replace(/[^\x00-\xff]/g, 'xx').length
					// 逐步循环右边单字符并减去其字节数,直到字节数=余数ys,停止循环
					for (let i = 1; i < (e / 2) + 1; i++) {
						if (e2 - k >= ys) {
							var middleWord = v.substring(middle, word_num - (e / 2) + i + 1).split('')
							var endWord = v.substring(word_num - (e / 2) + i + 1, word_num)
							break
						} else {
							let jq4 = v.substring(word_num - (e / 2) + i, word_num - (e / 2) + i + 1)
							k = k - jq4.replace(/[^\x00-\xff]/g, 'xx').length
						}
					}
				} else {
					var middleWord = v.substring(middle, word_num - (e / 2)).split('')
					var endWord = v.substring(word_num - (e / 2), word_num)
				}
			}
			newWord = newWord.concat(middleWord)
			newWord.push(endWord)
			newText[i] = newWord
		} else {
			// 字节有多行,且最后一行字节正好满一行,直接拆分成数组
			console.log("字节有多行,且最后一行字节正好满一行,直接拆分成数组")
			newText[i] = word
		}

	})
	console.log(newText)
	return newText
}

不过但是发现写着写着有点鸡肋,就又换了一个思路去写
最终版

Logo

前往低代码交流专区

更多推荐