weex實現(xiàn)富文本RichText效果
話不錯說,先上效果圖:

1559011516332.png
由于最近公司需求,仿微信朋友圈,評論回復@的人需要實現(xiàn)變藍色效果,研究了一番,發(fā)現(xiàn)weex-ui里面的富文本控件滿足不了要求,weex官網(wǎng)提供的RichText經(jīng)檢測,只能在0.20.0.0的版本才能正常使用,所以參考他們的源碼,寫了一個自己的富文本。
weex-ui源碼:https://github.com/alibaba/weex-ui
首先分析一波:
1,要實現(xiàn)不同顏色的字體,這里肯定是for循環(huán)創(chuàng)建了多個text,所以這里你看到,其實是多個text標簽,而非一個text標簽實現(xiàn)的。
2,既然是多個text標簽,如何截取給定的字符串,找到換行的具體位置是整個RichText實現(xiàn)的關鍵所在。(當然這里主要是算法上的一些操作,因人而異,最終的目的就是找出換行的位置)。
其實看到這里,相信每個人心里都有一些思考,那么這里先上效果分析圖:

1559012964700.png
上面每一個紅色矩形都表示一個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:{
//數(shù)據(jù)源,我們這里傳進來就是一個數(shù)組包,數(shù)組中的每一個對象包含以下三個字段
//color 這里的color跟我們在Style里面寫的color是一樣的,例如白色,這里就寫color:'#ffffff'
//size 這里的size跟style里面的font-size有區(qū)別,這里的類型為數(shù)字,例如style里面的font-size:28px;那么這里就寫 color:28
//content 這就是我們需要展示的內(nèi)容,類型為String
datas:Array,
//設置RichText的寬度,默認為690,注意這里的類型是數(shù)字,而不是我們在style中寫的那樣
RichTextwidth:{
type:Number,
default:690,
},
},
mounted() {
//這個地方對數(shù)據(jù)做重新封裝之后再添加到data里面去
let data = this.datas;
//對data做判空處理,不為空時這里為true
if (data){
if (data.length){
//這個數(shù)組是我們對傳入數(shù)組處理之后得到的新的數(shù)據(jù)源
let tempContent = [];
//定義當前行可用的展示空間,第一行的時候,默認就是設置的行寬
let endLenght = this.RichTextwidth;
//對傳入的數(shù)據(jù)源做for循環(huán)操作得到每一個具體的元素
for (let i = 0; i < data.length; i++) {
//獲取到當前索引下的content
let tempStr = data[i].content;
//這里跟Java有細微區(qū)別,通過split轉換成字符數(shù)組
let char = tempStr.split("");
//strLength表示當前字符串的內(nèi)容長度,默認是0
let strLength = 0;
//ratio 表示當前字符的寬度占比
let ratio = 0;
/**
* 需要說明兩點點,一:這里的ratio并不是一個準確的值,這個是根據(jù)50px下 750px寬度內(nèi) 中文 英文大小寫 數(shù)字最大個數(shù) 計算出來的一個比率
* 經(jīng)測試,發(fā)現(xiàn)這樣計算出來的比率 在font-size為40px的情況下展示效果是最好的,所以我的效果圖,也是按照size為40給出的。
* 二:這里由于無法判斷符號是中文還是英文的,所以在此未對符號做兼容,如果你有什么好的方法,不妨在下方評論留言,謝謝
* */
for (let j = 0; j < char.length; j++) {
//常規(guī)操作
if (0 <= char[j] && char[j] <= 9){
//數(shù)字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 {
//中文和符號暫不做區(qū)分,寬度系數(shù)就為1,
ratio = 1;
}
//總長度做求和操作
strLength += data[i].size * ratio
}
// 對比當前字符串長度是否小于當前可展示空間
if (strLength >= 0 && strLength <= endLenght){
//改變結束位置的長度,用來動態(tài)對比下一個元素是否需要直接存儲
endLenght = endLenght - strLength;
//長度小于當前可用空間長度,直接存儲到數(shù)組中
tempContent.push(data[i]);
} else {
//截取當前字符串長度,按照當前可用空間做截取
let arr = this.subStr2Length(data[i], endLenght);
//當前行可用空間能展示的當前字符串的最大索引值
let index = arr[0];
//當前可用空間展示的字符串的真實長度
let lenght = arr[1];
/**
* 計算剩余長度的字符串還可以占幾行
* 需要注意一點,Vue里面的“/”除操作跟Java有區(qū)別,這里得到的值是浮點數(shù),也就是小數(shù),舉例,12/10=1.2,跟Java有區(qū)別
* */
let rows = (strLength - lenght) / this.RichTextwidth;
//將剛剛計算出的需要添加的元素添加到數(shù)組中
let item1 = {
//截取真實長度填充到當前行末尾的可用空間中
content:data[i].content.substring(0,index),
size:data[i].size,
color:data[i].color,
};
tempContent.push(item1);
//判斷剩下的內(nèi)容是否新起一行可以展示完
if (rows <= 1){
//如果可以展示完,直接添加
let item2 = {
/**
* 需要說明一點,這里的subString跟Java中有細微區(qū)別,Vue中subString不會出現(xiàn)索引越界的問題,超出的話,截取至末尾
*/
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;
//循環(huán)操作剩余的長度
for (let j = 0; j < rows; j++) {
//獲取數(shù)據(jù)源中剩余的可用于展示的內(nèi)容
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)裁剪當前字符串,返回一個數(shù)組,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 {
//中文和符號暫不做區(qū)分,寬度系數(shù)就為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控件的代碼,算法上可以根據(jù)自己的需求做相應的修改,接下來提供一個使用的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>
以上代碼如果有任何問題,都可以直接在下方留言,同時感謝大家指出的問題。