美文网首页
weex实现RichText自动换行效果

weex实现RichText自动换行效果

作者: 码个蛋 | 来源:发表于2019-05-28 13:44 被阅读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>
    

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

    相关文章

      网友评论

          本文标题:weex实现RichText自动换行效果

          本文链接:https://www.haomeiwen.com/subject/qtzrtctx.html