UNPKG

flex-text

Version:

Mastering font-size like flexbox!

226 lines (168 loc) 5.72 kB
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define([], factory); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } else { // Browser globals (root is window) root.FlexText = factory(); } }(this, function () { 'use strict'; // init measuring element var span = document.createElement('span'); span.style.visibility = 'hidden'; span.style.position = 'fixed'; span.style.top = '99999999px'; document.body.appendChild(span); var BASE_FONT_SIZE = 100; function async(fn) { if (typeof window.requestAnimationFrame === 'function') { requestAnimationFrame(fn); } else { setTimeout(fn, 0); } } function checkElem(elem, name) { if (!elem || elem.nodeType !== 1) { throw new TypeError('expect `' + (name || 'elem') + '` to be an Element, but got ' + typeof elem); } return true; } function getStyle(el, prop) { if (typeof window.getComputedStyle === 'function') { return getComputedStyle(el)[prop]; } prop = prop.replace(/-(\w)/g, function (m, $1) { return $1.toUpperCase(); }); return el.currentStyle[prop]; } function getWidth(el) { var bound = el.getBoundingClientRect(); return bound.width || (bound.right - bound.left); }; function forEach(arr, fn) { if (typeof arr.forEach === 'function') { return arr.forEach(fn); } for (var i = 0, max = arr.length; i < max; i++) { fn(arr[i], i, arr); } } function map(arr, fn) { if (typeof arr.map === 'function') { return arr.map(fn); } var res = []; for (var i = 0, max = arr.length; i < max; i++) { res.push(fn(arr[i], i, arr)); } return res; } function FlexText(options) { options = options || {}; this.items = []; this.setSpacing(options.spacing); if (options.items && options.items.length) { var self = this; forEach(options.items, function (v) { if (v && v.elem) { self.addItem(v.elem, v.flex); } }); } if (options.container) { this.attachTo(options.container); } } FlexText.prototype.attachTo = function attachTo(container) { checkElem(container, 'container'); this.container = container; this.update(); }; FlexText.prototype.setSpacing = function setSpacing(val) { this.spacing = parseFloat(val) || 0; }; FlexText.prototype.addItem = function addItem(elem, flex) { checkElem(elem, 'elem'); flex = flex || 1; if (flex <= 0) { throw new Error('expect flex to be greater than 0, but got ' + flex); } this.items.push({ elem: elem, flex: flex, }); }; FlexText.prototype.removeItem = function removeItem(elem) { checkElem(elem, 'elem'); var items = this.items; for (var i = 0, max = items.length; i < max; i++) { if (elem === items[i].elem) { return items.splice(i, 1); } } }; FlexText.prototype.clear = function clear() { this.items.length = 0; }; FlexText.prototype.alloc = function alloc() { var items = this.items; var spacing = this.spacing; var container = this.container; var totalSpace = getWidth(container); var widths = []; var totalWidth = 0; var whiteSpaceCount = items.length - 1; forEach(items, function (item) { var elem = item.elem; var flex = item.flex; var text = elem.textContent || elem.innerText; var fontSize = BASE_FONT_SIZE * flex; if (!text && whiteSpaceCount > 0) { whiteSpaceCount--; } span.style.fontWeight = getStyle(elem, 'font-weight'); span.style.fontFamily = getStyle(elem, 'font-family'); span.style.fontSize = fontSize + 'px'; span.textContent = span.innerText = text; var width = getWidth(span); widths.push(width); totalWidth += width; }); totalSpace -= parseFloat(spacing) * whiteSpaceCount; return map(widths, function (w, i) { var item = items[i]; var fontSize = BASE_FONT_SIZE * item.flex; var targetWidth = (w / totalWidth) * totalSpace; return { elem: item.elem, fontSize: Math.max(0, fontSize / (w / targetWidth)), }; }); }; FlexText.prototype.render = function render() { var spacing = this.spacing; var result = this.alloc(); forEach(result, function (item, idx) { var elem = item.elem; var fontSize = item.fontSize; elem.style.fontSize = Math.floor(fontSize) + 'px'; if (idx > 0) { elem.style.marginLeft = spacing + 'px'; } }); }; FlexText.prototype.update = function update() { var self = this; async(function () { self.render(); }); }; return FlexText; }));