UNPKG

wangbd-myui

Version:

MY前端基础框架核心组件库

273 lines (237 loc) 7.05 kB
import Vue from 'vue' import {setStyle} from 'element-ui/lib/utils/dom' // We only handle element & text node. const ELEMENT_NODE = 1; const TEXT_NODE = 3; const COMMENT_NODE = 8; const wrapperStyle = { padding: 0, margin: 0, display: 'inline', lineHeight: 'inherit' }; /** * 像素转换成数字类型 * @param {string} value * @return {number} */ function pxToNumber(value) { if (!value) return 0; const match = value.match(/^\d*(\.\d*)?/); return match ? Number(match[0]) : 0; } /** * 样式对象转换成字符串形式 * @param {Object} style * @return {string} */ function styleToString(style) { const styleNames = Array.prototype.slice.apply(style); return styleNames.map(name => `${name}: ${style.getPropertyValue(name)};`).join(''); } /** * 创建临时容器 * @param {Object} style * @return {HTMLElement} */ function createEllipsisContainer(style) { const el = document.createElement('div') const originCSS = styleToString(style) el.setAttribute('style', originCSS) setStyle(el, { position: 'fixed', left: '0', height: 'auto', minHeight: 'auto', maxHeight: 'auto', top: '-999999px', zIndex: '-1000', textOverflow: 'clip', whiteSpace: 'normal', webkitLineClamp: 'none' }) document.body.appendChild(el) return el } /** * * 删除临时容器 * @param {HTMLElement} el */ function removeEllipsisContainer(el) { el.parentNode.removeChild(el) } /** * 实例化用于计算的临时DOM * @param el * @param content * @param fixedContent * @return {*} */ function instanceVue(el, content, fixedContent) { const Container = Vue.extend({ functional: true, render() { return ( <div style={wrapperStyle}> <span style={wrapperStyle}>{content}</span> <span style={wrapperStyle}>{fixedContent}</span> </div> ) } }) const vm = new Container() vm.$mount() el.appendChild(vm.$el) return vm } /** * 销毁临时DOM * @param vm */ function destroyVue(vm) { vm.$destroy() vm.$el.parentNode.removeChild(vm.$el) } /** * 检查是否在范围内,超出范围需要显示省略号 * @param el * @param maxHeight * @return {boolean} */ function inRange(el, maxHeight) { return el.offsetHeight < maxHeight } /** * createEllipsisContentHolder * @param el * @param ellipsisTextNode * @return {HTMLSpanElement} */ function createEllipsisContentHolder(el, ellipsisTextNode) { el.innerHTML = '' // Create origin content holder const ellipsisContentHolder = document.createElement('span'); el.appendChild(ellipsisContentHolder); ellipsisContentHolder.appendChild(ellipsisTextNode); return ellipsisContentHolder } /** * Append before fixed nodes * @param ellipsisContentHolder * @param node * @param ellipsisTextNode */ function appendChildNode(ellipsisContentHolder, node, ellipsisTextNode) { ellipsisContentHolder.insertBefore(node, ellipsisTextNode); } /** * 计算省略文本 * @param {HTMLElement} originEle * @param {Number} rows * @param {VNode[]} content * @param {VNode[]} fixedContent * @param {string} ellipsisStr * @return {Object} */ export default function (originEle, rows, content, fixedContent, ellipsisStr = '...') { // 获取原始样式 const originStyle = window.getComputedStyle(originEle) const ellipsisContainer = createEllipsisContainer(originStyle) const lineHeight = pxToNumber(originStyle.lineHeight) || 16 const maxHeight = lineHeight * (rows + 1) + pxToNumber(originStyle.paddingTop) + pxToNumber(originStyle.paddingBottom) if (maxHeight === 0) { removeEllipsisContainer(ellipsisContainer) return { content, ellipsis: false } } const vm = instanceVue(ellipsisContainer, content, fixedContent) if (inRange(ellipsisContainer, maxHeight)) { destroyVue(vm) removeEllipsisContainer(ellipsisContainer) return { content, ellipsis: false } } const cloneNodes = Array.prototype.slice.apply(ellipsisContainer.childNodes[0].childNodes[0].cloneNode(true).childNodes) const childNodes = cloneNodes.filter(nodeType => nodeType !== COMMENT_NODE) const fixedNodes = Array.prototype.slice.apply( ellipsisContainer.childNodes[0].childNodes[1].cloneNode(true).childNodes ) destroyVue(vm) // ========================= Find match ellipsis content ========================= const ellipsisChildren = [] const ellipsisTextNode = document.createTextNode(ellipsisStr) const ellipsisContentHolder = createEllipsisContentHolder(ellipsisContainer, ellipsisTextNode) fixedNodes.forEach(childNode => { ellipsisContainer.appendChild(childNode); }); function measureText(textNode, fullText, startLoc = 0, endLoc = fullText.length, lastSuccessLoc = 0) { const midLoc = Math.floor((startLoc + endLoc) / 2) textNode.textContent = fullText.slice(0, midLoc) if (startLoc >= endLoc - 1) { for (let step = endLoc; step >= startLoc; step -= 1) { const currentStepText = fullText.slice(0, step) textNode.textContent = currentStepText if (inRange(ellipsisContainer, maxHeight)) { return step === fullText.length ? { finished: false, vnode: fullText } : { finished: true, vnode: currentStepText } } } } if (inRange(ellipsisContainer, maxHeight)) { return measureText(textNode, fullText, midLoc, endLoc, midLoc); } return measureText(textNode, fullText, startLoc, midLoc, lastSuccessLoc) } function measureNode(childNode, index) { const type = childNode.nodeType if (type === ELEMENT_NODE) { appendChildNode(ellipsisContentHolder, childNode, ellipsisTextNode) if (inRange(ellipsisContainer, maxHeight)) { return { finished: false, vnode: content[index] } } // Clean up if can not pull in ellipsisContentHolder.removeChild(childNode) return { finished: true, vnode: null } } if (type === TEXT_NODE) { const fullText = childNode.textContent || ''; const textNode = document.createTextNode(fullText); appendChildNode(ellipsisContentHolder, textNode, ellipsisTextNode) return measureText(textNode, fullText); } return { finished: false, vnode: null } } childNodes.some((childNode, index) => { const {finished, vnode} = measureNode(childNode, index); if (vnode) { ellipsisChildren.push(vnode); } return finished; }); removeEllipsisContainer(ellipsisContainer) return { content: ellipsisChildren, ellipsis: true } }