weex实现富文本RichText效果
话不错说,先上效果图:

由于最近公司需求,仿微信朋友圈,评论回复@的人需要实现变蓝色效果,研究了一番,发现weex-ui里面的富文本控件满足不了要求,weex官网提供的RichText经检测,只能在0.20.0.0的版本才能正常使用,所以参考他们的源码,写了一个自己的富文本。
weex-ui源码:https://github.com/alibaba/weex-ui
首先分析一波:
1,要实现不同颜色的字体,这里肯定是for循环创建了多个text,所以这里你看到,其实是多个text标签,而非一个text标签实现的。
2,既然是多个text标签,如何截取给定的字符串,找到换行的具体位置是整个RichText实现的关键所在。(当然这里主要是算法上的一些操作,因人而异,最终的目的就是找出换行的位置)。
其实看到这里,相信每个人心里都有一些思考,那么这里先上效果分析图:

上面每一个红色矩形都表示一个text标签,可以看到,这里一共有8个text标签。
话不多说,上代码:
<template>
<div style="width:750px;align-items: center; background-color: #0094ff">
<div style="flex-wrap: wrap; flex-direction: row;background-color: #ffffff" :style="{width:RichTextwidth+'px'}">
<div v-for="(item,index) in data" :key="index">
<text :style="{color:item.color,fontSize:item.size + 'px'}">{{item.content}}</text>
</div>
</div>
</div>
</template>
<script>
export default {
name: "RichText",
data () {
return {
data:[],
}
},
props:{
//数据源,我们这里传进来就是一个数组包,数组中的每一个对象包含以下三个字段
//color 这里的color跟我们在Style里面写的color是一样的,例如白色,这里就写color:'#ffffff'
//size 这里的size跟style里面的font-size有区别,这里的类型为数字,例如style里面的font-size:28px;那么这里就写 color:28
//content 这就是我们需要展示的内容,类型为String
datas:Array,
//设置RichText的宽度,默认为690,注意这里的类型是数字,而不是我们在style中写的那样
RichTextwidth:{
type:Number,
default:690,
},
},
mounted() {
//这个地方对数据做重新封装之后再添加到data里面去
let data = this.datas;
//对data做判空处理,不为空时这里为true
if (data){
if (data.length){
//这个数组是我们对传入数组处理之后得到的新的数据源
let tempContent = [];
//定义当前行可用的展示空间,第一行的时候,默认就是设置的行宽
let endLenght = this.RichTextwidth;
//对传入的数据源做for循环操作得到每一个具体的元素
for (let i = 0; i < data.length; i++) {
//获取到当前索引下的content
let tempStr = data[i].content;
//这里跟Java有细微区别,通过split转换成字符数组
let char = tempStr.split("");
//strLength表示当前字符串的内容长度,默认是0
let strLength = 0;
//ratio 表示当前字符的宽度占比
let ratio = 0;
/**
* 需要说明两点点,一:这里的ratio并不是一个准确的值,这个是根据50px下 750px宽度内 中文 英文大小写 数字最大个数 计算出来的一个比率
* 经测试,发现这样计算出来的比率 在font-size为40px的情况下展示效果是最好的,所以我的效果图,也是按照size为40给出的。
* 二:这里由于无法判断符号是中文还是英文的,所以在此未对符号做兼容,如果你有什么好的方法,不妨在下方评论留言,谢谢
* */
for (let j = 0; j < char.length; j++) {
//常规操作
if (0 <= char[j] && char[j] <= 9){
//数字0~9
ratio = 0.56;
} else if ('a' <= char[j] && char[j] <= 'z'){
//小写字母a~z
ratio = 0.51;
} else if ('A' <= char[j] && char[j] <= 'Z'){
//大些字母A~Z
ratio = 0.64;
} else {
//中文和符号暂不做区分,宽度系数就为1,
ratio = 1;
}
//总长度做求和操作
strLength += data[i].size * ratio
}
// 对比当前字符串长度是否小于当前可展示空间
if (strLength >= 0 && strLength <= endLenght){
//改变结束位置的长度,用来动态对比下一个元素是否需要直接存储
endLenght = endLenght - strLength;
//长度小于当前可用空间长度,直接存储到数组中
tempContent.push(data[i]);
} else {
//截取当前字符串长度,按照当前可用空间做截取
let arr = this.subStr2Length(data[i], endLenght);
//当前行可用空间能展示的当前字符串的最大索引值
let index = arr[0];
//当前可用空间展示的字符串的真实长度
let lenght = arr[1];
/**
* 计算剩余长度的字符串还可以占几行
* 需要注意一点,Vue里面的“/”除操作跟Java有区别,这里得到的值是浮点数,也就是小数,举例,12/10=1.2,跟Java有区别
* */
let rows = (strLength - lenght) / this.RichTextwidth;
//将刚刚计算出的需要添加的元素添加到数组中
let item1 = {
//截取真实长度填充到当前行末尾的可用空间中
content:data[i].content.substring(0,index),
size:data[i].size,
color:data[i].color,
};
tempContent.push(item1);
//判断剩下的内容是否新起一行可以展示完
if (rows <= 1){
//如果可以展示完,直接添加
let item2 = {
/**
* 需要说明一点,这里的subString跟Java中有细微区别,Vue中subString不会出现索引越界的问题,超出的话,截取至末尾
*/
content:data[i].content.substring(index,data[i].content.length),
size:data[i].size,
color:data[i].color,
};
tempContent.push(item2);
//添加完成,需要修改我们的可用空间的值,以便于下一次进行对比
endLenght = this.RichTextwidth - (strLength - lenght);
} else {
//如果剩余长度,大于等于2行时
//定义一个值,记录最后一行所占的长度
let lenght = 0;
//循环操作剩余的长度
for (let j = 0; j < rows; j++) {
//获取数据源中剩余的可用于展示的内容
data[i].content = data[i].content.substring(index ,data[i].content.length);
//计算当前长度下能展示到的索引,和真实展示的长度
let arr = this.subStr2Length(data[i], this.RichTextwidth);
//获取当前行最后一个元素的index,这里取值是长度刚刚超过一行时的索引,因为subString这个方法包含左不包含右,
index = arr[0];
//获取真实展示的长度
lenght = arr[1];
let item2 = {
content:data[i].content.substring(0,index),
size:data[i].size,
color:data[i].color,
};
tempContent.push(item2);
}
//记录最后一行的可用空间
endLenght = this.RichTextwidth - lenght;
}
}
}
this.data = this.data.concat(tempContent);
}
}
},
methods : {
//按照长度(px)裁剪当前字符串,返回一个数组,0索引位置返回的是当前换行时的index,1索引位置记录的是截取的元素的真实长度。
subStr2Length (data , lenght) {
if (data.content){
let char = data.content.split("");
if (lenght && lenght != 0){
let tempLenght = 0;
for (let j = 0; j < char.length; j++) {
let ratio = 0;
if (0 <= char[j] && char[j] <= 9){
ratio = 0.56;
} else if ('a' <= char[j] && char[j] <= 'z'){
ratio = 0.51;
} else if ('A' <= char[j] && char[j] <= 'Z'){
ratio = 0.64;
} else {
//中文和符号暂不做区分,宽度系数就为1,
ratio = 1;
}
tempLenght += data.size * ratio;
if (tempLenght > lenght){
let arr = [j, tempLenght - data.size * ratio];
return arr;
}
}
//当截取传入的长度超出了剩余长度的时候,返回最后的真实长度和索引
return [char.length, tempLenght];
} else {
return [0,0];
}
}
}
}
}
</script>
<style scoped>
</style>
以上是RichText控件的代码,算法上可以根据自己的需求做相应的修改,接下来提供一个使用的demo代码:
<template>
<div style="flex: 1">
<RichText :datas="data"></RichText>
</div>
</template>
<script>
import RichText from "./RichText";
export default {
name: "TestDemo",
components: {RichText},
data () {
return {
data:[
{
content:'张三张三张三张三张三张张三张三张三张三张三张',
size:'40',
color:'#00ffff',
},
{
content:'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
size:'40',
color:'#0094ff',
},
{
content:'abcdefghijklmnopqrstuvwxyzabcdefj',
size:'40',
color:'#ff94ff',
},
{
content:'012345678901234567890123456',
size:'40',
color:'#9494ff',
},
]
}
},
}
</script>
<style scoped>
</style>
以上代码如果有任何问题,都可以直接在下方留言,同时感谢大家指出的问题。
网友评论