为什么需要做讷?

  • 自适应实现:根据不同的设备屏幕大小来实现来自动化的调整页面的尺寸和大小,实现自动化的适配

  • 响应式实现:随着屏幕的实时变动来实现自动的调整来实现自适应功能吧

基础配置

  • 对 html 进行动刀子,确保布局视口、视觉视口、理想视口一样的大小,后续不出现大小问题

<!--
  initial-scale=1.0  一般都是 1.0
  user-scalable=no  不可以缩放
  minimum-scale=1.0 最小缩放比例
  maximum-scale=1.0 最大缩放比例
-->
<meta
  name="viewport"
  content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />

常见的适配方案

  • 百分比布局

    • 书写起来十分的简单,原生支持的讷,相对于容器来进行计算实现吧

    • 但是不同容器的百分比设置会导致后期项目迭代开发的混乱吧,导致后续不可维护的特性吧

  • rem 适配方案

    • 相对于根元素的字体来进行统一的管理控制实现讷,兼容性方面还是可以的讷,可以配合 js 进行动态的适配实现讷

    • 但是需要额外的 js 计算,存在字体大小的继承关系存在吧,小数的像素可能导致边框的显示问题出现

  • vw 适配方案

    • 不需要进行 js 计算,语义化更好,直接相对于视口宽度来进行计算的讷,可以和 rem 方案进行混合使用吧

    • 有些设备上需要进行兼容性的考量和处理吧

  • 弹性盒布局

    • 通过 flex 弹性盒方案来实现布局吧

视口讲解

  • 布局视口(layout Viewport)

    • PC 端的网页在移动端会相对于 980px 布局的这个视口来进行布局讷,一般这是大于视觉视口的讷

layout viewport

  • 视觉视口(Visual Viewport)

    • 显示在可见区域的这个视口,也是设备的宽度

    • 默认情况下,手机端上的浏览器会按照 980px 的布局来进行渲染内容,一般大于视觉视口的讷,那么右侧就有超出的部分,就无法显示出来吧,手机端的浏览器会默认的对页面进行缩放来显示到用户的可见视口内吧

visual viewport

  • 理想视口(Ideal Viewport)

    • 如果所有的网页按照 980px 的宽度在移动端进行布局,那么最终的页面儿都会被缩放的显示出来吧,不利于进行移动端的开发,希望得到的是设置 100px 就实现显示 100px 的逻辑像素吧

    • 可以对 layout viewport 进行宽度和缩放的位置,来满足在一个移动端视口中的布局吧

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

具体方案实现

rem 实现方案

js 动态计算设置

/**
 * 防抖触发一下吧
 * @param {*} fn 
 * @param {*} delay 
 * @returns 
 */
function debounce(fn, delay = 500) {
    let timer = null;
    return function (...args) {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            fn.apply(this, args);
        }, delay);
    }
}

function setRemUnit(devide = 10) {
    // 获取得到根元素
    const htmlEl = document.documentElement || document.body;
    // 获取得到根元素的具体宽度吧
    const htmlWidth = htmlEl.clientWidth;
    // 实现将宽度等分为十份
    const htmlFontSize = htmlWidth / devide;
    // 将值设置给我们的html 的 font-size 属性
    // 此时设置的信息为:5rem,那么就是占用屏幕的50%宽度吧
    htmlEl.style.fontSize = `${htmlFontSize}px`;
}

const debounceSetRemUnit = debounce(setRemUnit);

window.addEventListener("resize", debounceSetRemUnit);

window.addEventListener("pageshow", function (e) {
    // 页面显示时,重新设置rem单位
    if (e.persisted) {
        debounceSetRemUnit();
    }
})

lib-flexiable 方案实现

/**
 * 响应式布局
 * window 窗口对象
 * document 文档对象
 */
(function flexible(window, document) {
    // 获取得到根元素
    const htmlEl = document.documentElement || document.body;
    // 获取得到像素比
    const dpr = window.devicePixelRatio || 1;

    // 设置 body 的 font-szie 信息吧
    function setBodyFontSize() {
        if (document.body) {
            // 为什么是 12 讷?
            // 这样设置达到的鲜果是就是 12px 的字体大小
            document.body.style.fontSize = 12 * dpr + "px";
        } else {
            document.addEventListener("DOMContentLoaded", setBodyFontSize);
        }
    }
    setBodyFontSize();

    // 配置一下rem单位的转换
    function setRemUnit(devide = 10) {
        // 获取得到根元素
        const htmlEl = document.documentElement || document.body;
        // 获取得到根元素的具体宽度吧
        const htmlWidth = htmlEl.clientWidth;
        // 实现将宽度等分为十份
        const htmlFontSize = htmlWidth / devide;
        // 将值设置给我们的html 的 font-size 属性
        // 此时设置的信息为:5rem,那么就是占用屏幕的50%宽度吧
        htmlEl.style.fontSize = `${htmlFontSize}px`;
    }
    setRemUnit();

    window.addEventListener("resize", setRemUnit);
    window.addEventListener("pageshow", function (e) {
        // 页面显示时,重新设置rem单位
        if (e.persisted) {
            setRemUnit();
        }
    })

    // 检测 0.5 的支持性
    if (dpr >= 2) {
        var fakeBody = document.createElement("body");
        var testElement = document.createElement("div");
        testElement.style.border = ".5px solid transparent";
        fakeBody.appendChild(testElement);
        docEl.appendChild(fakeBody);
        if (testElement.offsetHeight === 1) {
            docEl.classList.add("hairlines");
        }
        docEl.removeChild(fakeBody);
    }
})(window, document)

vw 方案

  • 100vw 相对于整个视口的宽度 innerWidth 来实现的讷,1vw 就是屏幕 1% 的宽度吧,将 px 转换为 vw 即可完成适配讷

  • vw 相较于 rem 的优势

    • 不需要去计算 html 的 font-size 大小,也不需要去给 html 设置 font-size;

    • 不会因为设置 html 的 font-size 大小,而必须再给 body 设置一个 font-size 防止继承;

    • 因为不依赖 font-size 的尺寸,所以不用担心某些原因的 html 的 font-size 尺寸被篡改,导致页面尺寸混乱;

    • vw 更加语义化,1vw 相当于 1/100 viewport 的大小;

    • rem 事实上作为一种过渡的方案,它利用的也是 vw 的思想

CSS 单位转化方案

手动转换

  • less/sass函数实现

@vwUnit: 3.75;
.pxToVw(@px) {
  result: (@px / @vwUnit) * 1vw;
}

.pxToRem(@px) {
  result: 1rem * (@px / 37.5);
}

.box {
  width: .pxToVw(100) [result];
  height: .pxToVw(100) [result];
  font-size: .pxToRem(14) [result];
}

自动化工具

  • postcss-px-to-viewport 来实现

  • postcss-pxtorem 来实现吧

https://github.com/amfe/lib-flexible