export interface IgnoreOption { el: string; height?: string; width?: string; scale?: number; fontSize?: number; } export interface AutofitOption { el?: string; dw?: number; dh?: number; resize?: boolean; ignore?: (IgnoreOption | string)[]; transition?: number; delay?: number; limit?: number; cssMode?: "scale" | "zoom"; allowScroll?: boolean; } declare interface Autofit { /** * 参数列表 * 对象: * * @param {AutofitOption|String|undefined} options * @param {boolean|undefined} isShowInitTip * - 传入对象,对象中的属性如下: * - el(可选):渲染的元素,默认是 "body" * - dw(可选):设计稿的宽度,默认是 1920 * - dh(可选):设计稿的高度,默认是 1080 * - resize(可选):是否监听resize事件,默认是 true * - ignore(可选):忽略缩放的元素(该元素将反向缩放),参数见readme.md * - transition(可选):过渡时间,默认是 0 * - delay(可选):延迟,默认是 0 * - limit(可选):缩放限制,默认是 0.1 * - cssMode(可选):缩放模式,默认是 scale,可选值有 scale 和 zoom, zoom 模式可能对事件偏移有利 */ init(options?: AutofitOption | string, isShowInitTip?: boolean): void; /** * @param {String} id * 关闭autofit.js造成的影响 * */ off(id?: string): void; /** * 检查autofit.js是否正在运行 */ isAutofitRunning: boolean; /** * @param {string} el - 待处理的元素选择器 * @param {boolean} isKeepRatio - 是否保持纵横比(可选,默认为true,false时将充满父元素) * @param {number|undefined} level - 缩放等级,用于手动调整缩放程度(可选,默认为 1) */ elRectification: typeof elRectification; /** * 当前缩放比例 */ scale: number } // type Ignore = Array<{ height: number, width: number, fontSize: number, scale: number, el: HTMLElement, dom: HTMLElement }>; let currRenderDom: string | HTMLElement = null!; let currelRectification = ""; let currelRectificationLevel: string | number = ""; let currelRectificationIsKeepRatio: string | boolean = ""; let resizeListener: EventListenerOrEventListenerObject = null!; let timer: number = null!; let currScale: string | number = 1; let isElRectification = false; const autofit: Autofit = { isAutofitRunning: false, init(options = {}, isShowInitTip = true) { if (isShowInitTip) { console.log(`autofit.js is running`); } const { dw = 1920, dh = 1080, el = typeof options === "string" ? options : "body", resize = true, ignore = [], transition = "none", delay = 0, limit = 0.1, cssMode = "scale", allowScroll = false, } = options as AutofitOption; currRenderDom = el; const dom = document.querySelector(el); if (!dom) { console.error(`autofit: '${el}' is not exist`); return; } const style = document.createElement("style"); const ignoreStyle = document.createElement("style"); style.lang = "text/css"; ignoreStyle.lang = "text/css"; style.id = "autofit-style"; ignoreStyle.id = "ignoreStyle"; !allowScroll && (style.innerHTML = `body {overflow: hidden;}`); const bodyEl = document.querySelector("body")!; bodyEl.appendChild(style); bodyEl.appendChild(ignoreStyle); dom.style.height = `${dh}px`; dom.style.width = `${dw}px`; dom.style.transformOrigin = `0 0`; !allowScroll && (dom.style.overflow = "hidden"); keepFit(dw, dh, dom, ignore, limit, cssMode); resizeListener = () => { clearTimeout(timer); if (delay != 0) timer = setTimeout(() => { keepFit(dw, dh, dom, ignore, limit, cssMode); isElRectification && elRectification(currelRectification, currelRectificationIsKeepRatio, currelRectificationLevel); }, delay) as unknown as number; else { keepFit(dw, dh, dom, ignore, limit, cssMode); isElRectification && elRectification(currelRectification, currelRectificationIsKeepRatio, currelRectificationLevel); } }; resize && window.addEventListener("resize", resizeListener); this.isAutofitRunning = true; setTimeout(() => { dom.style.transition = `${transition}s`; }); }, off(el = "body") { try { window.removeEventListener("resize", resizeListener); const autofitStyle = document.querySelector("#autofit-style"); autofitStyle && autofitStyle.remove(); const ignoreStyleDOM = document.querySelector("#ignoreStyle"); ignoreStyleDOM && ignoreStyleDOM.remove(); const temp = document.querySelector(currRenderDom ? currRenderDom as string : el); temp && (temp.style.cssText = "") isElRectification && offelRectification(); } catch (error) { console.error(`autofit: Failed to remove normally`, error); } this.isAutofitRunning = false; console.log(`autofit.js is off`); }, elRectification: null!, scale: currScale }; function elRectification(el: string, isKeepRatio: string | boolean = true, level: string | number = 1) { if (!autofit.isAutofitRunning) { console.error("autofit.js:(elRectification): autofit has not been initialized yet"); return; } offelRectification(); !el && console.error(`autofit.js:elRectification bad selector: ${el}`); currelRectification = el; currelRectificationLevel = level; currelRectificationIsKeepRatio = isKeepRatio; const currEl = Array.from(document.querySelectorAll(el)); if (currEl.length == 0) { console.error(`autofit.js:elRectification found no element by selector: "${el}"`); return; } for (const item of currEl) { const rectification = currScale == 1 ? 1 : Number(currScale) * Number(level); if (!isElRectification) { item.originalWidth = item.clientWidth; item.originalHeight = item.clientHeight; } if (isKeepRatio) { item.style.width = `${item.originalWidth * rectification}px`; item.style.height = `${item.originalHeight * rectification}px`; } else { item.style.width = `${100 * rectification}%`; item.style.height = `${100 * rectification}%`; } item.style.transform = `translateZ(0) scale(${1 / Number(currScale)})`; item.style.transformOrigin = `0 0`; } isElRectification = true; } function offelRectification() { if (!currelRectification) return; isElRectification = false; for (const item of Array.from(document.querySelectorAll(currelRectification))) { item.style.width = ``; item.style.height = ``; item.style.transform = ``; } } function keepFit( dw: number, dh: number, dom: HTMLElement, ignore: AutofitOption['ignore'], limit: number, cssMode: AutofitOption['cssMode'] = "scale" ) { const clientHeight = document.documentElement.clientHeight; const clientWidth = document.documentElement.clientWidth; currScale = clientWidth / clientHeight < dw / dh ? clientWidth / dw : clientHeight / dh; currScale = Math.abs(1 - currScale) > limit ? currScale : 1; autofit.scale = +currScale; const height = Math.round(clientHeight / Number(currScale)); const width = Math.round(clientWidth / Number(currScale)); dom.style.height = `${height}px`; dom.style.width = `${width}px`; if (cssMode === "zoom") { (dom.style as any).zoom = `${currScale}`; } else { dom.style.transform = `translateZ(0) scale(${currScale})`; } const ignoreStyleDOM = document.querySelector("#ignoreStyle")!; ignoreStyleDOM.innerHTML = ""; for (const temp of ignore!) { const item = temp as IgnoreOption & { dom: string }; let itemEl = item.el || item.dom; typeof item == "string" && (itemEl = item); if (!itemEl || (typeof itemEl === "object" && !Object.keys(itemEl).length)) { console.error(`autofit: found invalid or empty selector/object: ${itemEl}`); continue; } const realScale: number = item.scale ? item.scale : 1 / Number(currScale); const realFontSize = realScale != currScale && item.fontSize; const realWidth = realScale != currScale && item.width; const realHeight = realScale != currScale && item.height; ignoreStyleDOM.innerHTML += `\n${itemEl} { transform: scale(${realScale})!important; transform-origin: 0 0; ${realWidth ? `width: ${realWidth}!important;` : ''} ${realHeight ? `height: ${realHeight}!important;` : ''} }`; if (realFontSize) { ignoreStyleDOM.innerHTML += `\n${itemEl} div ,${itemEl} span,${itemEl} a,${itemEl} * { font-size: ${realFontSize}px; }`; } } } autofit.elRectification = elRectification; export { autofit as default, elRectification };