首页 > 其他分享 >PostCSS通过px2rem插件和lib-flexible将px单位转换为rem(root em)单位实现大屏适配

PostCSS通过px2rem插件和lib-flexible将px单位转换为rem(root em)单位实现大屏适配

时间:2023-11-14 12:33:14浏览次数:41  
标签:em 插件 dpr plugin 适配 width rem var postcss



目录

  • 文档
  • postcss中使用postcss-plugin-px2rem
  • 安装postcss-plugin-px2rem
  • 示例
  • 默认配置
  • webpack中使用postcss-plugin-px2rem
  • 项目结构
  • 安装依赖
  • 文件内容
  • 大屏适配
  • 参考文章


文档

类似的插件

  • postcss-plugin-px2rem
  • postcss-pxtorem
  • postcss-px2rem

amfe-flexible
lib-flexible

postcss中使用postcss-plugin-px2rem

安装postcss-plugin-px2rem

pnpm install postcss postcss-plugin-px2rem --save-dev

依赖 package.json

{
    "type": "module",
    "dependencies": {
        "postcss": "^8.4.31",
        "postcss-plugin-px2rem": "^0.8.1"
    }
}

示例

main.js

import { writeFileSync, readFileSync } from "fs";
import postcss from "postcss";
import pxtorem from "postcss-plugin-px2rem";

const css = readFileSync("./style.css", "utf8");

// 修改默认配置
const options = {};

const processedCss = postcss(pxtorem(options)).process(css).css;

writeFileSync("./style.rem.css", processedCss);

输入 style.css

h1 {
  margin: 0 0 20px;
  font-size: 32px;
  line-height: 1.2;
  letter-spacing: 1PX; /* ignored */
}

输出 style.rem.css

h1 {
  margin: 0 0 0.2rem;
  font-size: 0.32rem;
  line-height: 1.2;
  letter-spacing: 1PX;
}

默认配置

{
  rootValue: 100,
  unitPrecision: 5,
  propWhiteList: [],
  propBlackList: [],
  exclude:false,
  selectorBlackList: [],
  ignoreIdentifier: false,
  replace: true,
  mediaQuery: false,
  minPixelValue: 0
}

webpack中使用postcss-plugin-px2rem

项目结构

$ tree -I node_modules
.
├── package.json
├── src
│   ├── index.js
│   └── style.css
└── webpack.config.cjs

安装依赖

pnpm install webpack webpack-cli style-loader css-loader postcss-loader mini-css-extract-plugin --save-dev

完整依赖 package.json

{
  "type": "module",

  "scripts": {
    "build": "webpack"
  },

  "devDependencies": {
    "css-loader": "^6.8.1",
    "mini-css-extract-plugin": "^2.7.6",
    "postcss": "^8.4.31",
    "postcss-loader": "^7.3.3",
    "postcss-plugin-px2rem": "^0.8.1",
    "style-loader": "^3.3.3",
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4"
  }
}

文件内容

webpack.config.cjs

"use strict";

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  // 生产环境
  mode: "production",

  // 打包入口
  entry: {
    index: "./src/index.js",
  },

  // 指定输出地址及打包出来的文件名
  output: {
    path: path.join(__dirname, "dist"),
    filename: "index.js",
  },

  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-plugin-px2rem",
                    // 配置参数
                    {
                      rootValue: 100,
                    },
                  ],
                ],
              },
            },
          },
        ],
      },
    ],
  },

  plugins: [
    // 将 CSS 提取到单独的文件中
    new MiniCssExtractPlugin(),
  ],
};

入口文件 index.js

import "./style.css";

样式文件 style.css

h1 {
  margin: 0 0 20px;
  font-size: 32px;
  line-height: 1.2;
  letter-spacing: 1PX; /* ignored */
}

运行打包

$ npm run build

输出结果

h1 {
  margin: 0 0 0.2rem;
  font-size: 0.32rem;
  line-height: 1.2;
  letter-spacing: 1PX; /* ignored */
}

大屏适配

原理

  • 打包阶段:根据设计稿的尺寸编写css代码(px为单位),将css代码转为rem为单位的数据
  • 浏览器运行阶段:根据根节点root的字体大小,将rem为单位的尺寸还原为px单位

引入文件:lib-flexible.js

因为lib-flexible.js 原本是用来适配移动端的,所以,需要改动一些代码才能适配PC大屏,只能通过整个文件引入,不要通过npm安装,否则每次安装都需要重新修改代码

需要改动的代码如下

function refreshRem() {
    var width = docEl.getBoundingClientRect().width;
    
    // 适配移动端
    // if (width / dpr > 540) {
    //   width = 540 * dpr;
    // }

    // 适配PC端
    // 最小宽度1200px
    // if (width / dpr < 1200) {
    //   width = 1200 * dpr;
    // }
    
    // 根据这个值计算postcss-plugin-px2rem的配置参数 rootValue
    // 设置px->rem的比例是:100
    // var rem = width / 10;
    var rem = width / 100;
    docEl.style.fontSize = rem + "px";
    flexible.rem = win.rem = rem;
  }

