weex实现RichText自动换行效果

2019-05-28  本文已影响0人  码个蛋

weex实现富文本RichText效果

话不错说,先上效果图:


1559011516332.png

由于最近公司需求,仿微信朋友圈,评论回复@的人需要实现变蓝色效果,研究了一番,发现weex-ui里面的富文本控件满足不了要求,weex官网提供的RichText经检测,只能在0.20.0.0的版本才能正常使用,所以参考他们的源码,写了一个自己的富文本。

weex-ui源码:https://github.com/alibaba/weex-ui

首先分析一波:

1,要实现不同颜色的字体,这里肯定是for循环创建了多个text,所以这里你看到,其实是多个text标签,而非一个text标签实现的。

2,既然是多个text标签,如何截取给定的字符串,找到换行的具体位置是整个RichText实现的关键所在。(当然这里主要是算法上的一些操作,因人而异,最终的目的就是找出换行的位置)。

其实看到这里,相信每个人心里都有一些思考,那么这里先上效果分析图:

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:{
          //数据源,我们这里传进来就是一个数组包,数组中的每一个对象包含以下三个字段
          //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>

以上代码如果有任何问题,都可以直接在下方留言,同时感谢大家指出的问题。

上一篇下一篇

猜你喜欢

热点阅读