Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 59 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## 功能

一个postcss插件,用来将css中的dpx 换算成px
基于postcss-plugin-px2rem 实现的一个postcss插件,用来将css中的dpx 换算成px以及将px转换成rem.保留了postcss-plugin-px2rem的所有配置。

### 输入输出

Expand All @@ -12,7 +12,7 @@ h1 {

// 输出
h1 {

font-size: 16px
}
[data-dpr="1"] h1 {
font-size: 16px
Expand All @@ -23,11 +23,28 @@ h1 {
[data-dpr="3"] h1 {
font-size: 48px
}
```
// 输入
h1 {
margin: 0 0 20px;
font-size: 32px;
line-height: 1.2;
letter-spacing: 1px;
}

// 输出
h1 {
margin: 0 0 0.2rem;
font-size: 0.32rem;
line-height: 1.2;
letter-spacing: 0.01rem;
}
```
### 安装
npm install --save-dev postcss-plugin-pxtorem

### webpack配置
webpack rules添加 postcss-loader
### 配置使用
###### webpack配置
webpack rules添加 postcss-loader配置
```js
module.exports = {
module: {
Expand All @@ -39,34 +56,57 @@ module.exports = {
}
}
```

在根目录新增 `postcss.config.js` 文件:
##### postcss 配置
在根目录新增 `postcss.config.js` postcss配置文件:

```js
// 默认的配置
const pxtoremOpts = {
......
};
module.exports = {
plugins: [
require('postcss-plugin-dpxtopx')({
// prevName: 'data-dpr'
// maxDpr: 3,
// delete: true
})
require('postcss-plugin-pxtorem')(pxtoremOpts)
]
}
```

## 配置

Default:
默认配置:
```js
{
prevName: 'data-dpr',
rootValue: 100,
unitPrecision: 5,
propWhiteList: [],
propBlackList: [],
selectorBlackList: [],
ignoreIdentifier: false,
replace: true,
mediaQuery: false,
minPixelValue: 0
prefix: 'data-dpr',
maxDpr: 3,
delete: true
delete: true,
pxtorem: true
}
```
`prevName` 生成的前缀 默认值 data-dpr
`maxDpr` 生成的dpr的最大值 默认值 3
`delete` 是否删除匹配到的声明 默认值 true
`prefix` (String)生成的前缀 默认值 data-dpr
`maxDpr` (Number)生成的dpr的最大值 默认值 3
`delete` (Boolean)是否删除匹配到的声明 默认值 true
`pxtorem` (Boolean)是否需要讲px转化成rem 默认值 true
`rootValue` (Number\Object)根元素字体大小。默认值为100。-如果rootValue是一个对象,例如`{px:50,rpx:100}‘,它将替换RPX到1/100 rem,将px替换为1/50 rem.

`unitPrecision` (Number)允许rem保留几位小数
`propWhiteList` (Array)可以从px更改为rem的属性。默认值是一个空数组,白名单并启用所有属性。值必须是完全匹配的。
`propBlackList` (Array)不应从px更改为rem的属性。值需要完全匹配.

`selectorBlackList` (Array) 如果值是字符串,则检查选择器是否包含字符串,如`[body]`将匹配`.body-class`。如果值是正则表达式,则检查选择器是否与该正则表达式匹配,如`[/^body$/]`将匹配`body`,但不匹配`.body`。


`ignoreIdentifier` (Boolean/String) 一种将单个属性忽略的方法,如果启用了不命名标识符,则“replace”将自动设置为“true”。
`replace` (Boolean) 取代包含rems的规则,而不是添加后备项。
`mediaQuery` (Boolean) 允许在媒体查询中转换PX。
`minPixelValue` (Number) 设置要替换的最小像素值。
## 测试
控制台输入 npm test
控制台输入 npm run test 测试dpx转px以及px转rem
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
module.exports = require('./src');
module.exports = require('./lib');

40 changes: 20 additions & 20 deletions src/index.js → lib/dpxtopx.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
var postcss = require('postcss');

const defaultOpts = {
prevName: 'data-dpr',
prefix: 'data-dpr',
maxDpr: 3,
delete: true
};


module.exports = postcss.plugin('postcss-plugin-dpxtopx', function(options) {
return function(root) {
options = Object.assign({}, defaultOpts, options || {});
let insertRule = insertDpx(options.prevName, options.maxDpr);
root.walkRules((rule) => {
let declList = [];
rule.walkDecls((decl) => {
let data = getDpx(decl);
if (data) {
declList.push(data);
if (options.delete) {
decl.remove();
}
module.exports = function(opts, root) {
let options = { ...defaultOpts, ...opts };
let insertRule = insertDpx(options.prefix, options.maxDpr);
root.walkRules((rule) => {
let declList = [];
rule.walkDecls((decl) => {
let data = getDpx(decl);
if (data) {
declList.push(data);
if (options.delete) {
decl.remove();
} else {
decl.value = data.value + 'px';
}
})
insertRule(rule, declList);
}
})
}
});
insertRule(rule, declList);
})
};


const getDpx = (decl) => {
Expand All @@ -44,13 +44,13 @@ const getDpx = (decl) => {
return undefined;
}

const insertDpx = (prevName, maxDpr) => (rule, declList) => {
const insertDpx = (prefix, maxDpr) => (rule, declList) => {
if (!declList || declList.length === 0) {
return;
}
let unit = 'px';
for (let i = maxDpr; i > 0; i--) {
let ruleName = `[${prevName}="${i}"] ${rule.selector}`;
let ruleName = `[${prefix}="${i}"] ${rule.selector}`;
let newRule = createRule(ruleName);
declList.forEach(decl => {
newRule.append({ prop: decl.prop, value: (decl.value * i) + unit });
Expand Down
10 changes: 10 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let postcss = require('postcss');
let pxtorem = require('./pxtorem');
let dpxtopx = require('./dpxtopx');
module.exports = postcss.plugin('postcss-plugin-pxtorem', options => {
const opts = { ...options };
return css => {
dpxtopx(opts, css);
pxtorem(opts, css);
};
});
122 changes: 122 additions & 0 deletions lib/pxtorem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
let postcss = require('postcss');
const defaultOpts = {
rootValue: 100,
unitPrecision: 5,
selectorBlackList: [],
propWhiteList: [],
propBlackList: [],
ignoreIdentifier: false,
replace: true,
mediaQuery: false,
minPixelValue: 0,
pxtorem: true
};

const toFixed = (number, precision) => {
const multiplier = Math.pow(10, precision + 1);
const wholeNumber = Math.floor(number * multiplier);

return Math.round(wholeNumber / 10) * 10 / multiplier;
};
const isObject = o => typeof o === 'object' && o !== null;

const createPxReplace = (rootValue, identifier, unitPrecision, minPixelValue) => (m, $1, $2) => {
if (!$1) return m;
if (identifier && m.indexOf(identifier) === 0) return m.replace(identifier, '');
const pixels = parseFloat($1);
if (pixels < minPixelValue) return m;
// { px: 100, rpx: 50 }
const baseValue = isObject(rootValue) ? rootValue[$2] : rootValue;
const fixedVal = toFixed((pixels / baseValue), unitPrecision);

return `${fixedVal}rem`;
};

const declarationExists = (decls, prop, value) => decls.some(decl =>
decl.prop === prop && decl.value === value
);

const blacklistedSelector = (blacklist, selector) => {
if (typeof selector !== 'string') return false;

return blacklist.some(regex => {
if (typeof regex === 'string') return selector.indexOf(regex) !== -1;

return selector.match(regex);
});
};

const blacklistedProp = (blacklist, prop) => {
if (typeof prop !== 'string') return false;

return blacklist.some(regex => {
if (typeof regex === 'string') return prop.indexOf(regex) !== -1;

return prop.match(regex);
});
};

const handleIgnoreIdentifierRegx = (identifier, unit) => {
const _identifier = identifier;
let backslashfy = _identifier.split('').join('\\');
backslashfy = `\\${backslashfy}`;
const pattern = `"[^"]+"|'[^']+'|url\\([^\\)]+\\)|((?:${backslashfy}|\\d*)\\.?\\d+)(${unit})`;

return new RegExp(pattern, 'ig');
};

module.exports = (options, css) => {
const opts = { ...defaultOpts, ...options };
if (!opts.pxtorem) {
return;
}
let unit = 'px';
if (isObject(opts.rootValue)) {
unit = Object.keys(opts.rootValue).join('|');
}

const regText = `"[^"]+"|'[^']+'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)(${unit})`;
let pxRegex = new RegExp(regText, 'ig');
let identifier = opts.ignoreIdentifier;
if (identifier && typeof identifier === 'string') {
identifier = identifier.replace(/\s+/g, '');
opts.replace = true;
pxRegex = handleIgnoreIdentifierRegx(identifier, unit);
} else {
identifier = false;
}
const pxReplace = createPxReplace(opts.rootValue, identifier, opts.unitPrecision, opts.minPixelValue);

css.walkDecls((decl, i) => {
const _decl = decl;
// 1st check 'px'
if (_decl.value.indexOf('px') === -1) return;
// 2nd check property black list
if (blacklistedProp(opts.propBlackList, _decl.prop)) return;
// 3rd check property white list
if (opts.propWhiteList.length && opts.propWhiteList.indexOf(_decl.prop) === -1) return;
// 4th check seletor black list
if (blacklistedSelector(opts.selectorBlackList, _decl.parent.selector)) return;

const value = _decl.value.replace(pxRegex, pxReplace);

// if rem unit already exists, do not add or replace
if (declarationExists(_decl.parent, _decl.prop, value)) return;

if (opts.replace) {
_decl.value = value;
} else {
_decl.parent.insertAfter(i, _decl.clone({
value,
}));
}
});

if (opts.mediaQuery) {
css.walkAtRules('media', rule => {
const _rule = rule;
if (_rule.params.indexOf('px') === -1) return;
_rule.params = _rule.params.replace(pxRegex, pxReplace);
});
}
};
22 changes: 14 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
{
"name": "postcss-plugin-dpxtopx",
"version": "1.0.0",
"description": "基于 postcss 添加 dpx 转 px 单位支持",
"name": "postcss-plugin-pxtorem",
"version": "0.0.1",
"description": "基于postcss-plugin-px2rem 实现的一个postcss插件,用来将css中的dpx 换算成px以及将px转换成rem.保留了postcss-plugin-px2rem的所有配置。",
"main": "index.js",
"scripts": {
"test": "mocha"
"test": "mocha",
"test-rem": "mocha ./test/index-test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sunxhh/postcss-plugin-pxtorem.git"
"url": "git@github.com:sunxhh/postcss-plugin-pxtorem.git"
},
"keywords": [
"postcss",
"dpx",
"px"
],
"author": "",
"license": "ISC",
"author": {
"name": "sunxh",
"email": "997854244@qq.com"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/sunxhh/postcss-plugin-pxtorem/issues"
},
Expand All @@ -29,5 +33,7 @@
"expect": "^23.0.0",
"mocha": "^5.2.0"
},
"dependencies": {}
"dependencies": {
"postcss-plugin-pxtorem": "0.0.1"
}
}
Loading