这里可以设置一个最小宽度,配置页面也设置一个最小宽度,避免屏幕过小而变形

body{
	min-width: 1200px;
}

完整代码

/**
 * lib-flexible
 * Version 0.3.2
 * https://www.npmjs.com/package/lib-flexible?activeTab=code
 */
(function (win, lib) {
  var doc = win.document;
  var docEl = doc.documentElement;
  var metaEl = doc.querySelector('meta[name="viewport"]');
  var flexibleEl = doc.querySelector('meta[name="flexible"]');
  var dpr = 0;
  var scale = 0;
  var tid;
  var flexible = lib.flexible || (lib.flexible = {});

  if (metaEl) {
    console.warn("将根据已有的meta标签来设置缩放比例");
    var match = metaEl
      .getAttribute("content")
      .match(/initial\-scale=([\d\.]+)/);
    if (match) {
      scale = parseFloat(match[1]);
      dpr = parseInt(1 / scale);
    }
  } else if (flexibleEl) {
    var content = flexibleEl.getAttribute("content");
    if (content) {
      var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
      var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
      if (initialDpr) {
        dpr = parseFloat(initialDpr[1]);
        scale = parseFloat((1 / dpr).toFixed(2));
      }
      if (maximumDpr) {
        dpr = parseFloat(maximumDpr[1]);
        scale = parseFloat((1 / dpr).toFixed(2));
      }
    }
  }

  if (!dpr && !scale) {
    var isAndroid = win.navigator.appVersion.match(/android/gi);
    var isIPhone = win.navigator.appVersion.match(/iphone/gi);
    var devicePixelRatio = win.devicePixelRatio;
    if (isIPhone) {
      // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
      if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
        dpr = 3;
      } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
        dpr = 2;
      } else {
        dpr = 1;
      }
    } else {
      // 其他设备下,仍旧使用1倍的方案
      dpr = 1;
    }
    scale = 1 / dpr;
  }

  docEl.setAttribute("data-dpr", dpr);
  if (!metaEl) {
    metaEl = doc.createElement("meta");
    metaEl.setAttribute("name", "viewport");
    metaEl.setAttribute(
      "content",
      "initial-scale=" +
        scale +
        ", maximum-scale=" +
        scale +
        ", minimum-scale=" +
        scale +
        ", user-scalable=no"
    );
    if (docEl.firstElementChild) {
      docEl.firstElementChild.appendChild(metaEl);
    } else {
      var wrap = doc.createElement("div");
      wrap.appendChild(metaEl);
      doc.write(wrap.innerHTML);
    }
  }

  function refreshRem() {
    var width = docEl.getBoundingClientRect().width;
    
    // 适配移动端
    // if (width / dpr > 540) {
    //   width = 540 * dpr;
    // }

    // 适配PC端
    // 最小宽度1200px
    // if (width / dpr < 1200) {
    //   width = 1200 * dpr;
    // }
    
    // 根据这个值计算postcss-plugin-px2rem的配置参数 rootValue
    // 设置px->rem的比例是:100
    // var rem = width / 10;
    var rem = width / 100;
    docEl.style.fontSize = rem + "px";
    flexible.rem = win.rem = rem;
  }

  win.addEventListener(
    "resize",
    function () {
      clearTimeout(tid);
      tid = setTimeout(refreshRem, 300);
    },
    false
  );
  win.addEventListener(
    "pageshow",
    function (e) {
      if (e.persisted) {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
      }
    },
    false
  );

  if (doc.readyState === "complete") {
    doc.body.style.fontSize = 12 * dpr + "px";
  } else {
    doc.addEventListener(
      "DOMContentLoaded",
      function (e) {
        doc.body.style.fontSize = 12 * dpr + "px";
      },
      false
    );
  }

  refreshRem();

  flexible.dpr = win.dpr = dpr;
  flexible.refreshRem = refreshRem;
  flexible.rem2px = function (d) {
    var val = parseFloat(d) * this.rem;
    if (typeof d === "string" && d.match(/rem$/)) {
      val += "px";
    }
    return val;
  };
  flexible.px2rem = function (d) {
    var val = parseFloat(d) / this.rem;
    if (typeof d === "string" && d.match(/px$/)) {
      val += "rem";
    }
    return val;
  };
})(window, window["lib"] || (window["lib"] = {}));

webpack参数设置

{
 loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        [
          "postcss-plugin-px2rem",
          // 配置参数
          {
            // 假设设计稿是1200px, px->rem的比例是:100
            // 1200 / 100 = 12
            rootValue: 12,
          },
        ],
      ],
    },
  },
},

比如:

设计稿尺寸为1200px

.box {
  width: 300px;
  height: 100px;
  background-color: green;
  font-size: 32px;
  line-height: 1.2;
  letter-spacing: 1px; /* ignored */
}

转换结果是

.box {
  width: 25rem;
  height: 8.33333rem;
  background-color: green;
  font-size: 2.66667rem;
  line-height: 1.2;
  letter-spacing: 0.08333rem; /* ignored */
}

