最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 自适应布局方案与px2rem-loader加强版源码实现

    正文概述 掘金(九殇)   2021-05-03   747

    1.核心概念

    1.1 设备物理像素

    • 是一个物理概念,是显示器显示的最小物理单位,设备屏幕的物理像素,任何设备的物理像素的数量都是固定的
    • iPhone6的像素分辨率是750*1334

    1.2设备独立像素

    • 是一个逻辑概念,是为web开发者创造的,在CSS和javascript中使用的一个抽象的层,用于向CSS中的宽度、高度等提供信息

    • iPhone6的逻辑分辨率是375*667

    • iPhone6:window.screen.width=375,window.screen.height=667

    • px是一个相对单位,相对的是设备像素(device pixel)

      像素,又称画素,是图像显示的基本单位,译自英文“pixel”,pix是英语单词picture的常用简写,加上英语单词“元素”element,就得到pixel,故“像素”表示“图像元素”之意,有时亦被称为pel(picture element) 像素是网页布局的基础。一个像素就是计算机能够显示一种特定颜色的最小区域。当设备尺寸相同但像素变得更密集时,屏幕能显示的画面的过渡更细致,网站看起来更明快。

      每一个CSS声明和几乎所有的javascript属性都使用CSS像素,因此实际上从来用不上设备像素 ,唯一的例外是screen.width/height

    1.3 设备像素比

    • DPR(设备像素比) = 设备像素/CSS像素

    • 设备像素比 window.devicePixelRatio

      在早先的移动设备中,并没有DPR的概念。随着技术的发展,移动设备的屏幕像素密度越来越高。从iphone4开始,苹果公司推出了所谓的retina视网膜屏幕。之所以叫做视网膜屏幕,是因为屏幕的PPI(屏幕像素密度)太高,人的视网膜无法分辨出屏幕上的像素点。iphone4的分辨率提高了一倍,但屏幕尺寸却没有变化,这意味着同样大小的屏幕上,像素多了一倍,于是DPR = 2 实际上,此时的CSS像素对应着以后要提到的理想视口,其对应的javascript属性是screen.width/screen.height而对于设备像素比DPR也有对应的javascript属性window.devicePixelRatio 以iphone5为例,iphone5的CSS像素为320px*568px,DPR是2,所以其设备像素为640px*1136px

    自适应布局方案与px2rem-loader加强版源码实现

    1.4 移动端适配

    • 一般由设计师按照设备像素(device pixel)为单位制作设计稿
    • 然后由前端工程师参照设备像素比(device pixel ratio)进行换算

    1.4.1 rem

    • 参照根元素的字体大小
    • 适配就是让根元素的字体大小根据分辨率进行动态改变
    • px2rem-loader

    1.4.2 vw和vh

    • 参照的是viewport视口
    • vw参照的是视口的宽度(1vw=视口宽度/100)
    • vh参照的是视口的高度(1vh=视口高度/100)
    • iPhone6 1vw=3.75px
    • postcss-px-to-viewport

    2.px2rem-loader实战

    2.1 安装

    npm install webpack webpack-cli html-webpack-plugin style-loader css-loader amfe-flexible px2rem-loader --save-dev
    

    2.2 src\index.js

    import './base.css'
    

    2.3 src\base.css

     width:750px;
      height:750px;
      background-color: red;
      font-size:12px;/*px*/
      border: 1px solid #ddd; /*no*/
      box-shadow: 0 2px 0 rgb(0 0 0 / 5%);
    }
    

    2.4 src\index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>webpack</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
    </html>
    

    2.5 webpack.config.js

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        mode:'development',
        devtool: false,
        entry: './src/index.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: '[name].js',
        },
        resolveLoader: {
            alias: {
              "px2rem-plus-loader": path.resolve('./loaders/px2rem-plus-loader.js')
            },
            modules: [path.resolve('./loaders'), 'node_modules']
        },
        module: {
            rules: [
              {
                test: /\.css$/,
                use: [{
                    loader: 'style-loader'
                }, {
                    loader: 'css-loader'
                }, 
                // {
                //     loader: 'px2rem-loader',
                //     options: {
                //         remUni: 75,
                //         remPrecision: 8
                //     }
                // },
                {
                  loader: 'px2rem-plus-loader', //path.resolve(__dirname, 'loaders/px2rem-plus-loader.js'),
                  options: {
                      remUnit: 75,
                      remPrecision: 8,
                      baseDpr:1,
                      exclude:/antd\.css/
                  }
                }
                ]
              },
              {
                test:/\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                      presets: ['@babel/preset-env','@babel/preset-react'],
                      plugins: ['@babel/transform-runtime']
                    }
                  },
                exclude: /node_modules/,        
              },
            ]
        },
        plugins: [
            new HtmlWebpackPlugin(
              { template: './src/index.html', 
                title:'index',
                inject: true
              }
            ),
        ]
    };
    
    

    2.6 package.json

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

    3.loader

    • loader 用于对模块的源代码进行转换
    • loader 可以使你在 import 模块时预处理文件
    • loader 可以将文件从不同的语言(如TypeScript)转换为 JavaScript

    loaders\px2rem-plus-loader.js

    var loaderUtils = require('loader-utils');
    //var Px2rem = require('px2rem');
    var Px2rem = require('./px2rem');
    function loader(source) {
      var options = loaderUtils.getOptions(this);
      if(options.exclude && options.exclude.test(this.resource)){
           return source;
      }
      var px2remIns = new Px2rem(options);
      let targetSource = px2remIns.generateRem(source);
      return targetSource;
    }
    module.exports = loader;
    

    4. 使用自定义loader

    webpack.config.js 中的配置

    resolveLoader: {
            alias: {
              "px2rem-plus-loader": path.resolve('./loaders/px2rem-plus-loader.js')
            },
            modules: [path.resolve('./loaders'), 'node_modules']
        },
    

    5.css

    5.1 AST

    • astexplorer

    • JavaScript Parser可以把源代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构

      自适应布局方案与px2rem-loader加强版源码实现

    5.2 AST工作流

    • Parse(解析) 将源代码转换成抽象语法树,树上有很多的estree节点
    • Transform(转换) 对抽象语法树进行转换
    • Generate(代码生成) 将上一步经过转换过的抽象语法树生成新的代码 自适应布局方案与px2rem-loader加强版源码实现

    5.3 px2rem.js

    px2rem

    /*
     * @Description: 
     * @Author: changqing
     * @Date: 2021-04-29 11:54:54
     * @LastEditTime: 2021-05-02 11:46:46
     * @LastEditors: changqing
     * @Usage: 
     */
    'use strict';
    
    var css = require('css');
    var extend = require('extend');
    
    var defaultConfig = {
      baseDpr: 2,             // base device pixel ratio (default: 2)
      remUnit: 75,            // rem unit value (default: 75)
      remPrecision: 6,        // rem value precision (default: 6)
      forcePxComment: 'px',   // force px comment (default: `px`)
      keepComment: 'no'       // no transform value comment (default: `no`)
    };
    
    var pxRegExp = /\b(\d+(\.\d+)?)px\b/;
    
    function Px2rem(options) {
      this.config = {};
      extend(this.config, defaultConfig, options);
    }
    
    // generate @1x, @2x and @3x version stylesheet
    Px2rem.prototype.generateThree = function (cssText, dpr) {
      dpr = dpr || 2;
      var self = this;
      var config = self.config;
      var astObj = css.parse(cssText);
    
      function processRules(rules) {
        for (var i = 0; i < rules.length; i++) {
          var rule = rules[i];
          if (rule.type === 'media') {
            processRules(rule.rules); // recursive invocation while dealing with media queries
            continue;
          } else if (rule.type === 'keyframes') {
            processRules(rule.keyframes); // recursive invocation while dealing with keyframes
            continue;
          } else if (rule.type !== 'rule' && rule.type !== 'keyframe') {
            continue;
          }
    
          var declarations = rule.declarations;
          for (var j = 0; j < declarations.length; j++) {
            var declaration = declarations[j];
            // need transform: declaration && has 'px'
            if (declaration.type === 'declaration' && pxRegExp.test(declaration.value)) {
              var nextDeclaration = rule.declarations[j + 1];
              if (nextDeclaration && nextDeclaration.type === 'comment') { // next next declaration is comment
                if (nextDeclaration.comment.trim() === config.keepComment) { // no transform
                  declarations.splice(j + 1, 1); // delete corresponding comment
                  continue;
                } else if (nextDeclaration.comment.trim() === config.forcePxComment) { // force px
                  declarations.splice(j + 1, 1); // delete corresponding comment
                }
              }
              declaration.value = self._getCalcValue('px', declaration.value, dpr); // common transform
            }
          }
        }
      }
    
      processRules(astObj.stylesheet.rules);
    
      return css.stringify(astObj);
    };
    
    // generate rem version stylesheet
    Px2rem.prototype.generateRem = function (cssText) {
      var self = this;
      var config = self.config;
      var astObj = css.parse(cssText);
    
      function processRules(rules, noDealPx) { // FIXME: keyframes do not support `force px` comment
        for (var i = 0; i < rules.length; i++) {
          var rule = rules[i];
          if (rule.type === 'media') {
            processRules(rule.rules); // recursive invocation while dealing with media queries
            continue;
          } else if (rule.type === 'keyframes') {
            processRules(rule.keyframes, true); // recursive invocation while dealing with keyframes
            continue;
          } else if (rule.type !== 'rule' && rule.type !== 'keyframe') {
            continue;
          }
    
          if (!noDealPx) {
            // generate 3 new rules which has [data-dpr]
            var newRules = [];
            for (var dpr = 1; dpr <= 3; dpr++) {
              var newRule = {};
              newRule.type = rule.type;
              newRule.selectors = rule.selectors.map(function (sel) {
                return '[data-dpr="' + dpr + '"] ' + sel;
              });
              newRule.declarations = [];
              newRules.push(newRule);
            }
          }
    
          var declarations = rule.declarations;
          for (var j = 0; j < declarations.length; j++) {
            var declaration = declarations[j];
            // need transform: declaration && has 'px'
            if (declaration.type === 'declaration' && pxRegExp.test(declaration.value)) {
              var nextDeclaration = declarations[j + 1];
              if (nextDeclaration && nextDeclaration.type === 'comment') { // next next declaration is comment
                if (nextDeclaration.comment.trim() === config.forcePxComment) { // force px
                  // do not transform `0px`
                  if (declaration.value === '0px') {
                    declaration.value = '0';
                    declarations.splice(j + 1, 1); // delete corresponding comment
                    continue;
                  }
                  if (!noDealPx) {
                    // generate 3 new declarations and put them in the new rules which has [data-dpr]
                    for (var dpr = 1; dpr <= 3; dpr++) {
                      var newDeclaration = {};
                      extend(true, newDeclaration, declaration);
                      newDeclaration.value = self._getCalcValue('px', newDeclaration.value, dpr);
                      newRules[dpr - 1].declarations.push(newDeclaration);
                    }
                    declarations.splice(j, 2); // delete this rule and corresponding comment
                    j--;
                  } else { // FIXME: keyframes do not support `force px` comment
                    declaration.value = self._getCalcValue('rem', declaration.value); // common transform
                    declarations.splice(j + 1, 1); // delete corresponding comment
                  }
                } else if (nextDeclaration.comment.trim() === config.keepComment) { // no transform
                  declarations.splice(j + 1, 1); // delete corresponding comment
                } else {
                  declaration.value = self._getCalcValue('rem', declaration.value); // common transform
                }
              } else {
                declaration.value = self._getCalcValue('rem', declaration.value); // common transform
              }
            }
          }
    
          // if the origin rule has no declarations, delete it
          if (!rules[i].declarations.length) {
            rules.splice(i, 1);
            i--;
          }
    
          if (!noDealPx) {
            // add the new rules which contain declarations that are forced to use px
            if (newRules[0].declarations.length) {
              rules.splice(i + 1, 0, newRules[0], newRules[1], newRules[2]);
              i += 3; // skip the added new rules
            }
          }
        }
      }
    
      processRules(astObj.stylesheet.rules);
    
      return css.stringify(astObj);
    };
    
    // get calculated value of px or rem
    Px2rem.prototype._getCalcValue = function (type, value, dpr) {
      var config = this.config;
      var pxGlobalRegExp = new RegExp(pxRegExp.source, 'g');
    
      function getValue(val) {
        val = parseFloat(val.toFixed(config.remPrecision)); // control decimal precision of the calculated value
        return val == 0 ? val : val + type;
      }
    
      return value.replace(pxGlobalRegExp, function ($0, $1) {
        return type === 'px' ? getValue($1 * dpr / config.baseDpr) : getValue($1 / config.remUnit);
      });
    };
    
    module.exports = Px2rem;
    
    
    

    5.4 usePx2rem.js

    let Px2rem = require('./px2rem');
    let px2rem = new Px2rem({
        remUnit: 75,
        remPrecision: 8
    });
    let cssText = `
    #root{
        width:750px;
        height:750px;
        font-size:12px;/*px*/
        border: 1px solid #ddd; /*no*/
    }
    `;
    let newCSS = px2rem.generateRem(cssText);
    console.log(newCSS);
    
    // #root {
    //   width: 10rem;
    //   height: 10rem;
    //   border: 1px solid #ddd;
    // }
    
    // [data-dpr="1"] #root {
    //   font-size: 6px;
    // }
    
    // [data-dpr="2"] #root {
    //   font-size: 12px;
    // }
    
    // [data-dpr="3"] #root {
    //   font-size: 18px;
    // }
    

    6. px2rem-loader.js

    • loader-utils是一个webpack工具类

    • px2rem-loader

    • 直接写px,编译后会直接转化成rem

    • 在px后面添加/no/,不会转化px,会原样输出 一般border需用这个

    • 在px后面添加/px/,会根据dpr的不同,生成三套代码 一般字体需用这个

    7. lib-flexible

    import './base.css';
    import 'amfe-flexible/index.js';
    

    8. 第三方框架样式问题

    • 如果第三方组件已经是为移动端做了适配,px2rem又转成了rem就导致其样式变的很小

    8.1 index.js

    import './base.css'
    //import 'amfe-flexible/index.js';
    import 'lib-flexible/flexible.js'
    import React from 'react';
    import ReactDOM from 'react-dom';
    import 'antd/dist/antd.css';
    import {Button} from 'antd';
    ReactDOM.render(<div>
        我是<Button type="primary">按钮</Button>
    </div>, document.getElementById('root'));
    

    8.2 webpack.config.js

    {
        loader: 'px2rem-plus-loader', //path.resolve(__dirname, 'loaders/px2rem-plus-loader.js'),
         options: {
                      remUnit: 75,
                      remPrecision: 8,
                      baseDpr:1,
                      exclude:/antd\.css/
                  }
          }
    

    px2rem-plus-loader 增加 exclude参数排除不需要处理的css文件

    仓库地址


    起源地下载网 » 自适应布局方案与px2rem-loader加强版源码实现

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元