基于clamp.js封装vue指令,处理多行文本的溢出

Posted jlfw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于clamp.js封装vue指令,处理多行文本的溢出相关的知识,希望对你有一定的参考价值。

最近做项目时,遇到了一个需求:要求div里文本在两行显示,div的宽度是固定的,如果溢出的话就显示省略号。单行文本的溢出问题,我们都很熟悉,只要添加以下css属性就ok:

  overflow: hidden;
  white-space: nowrap; //段落中文本不换行
  text-overflow: ellipsis;

但是多行文本的溢出怎么处理呢?

  • 查了资料之后发现还是有办法的

    在WebKit浏览器或移动端(绝大部分是WebKit内核的浏览器)的页面实现比较简单,可以直接使用WebKit的CSS扩展属性(WebKit是私有属性)-webkit-line-clamp ;注意:这是一个 不规范的属性(unsupported WebKit property),它没有出现在 CSS 规范草案中。-webkit-line-clamp用来限制在一个块元素显示的文本的行数。 为了实现该效果,它需要组合其他的WebKit属性。
  • 我们用一下代码即可实现:
    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  • 但是这样处理会遇到兼容性问题,在Firefox浏览器上就不起作用。
  • 为了解决兼容性问题,有一个clamp.js[https://www.npmjs.com/package...]很好的解决这个问题。
  • 为了更好的跟Vue相结合,今天我们就封装一个v-clamp的指令,来方便的解决这个问题。

    // 注册一个全局自定义指令 `v-clamp`
    Vue.directive(‘clamp‘, {
      // 当被绑定的元素插入到 DOM 中时……
      update: function (el, binding) {
          function clamp(element, options) {
              options = options || {};
          
              var self = this,
                win = window,
                opt = {
                  clamp: options.clamp || 2,
                  useNativeClamp: typeof(options.useNativeClamp) != ‘undefined‘ ? options.useNativeClamp : true,
                  splitOnChars: options.splitOnChars || [‘.‘, ‘-‘, ‘–‘, ‘—‘, ‘ ‘], //Split on sentences (periods), hypens, en-dashes, em-dashes, and words (spaces).
                  animate: options.animate || false,
                  truncationChar: options.truncationChar || ‘…‘,
                  truncationhtml: options.truncationHTML
                },
          
                sty = element.style,
                originalText = element.innerHTML,
          
                supportsNativeClamp = typeof(element.style.webkitLineClamp) != ‘undefined‘,
                clampValue = opt.clamp,
                isCSSValue = clampValue.indexOf && (clampValue.indexOf(‘px‘) > -1 || clampValue.indexOf(‘em‘) > -1),
                truncationHTMLContainer;
          
              if (opt.truncationHTML) {
                truncationHTMLContainer = document.createElement(‘span‘);
                truncationHTMLContainer.innerHTML = opt.truncationHTML;
              }
          
          
              // UTILITY FUNCTIONS __________________________________________________________
          
              /**
               * Return the current style for an element.
               * @param {HTMLElement} elem The element to compute.
               * @param {string} prop The style property.
               * @returns {number}
               */
              function computeStyle(elem, prop) {
                if (!win.getComputedStyle) {
                  win.getComputedStyle = function(el, pseudo) {
                    this.el = el;
                    this.getPropertyValue = function(prop) {
                      var re = /(-([a-z]){1})/g;
                      if (prop == ‘float‘) prop = ‘styleFloat‘;
                      if (re.test(prop)) {
                        prop = prop.replace(re, function() {
                          return arguments[2].toUpperCase();
                        });
                      }
                      return el.currentStyle && el.currentStyle[prop] ? el.currentStyle[prop] : null;
                    };
                    return this;
                  };
                }
          
                return win.getComputedStyle(elem, null).getPropertyValue(prop);
              }
          
              /**
               * Returns the maximum number of lines of text that should be rendered based
               * on the current height of the element and the line-height of the text.
               */
              function getMaxLines(height) {
                var availHeight = height || element.clientHeight,
                  lineHeight = getLineHeight(element);
          
                return Math.max(Math.floor(availHeight / lineHeight), 0);
              }
          
              /**
               * Returns the maximum height a given element should have based on the line-
               * height of the text and the given clamp value.
               */
              function getMaxHeight(clmp) {
                var lineHeight = getLineHeight(element);
                return lineHeight * clmp;
              }
          
              /**
               * Returns the line-height of an element as an integer.
               */
              function getLineHeight(elem) {
                var lh = computeStyle(elem, ‘line-height‘);
                if (lh == ‘normal‘) {
                  // Normal line heights vary from browser to browser. The spec recommends
                  // a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
                  lh = parseInt(computeStyle(elem, ‘font-size‘)) * 1.2;
                }
                return parseInt(lh);
              }
          
          
              // MEAT AND POTATOES (MMMM, POTATOES...) ______________________________________
              var splitOnChars = opt.splitOnChars.slice(0),
                splitChar = splitOnChars[0],
                chunks,
                lastChunk;
          
              /**
               * Gets an element‘s last child. That may be another node or a node‘s contents.
               */
              function getLastChild(elem) {
                //Current element has children, need to go deeper and get last child as a text node
                if (elem.lastChild.children && elem.lastChild.children.length > 0) {
                  return getLastChild(Array.prototype.slice.call(elem.children).pop());
                }
                //This is the absolute last child, a text node, but something‘s wrong with it. Remove it and keep trying
                else if (!elem.lastChild || !elem.lastChild.nodeValue || elem.lastChild.nodeValue === ‘‘ || elem.lastChild.nodeValue == opt.truncationChar) {
                  elem.lastChild.parentNode.removeChild(elem.lastChild);
                  return getLastChild(element);
                }
                //This is the last child we want, return it
                else {
                  return elem.lastChild;
                }
              }
          
              /**
               * Removes one character at a time from the text until its width or
               * height is beneath the passed-in max param.
               */
              function truncate(target, maxHeight) {
                if (!maxHeight) {
                  return;
                }
          
                /**
                 * Resets global variables.
                 */
                function reset() {
                  splitOnChars = opt.splitOnChars.slice(0);
                  splitChar = splitOnChars[0];
                  chunks = null;
                  lastChunk = null;
                }
          
                var nodeValue = target.nodeValue.replace(opt.truncationChar, ‘‘);
          
                //Grab the next chunks
                if (!chunks) {
                  //If there are more characters to try, grab the next one
                  if (splitOnChars.length > 0) {
                    splitChar = splitOnChars.shift();
                  }
                  //No characters to chunk by. Go character-by-character
                  else {
                    splitChar = ‘‘;
                  }
          
                  chunks = nodeValue.split(splitChar);
                }
          
                //If there are chunks left to remove, remove the last one and see if
                // the nodeValue fits.
                if (chunks.length > 1) {
                  // console.log(‘chunks‘, chunks);
                  lastChunk = chunks.pop();
                  // console.log(‘lastChunk‘, lastChunk);
                  applyEllipsis(target, chunks.join(splitChar));
                }
                //No more chunks can be removed using this character
                else {
                  chunks = null;
                }
          
                //Insert the custom HTML before the truncation character
                if (truncationHTMLContainer) {
                  target.nodeValue = target.nodeValue.replace(opt.truncationChar, ‘‘);
                  element.innerHTML = target.nodeValue + ‘ ‘ + truncationHTMLContainer.innerHTML + opt.truncationChar;
                }
          
                //Search produced valid chunks
                if (chunks) {
                  //It fits
                  if (element.clientHeight <= maxHeight) {
                    //There‘s still more characters to try splitting on, not quite done yet
                    if (splitOnChars.length >= 0 && splitChar !== ‘‘) {
                      applyEllipsis(target, chunks.join(splitChar) + splitChar + lastChunk);
                      chunks = null;
                    }
                    //Finished!
                    else {
                      return element.innerHTML;
                    }
                  }
                }
                //No valid chunks produced
                else {
                  //No valid chunks even when splitting by letter, time to move
                  //on to the next node
                  if (splitChar === ‘‘) {
                    applyEllipsis(target, ‘‘);
                    target = getLastChild(element);
          
                    reset();
                  }
                }
          
                //If you get here it means still too big, let‘s keep truncating
                if (opt.animate) {
                  setTimeout(function() {
                    truncate(target, maxHeight);
                  }, opt.animate === true ? 10 : opt.animate);
                } else {
                  return truncate(target, maxHeight);
                }
              }
          
              function applyEllipsis(elem, str) {
                elem.nodeValue = str + opt.truncationChar;
              }
          
          
              // CONSTRUCTOR ________________________________________________________________
          
              if (clampValue == ‘auto‘) {
                clampValue = getMaxLines();
              } else if (isCSSValue) {
                clampValue = getMaxLines(parseInt(clampValue));
              }
          
              var clampedText;
              if (supportsNativeClamp && opt.useNativeClamp) {
                sty.overflow = ‘hidden‘;
                sty.textOverflow = ‘ellipsis‘;
                sty.webkitBoxOrient = ‘vertical‘;
                sty.display = ‘-webkit-box‘;
                sty.webkitLineClamp = clampValue;
          
                if (isCSSValue) {
                  sty.height = opt.clamp + ‘px‘;
                }
              } else {
                var height = getMaxHeight(clampValue);
                if (height <= element.clientHeight) {
                  console.log(getLastChild(element));
                  clampedText = truncate(getLastChild(element), height);
                }
              }
          
              return {
                ‘original‘: originalText,
                ‘clamped‘: clampedText
              };
            }
    
           clamp(el,{clamp: 2}) 
    
    
      }
    })
    
    • 其实很简单,仅仅是把clamp.js中的函数搬移了过来。然后就可以像这样来使用:

        <div class="txt" v-clamp>很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板</div>   

以上是关于基于clamp.js封装vue指令,处理多行文本的溢出的主要内容,如果未能解决你的问题,请参考以下文章

Vue常用特性

Vue 常用特性:表单操作自定义指令计算属性侦听器过滤器生命周期

Vue入门基础

element-ui+vue-cli3.0系列问题三:el-tooltip实现多行,单行文本溢出省略号处理

Vue专题-js常用指令

(Vue -05) v-model指令 + 绑定事件 + 深度响应式