在尺寸为1200px的屏幕宽度下,可以还原为代码中设置的尺寸

width: 25rem + 12px =  300px

PostCSS通过px2rem插件和lib-flexible将px单位转换为rem(root em)单位实现大屏适配_css

在尺寸为1400px的屏幕宽度下,可以还原为代码中设置的尺寸

width: 25rem + 14px =  350px

PostCSS通过px2rem插件和lib-flexible将px单位转换为rem(root em)单位实现大屏适配_ci_02


这样,在不同的屏幕下,计算出来的根元素font-size就不一样,进而导致页面元素的尺寸也不一样,实现了缩放效果

需要注意的是,这个缩放是基于宽度缩放的,如果屏幕尺寸比例不一致,会导致竖向的内容会缺失,或者出现滚动

完整代码:https://github.com/mouday/webpack-lib-flexible

参考文章

  1. https://webpack.docschina.org/plugins/mini-css-extract-plugin
  2. https://webpack.docschina.org/loaders/postcss-loader/
  3. 使用lib-flexible和postcss-pxtorem解决大屏适配问题


标签:em,插件,dpr,plugin,适配,width,rem,var,postcss
From: https://blog.51cto.com/mouday/8366009

相关文章

  • element使用组件el-form自动定位到未填写的必填条目
    问题:在form表单el-form中经常会出现表单条目比较多的问题,而且在提交的时候需要校验表单并且定位到相应的条目位置。解决:html:<el-formref="form":model="form":rules="rules"label-width="140px"><el-form-itemlabel="规则名称"prop="ruleName&quo......
  • 推荐一个前端读取CSV文件的插件Papa Parse
    PapaParse点击跳转到官网,该插件可以将文件解析成2层数组。下面是vue项目引用的方法1.安装npminstallvue-papa-parse2.引入,在main.js里importVuefrom'vue'importVuePapaParsefrom'vue-papa-parse'Vue.use(VuePapaParse)3.使用,例如delimiter这类配置,可以参考......
  • [题解] CF1748E Yet Another Array Counting Problem
    YetAnotherArrayCountingProblem给你一个长度为\(n\)的序列和一个数\(m\),求有多少个长度为\(n\)的序列\(b\)满足:\(\foralli\in[1,n],b_i\in[1,m]\)。对于每个区间\([l,r]\),\(b\)中最大值最靠左的位置和\(a\)相同。\(n,m\le2\times10^5,n\ti......
  • 神经网络入门篇:详解向量化实现的解释(Justification for vectorized implementation)
    向量化实现的解释先对几个样本计算一下前向传播,看看有什么规律:公式1.16:\(z^{[1](1)}=W^{[1]}x^{(1)}+b^{[1]}\)\(z^{[1](2)}=W^{[1]}x^{(2)}+b^{[1]}\)\(z^{[1](3)}=W^{[1]}x^{(3)}+b^{[1]}\)这里,为了描述的简便,先忽略掉\(b^{[1]}\)后面将会看到利用Python的......
  • 加载网络映射盘中的assembly失败的处理办法
     2023年11月14日10:36:28有群友提出这样的问题:我在客户的机器安装插件后,报这个错,大概会是什么原因的?按照提示信息,打开微软的网页:http://go.microsoft.com/fwlink/?LinkId=155569同时也搜索到了这篇文章:https://www.cnblogs.com/1175429393wljblog/p/5065559.html上面这......
  • template
    templatedemos(......
  • 使用Bert模型实现embedding嵌入
    参考文献:保姆级教程,用PyTorch和BERT进行文本分类-知乎(zhihu.com)模型地址:https://huggingface.co/bert-base-casedfromtransformersimportBertTokenizer,BertModeltokenizer=BertTokenizer.from_pretrained('bert-base-cased')model=BertModel.from_pretrained("......
  • 解决only one element tensors can be converted to Python scalars
    解决"onlyoneelementtensorscanbeconvertedtoPythonscalars"错误当我们使用PyTorch进行深度学习任务时,有时会遇到以下错误信息:"onlyoneelementtensorscanbeconvertedtoPythonscalars"。这个错误通常发生在我们尝试将一个只包含一个元素的张量转换为Python标量(s......
  • 国产首款IDE环境:数字广东公司联合麒麟软件打造的国内首款适配国产操作系统、蜘蛛创新
    参考:https://www.youtube.com/watch?v=fOpBEWZVKU0   在中国it历史上继“木兰编程语言(实际上套壳Python),红旗操作系统(实际上套壳Chrome),汉芯(虚假芯片),鸿蒙操作系统(安卓套壳)”后又推出了一款由数字广东公司联合麒麟软件打造的国内首款适配国产操作系统、蜘蛛创新的集成开发环......
  • Java中ThreadLocal说明 使用线程内变量,完成后需调用remove()方法将其移除,即使异常也
    Java中ThreadLocal说明,完成后需调用remove()方法将其移除,即使异常也记得remove()回收,创建ThreadLocal线程变量publicstaticThreadLocalthreadLocal=newThreadLocal<>();1、ThreadLocal是什么ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储......