UNPKG

draw-page-structure

Version:

a nice tool to make skeleton screen

298 lines (258 loc) 9.35 kB
module.exports = function evalDOM() { const ELEMENTS = ['audio', 'button', 'canvas', 'code', 'img', 'input', 'pre', 'svg', 'textarea', 'video', 'xmp']; const blocks = []; const win_w = window.innerWidth; const win_h = window.innerHeight; let agrs = arguments; if (!agrs.length) agrs = {length: 1, 0: {}}; let agrs0 = agrs[0]; if (agrs.length !== 1 || getArgtype(agrs0) !== 'object') { agrs = parseAgrs([...agrs]); } const classProps = { position: 'fixed', zIndex: 999, background: agrs.background } if (agrs.animation) { classProps.animation = agrs.animation; } createCommonClass(classProps); function drawBlock({width, height, top, left, zIndex = 999, background = agrs.background, radius, subClas} = {}) { const styles = ['height:' + height + '%']; if (!subClas) { styles.push('top:' + top + '%', 'left:' + left + '%', 'width:' + width + '%'); } if (classProps.zIndex !== zIndex) { styles.push('z-index:' + zIndex); } if (classProps.background !== background) { styles.push('background:' + background); } radius && radius != '0px' && styles.push('border-radius:' + radius); blocks.push(`<div class="_${subClas ? ' __' : ''}" style="${styles.join(';')}"></div>`); } function wPercent(x) { return parseFloat(x / win_w * 100).toFixed(3); } function hPercent(x) { return parseFloat(x / win_h * 100).toFixed(3); } function noop() {} function getArgtype(arg) { return Object.prototype.toString.call(arg).toLowerCase().match(/\s(\w+)/)[1]; } function getStyle(node, attr) { return (node.nodeType === 1 ? getComputedStyle(node)[attr] : '') || ''; } function getRootNode(el) { if (!el) return el; return typeof el === 'object' ? el: (getArgtype(el) === 'string' ? document.querySelector(el): null); } function includeElement(elements, node) { return ~elements.indexOf((node.tagName || '').toLowerCase()); } function isHideStyle(node) { return getStyle(node, 'display') === 'none' || getStyle(node, 'visibility') === 'hidden' || getStyle(node, 'opacity') == 0 || node.hidden; } function isCustomCardBlock(node) { const bgStyle = getStyle(node, 'background'); const bgColorReg = /rgba\([\s\S]+?0\)/ig; const bdReg = /(0px)|(none)/; const hasBgColor = !bgColorReg.test(bgStyle) || ~bgStyle.indexOf('gradient'); const hasNoBorder = ['top', 'left', 'right', 'bottom'].some(item => { return bdReg.test(getStyle(node, 'border-' + item)); }); const {w, h} = getRect(node); const customCardBlock = !!(hasBgColor && (!hasNoBorder || getStyle(node, 'box-shadow') != 'none') && w > 0 && h > 0 && w < 0.95*win_w && h < 0.3*win_h); return customCardBlock; } function calcTextWidth(text, {fontSize, fontWeight} = {}) { if (!text) return 0; const div = document.createElement('div'); div.innerHTML = text; div.style.cssText = [ 'position:absolute', 'left:-99999px', `height:${fontSize}`, `font-size:${fontSize}`, `font-weight:${fontWeight}`, 'opacity:0' ].join(';'); document.body.appendChild(div); const w = getStyle(div, 'width'); const h = getStyle(div, 'height'); document.body.removeChild(div); return { w: parseInt(w), h: parseInt(h) }; } function getRect(node) { if (!node) return {}; const { top: t, left: l, width: w, height: h } = node.getBoundingClientRect(); return {t, l, w, h}; } function getPadding(node) { return { paddingTop: parseInt(getStyle(node, 'paddingTop')), paddingLeft: parseInt(getStyle(node, 'paddingLeft')), paddingBottom: parseInt(getStyle(node, 'paddingBottom')), paddingRight: parseInt(getStyle(node, 'paddingRight')) } } function createCommonClass(props) { const inlineStyle = ['<style>._{']; for(let prop in props) { inlineStyle.push(`${prop === 'zIndex'? 'z-index': prop}:${props[prop]};`); } inlineStyle.push('}.__{top:0%;left:0%;width:100%;}</style>'); blocks.push(inlineStyle.join('')); } function parseAgrs(agrs = []) { let params = {}; agrs.forEach(agr => { const sep = agr.indexOf(':'); const [appName, name, type] = agr.slice(0, sep).split('-'); const val = agr.slice(sep + 1); params[name] = type === 'function'? eval('(' + val + ')'): type === 'object'? JSON.parse(val): val; }); return params; } function DrawPageframe(opts) { this.rootNode = getRootNode(opts.rootNode) || document.body; this.offsetTop = opts.offsetTop || 0; this.includeElement = opts.includeElement; this.init = opts.init; this.originStyle = {}; return this instanceof DrawPageframe ? this : new DrawPageframe(opts); } DrawPageframe.prototype = { resetDOM: function() { this.init && this.init(); this.originStyle = { scrollTop: window.scrollY, bodyOverflow: getStyle(document.body, 'overflow') }; window.scrollTo(0, this.offsetTop); document.body.style.cssText += 'overflow:hidden!important;'; drawBlock({ height: 100, zIndex: 990, background: '#fff', subClas: true }); this.withHeader(); }, inHeader: function(node) { if (agrs.header) { const height = parseInt(agrs.header.height); if (height) { const {t, l, w, h} = getRect(node); return t <= height; } } }, withHeader: function() { if (agrs.header) { const {height, background} = agrs.header; const hHeight = parseInt(height); const hBackground = background || agrs.background; if (hHeight) { drawBlock({ height: hPercent(hHeight), zIndex: 999, background: hBackground, subClas: true }); } } }, showBlocks: function() { if (blocks.length) { const { body } = document; const blocksHTML = blocks.join(''); const div = document.createElement('div'); div.innerHTML = blocksHTML; body.appendChild(div); window.scrollTo(0, this.originStyle.scrollTop); document.body.style.overflow = this.originStyle.bodyOverflow; return blocksHTML; } }, startDraw: function() { const $this = this; this.resetDOM(); const nodes = this.rootNode.childNodes; function deepFindNode(nodes) { if (nodes.length) { for(let i = 0; i < nodes.length; i++) { let node = nodes[i]; if (isHideStyle(node) || (getArgtype($this.includeElement) === 'function' && $this.includeElement(node, drawBlock) == false)) continue; let childNodes = node.childNodes; let hasChildText = false; let background = getStyle(node, 'backgroundImage'); let backgroundHasurl = background.match(/url\(.+?\)/); backgroundHasurl = backgroundHasurl && backgroundHasurl.length; for(let j = 0; j < childNodes.length; j++) { if (childNodes[j].nodeType === 3 && childNodes[j].textContent.trim().length) { hasChildText = true; break; } } if ((includeElement(ELEMENTS, node) || backgroundHasurl || (node.nodeType === 3 && node.textContent.trim().length) || hasChildText || isCustomCardBlock(node)) && !$this.inHeader(node)) { const {t, l, w, h} = getRect(node); if (w > 0 && h > 0 && l >= 0 && l < win_w && win_h - t >= 20 && t >= 0) { const { paddingTop, paddingLeft, paddingBottom, paddingRight } = getPadding(node); drawBlock({ width: wPercent(w - paddingLeft - paddingRight), height: hPercent(h - paddingTop - paddingBottom), top: hPercent(t + paddingTop), left: wPercent(l + paddingLeft), radius: getStyle(node, 'border-radius') }); } } else if (childNodes && childNodes.length) { if (!hasChildText) { deepFindNode(childNodes); } } } } } deepFindNode(nodes); return this.showBlocks(); } } return new Promise((resolve, reject) => { setTimeout(() => { try { const html = new DrawPageframe({ init: agrs.init, rootNode: agrs.rootNode, includeElement: agrs.includeElement }).startDraw(); resolve(html); } catch (e) { reject(e); } }, 1000); }); }