前言
主要是用vw来实现的,1vw相当于100视窗宽度,设计的宽度是375px,那么1vw就是3.75像素了。
一、设置meta标签
<meta name="viewport" content="width=device-width, initial-scale=1.0">
二、配置适配plugin
1.在根目录传一个一个plugins文件
2.创建一个postcss-px-to-viewport.ts和一个types.ts
三、postcss-px-to-viewport.ts
/** 编写 postcss 插件 vite内置了postCss 无需安装 */
import { Plugin } from 'postcss'
import { type Options } from './types'
const Option = {
/** UI设计稿的宽度 给多少写多少 一般默认375 */
viewPortWidth: 375,
}
export const PostCssPxToViewport = (options: Options = Option): Plugin => {
const opt = Object.assign({}, Option, options)
return {
postcssPlugin: 'postcss-px-to-viewport',
/** 拿到所有css节点的钩子函数 */
Declaration(node) {
const value = node.value
if (value.includes('px')) {
/** 考虑到小数 */
const num = parseFloat(value)
/** 把px转换成vw再重新赋值给css节点 */
node.value = `${((num / opt.viewPortWidth) * 100).toFixed(2)}vw`
}
}
}
}
/** types.ts */
export type Options = {
viewPortWidth?: number
}
四、在tsconfig.node.json配置plugins
五、在vite.config.ts配置plugins
前面步骤完成之后 我们看一下效果,可以自适应调整看效果(本人不会搞gif,所以就截图了)。 前面的步骤是百度参考的。
乍一看可以了,开心的很。
可是刚开心没多久就发现问题了,当我写一个 padding: 0 20px 20px;样式的时候 发现为padding被转换为0了,咦!这是咋回事呢?
既然有问题那么咱们就是log打印康康,看了发现除了类似padding: 20px;的才正常。既然发现问题了那么我们就好去解决问题了
/** 拿到所有css节点的钩子函数 */
Declaration(node) {
const value = node.value
if (value.includes('px')) {
/** 考虑到小数 */
const num = parseFloat(value)
console.log('value', value)
console.log('num', num)
/** 把px转换成vw再重新赋值给css节点 */
node.value = `${((num / opt.viewPortWidth) * 100).toFixed(2)}vw`
}
}
六、分析一下怎么去处理上面截图转换失败的
1.我们写一个正则去匹配value中带有px的
2.通过上面匹配得出的数组我们再去遍历这个数组把px转换成vw
3.再把转换的值组成和我们想要的值(padding: 16px 20px; => padding: xxvw xxvw;)再这个值赋值给node.value
1.我们写出匹配带有px的正则
let str = '0 20px 20px'
const matches = str.match(/(\d+(\.\d+)?)\s*px/g) || []
console.log('matches', matches) => ['20px','20px']
let str = 'transformY(-14px)'
const matches = str.match(/(\d+(\.\d+)?)\s*px/g) || []
console.log('matches', matches) => ['14px']
2.我们写一个辅助函数去转换匹配到的值
/**
* 转换px辅助函数
* @param originalString 字符串来源
* @param searchString 要被替换的值
* @param replaceWith 替换成什么值
* @returns
*/
const transfromValue = (
originalString: string,
searchString: string,
replaceWith: string
) => {
if (originalString.includes(searchString)) {
/** 转义正则表达式特殊字符 */
const escapedSearchString = searchString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const regex = new RegExp(escapedSearchString, 'g')
const newString = originalString.replace(regex, replaceWith)
return newString;
}
/** 如果没有包含,则返回原始字符串 */
return originalString;
}
3.再把这个值赋值给node.value
/**
* 转换px辅助函数
* @param originalString 字符串来源
* @param searchString 要被替换的值
* @param replaceWith 替换成什么值
* @returns
*/
const transfromValue = (
originalString: string,
searchString: string,
replaceWith: string
) => {
if (originalString.includes(searchString)) {
/** 转义正则表达式特殊字符 */
const escapedSearchString = searchString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const regex = new RegExp(escapedSearchString, 'g')
const newString = originalString.replace(regex, replaceWith)
return newString;
}
/** 如果没有包含,则返回原始字符串 */
return originalString;
}
Declaration(node) {
const value = node.value
let str = value
/** 匹配字符串带px的数组 */
const matches = str.match(/(\d+(\.\d+)?)\s*px/g) || []
matches.forEach(item => {
const num = parseFloat(item)
const replaceValue = `${((num / opt.viewPortWidth) * 100).toFixed(3)}vw`
str = transfromValue(str, item, replaceValue)
})
if (!Number.isNaN(str)) node.value = str
}
4.修复一个BUG
matches.forEach(item => {
/** 0px就没必要转换了(不然转换’box-shadow‘的时候会出问题) */
if (item === '0px') return
const num = parseFloat(item)
const replaceValue = `${((num / opt.viewPortWidth) * 100).toFixed(6)}vw`
str = transfromValue(value, item, replaceValue)
})
七、postcss-px-to-viewport.ts完整代码
/** 编写 postcss 插件 vite内置了postCss 无需安装 */
import { Plugin } from 'postcss'
import { type Options } from './types'
const Option = {
/** UI设计稿的宽度 给多少写多少 一般默认375 */
viewPortWidth: 375,
}
/**
* 转换px辅助函数
* @param originalString 字符串来源
* @param searchString 要被替换的值
* @param replaceWith 替换成什么值
* @returns
*/
const transfromValue = (
originalString: string,
searchString: string,
replaceWith: string
) => {
if (originalString.includes(searchString)) {
/** 转义正则表达式特殊字符 */
const escapedSearchString = searchString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const regex = new RegExp(escapedSearchString, 'g')
const newString = originalString.replace(regex, replaceWith)
return newString;
}
/** 如果没有包含,则返回原始字符串 */
return originalString;
}
export const PostCssPxToViewport = (options: Options = Option): Plugin => {
const opt = Object.assign({}, Option, options)
return {
postcssPlugin: 'postcss-px-to-viewport',
/** 拿到所有css节点的钩子函数 */
Declaration(node) {
const value = node.value
if (!value.includes('px')) return
/** 判断是否是只有一个值 */
if (value.endsWith('px') && !value.includes(' ')) {
/** 考虑到有小数 */
const num = parseFloat(value)
if (!Number.isNaN(num)) node.value = `${((num / opt.viewPortWidth) * 100).toFixed(3)}vw`
} else {
let str = value
/** 匹配字符串带px的数组 */
const matches = str.match(/(\d+(\.\d+)?)\s*px/g) || []
matches.forEach(item => {
/** 0px就没必要转换了(不然转换’box-shadow‘的时候会出问题) */
if (item === '0px') return
const num = parseFloat(item)
const replaceValue = `${((num / opt.viewPortWidth) * 100).toFixed(3)}vw`
str = transfromValue(value, item, replaceValue)
})
if (!Number.isNaN(str)) node.value = str
}
}
}
}
八、咱们再来看看效果如何
下面的图已经看得出来不管是padding多个值还是border或者transfromY都正常的转化完成。
结语
本人主要是用RN开发APP的,Vue3没用过所以适配上面踩了一些坑,既然解决了就记录一下,如果有问题请帮忙指出哈!完结 撒花!!!