跳到主要内容

认识

2025年01月14日
柏拉文
越努力,越幸运

一、认识


vwViewport Width:相对于视口宽度的百分比单位。1vw 等于视口宽度的 1%。适用于流式布局,尤其在响应式设计中,用于根据设备宽度调整元素大小。适配思路为: 将 px 转化为 vw 即可。

VW 计算规则: targetValue(最终要在代码中写的 vw 值) = UI 稿元素(尺寸、字体大小等) / UI 稿宽度 * 100(100vw 始终为屏幕尺寸)

举例: 假如我们的 UI 稿宽度为 750px, 有一个元素宽为 100px, 我们在 iPhone 14 Pro Max 430*932 的终端开发, 为了很方便的适配各种终端以及不同终端尺寸与 UI 稿无缝衔接。我们需要将 100px 转换成对应的 vw 值, 计算为: targetValue = 100 / 750 * 100 = 13.33 vw

二、实现


2.1 App.tsx

import "uno.css";
import "./App.css";
import "./App.scss";
import city1 from "./images/city1.png";
import AppStyle from "./App.module.scss";


function App() {

return (
<div className="app">
App 页面
<div className={AppStyle.div1}></div>
<img className="w-750" src={city1} />
<div className="m-1 color-SubColor font-size-32">嘻嘻哈哈</div>
<div className="p-2 color-Text2 bg-BrandDark font-size-32">哈哈嘻嘻</div>
<div className="box"></div>
<div className="text-ellipsis-2 w-100 font-size-32">
敷设电缆;范德萨范德萨;林凤娇了;附件都说了;就发了;三等奖发了发;啦束带结发;拉数据;
发;苏妲己;啊;发的酸;拉法基;了发的酸;浪费啊;减肥的;是佛i额文峰街道舒服了扩大升级
</div>
<span className="text-ellipsis-2-inline w-100 font-size-32">
敷设电缆;范德萨范德萨;林凤娇了;附件都说了;就发了;三等奖发了发;啦束带结发;拉数据;
发;苏妲己;啊;发的酸;拉法基;了发的酸;浪费啊;减肥的;是佛i额文峰街道舒服了扩大升级
</span>
</div>
);
}

export default App;

2.2 uno.config.js

UnoCSS 中,需要我们自己换算单位。我们的方案是, targetValue(最终要在代码中写的 vw 值) = UI 稿元素(尺寸、字体大小等) / UI 稿宽度 * 100(100vw 始终为屏幕尺寸)。所以, 换算逻辑如下:

import { Preset } from "unocss";

const uiWidth = 750;

const getSizeValue = (value: string): string => {
if (!value) return "";

if (value.includes("-")) {
return value.split("-").map(getSizeValue).join(" ");
}

const numValue = Number(value);

if (isNaN(numValue)) {
return value;
}

const vwSize = (numValue / uiWidth) * 100;
return vwSize > 0 ? `${vwSize}vw` : "0";
};

const boxSizeValue = [
"border-box",
"content-box",
"clip",
"visible",
"scroll",
].join("|");

export const preset1: Preset = {
name: "preset1",
rules: [
[
/^w-([.\d]+)$/,
([_, num]) => ({ width: getSizeValue(num) }),
{ autocomplete: "w-<num>" },
],
[
/^max-w-([.\d]+)$/,
([, value]) => ({ "max-width": getSizeValue(value) }),
{ autocomplete: "max-w-<num>" },
],
[
/^min-w-([.\d]+)$/,
([, value]) => ({ "min-width": getSizeValue(value) }),
{ autocomplete: "min-w-<num>" },
],
[
/^h-([.\d]+)$/,
([, value]) => ({ height: getSizeValue(value) }),
{ autocomplete: "h-<num>" },
],
[
/^max-h-([.\d]+)$/,
([, value]) => ({ "max-height": getSizeValue(value) }),
{ autocomplete: "max-h-<num>" },
],
[
/^min-h-([.\d]+)$/,
([, value]) => ({ "min-height": getSizeValue(value) }),
{ autocomplete: "min-h-<num>" },
],
[
new RegExp(`^(?:box)-(${boxSizeValue})$`),
([, v]) => (boxSizeValue.includes(v) ? { "box-sizing": v } : undefined),
{
autocomplete: [`(box)-(${boxSizeValue})`],
},
],
[
/^m-([\d]+)$/,
([, value]) => ({ margin: getSizeValue(value) }),
{ autocomplete: "m-<num>" },
],
[
/^m-t-([\d]+)$/,
([, value]) => ({ "margin-top": getSizeValue(value) }),
{ autocomplete: "m-t-<num>" },
],
[
/^m-r-([\d]+)$/,
([, value]) => ({ "margin-right": getSizeValue(value) }),
{ autocomplete: "m-r-<num>" },
],
[
/^m-b-([\d]+)$/,
([, value]) => ({ "margin-bottom": getSizeValue(value) }),
{ autocomplete: "m-b-<num>" },
],
[
/^m-l-([\d]+)$/,
([, value]) => ({ "margin-left": getSizeValue(value) }),
{ autocomplete: "m-l-<num>" },
],
[
/^p-([\d]+)$/,
([, value]) => ({ padding: getSizeValue(value) }),
{ autocomplete: "p-<num>" },
],
[
/^p-t-([\d]+)$/,
([, value]) => ({ "padding-top": getSizeValue(value) }),
{ autocomplete: "p-t-<num>" },
],
[
/^p-r-([\d]+)$/,
([, value]) => ({ "padding-right": getSizeValue(value) }),
{ autocomplete: "p-r-<num>" },
],
[
/^p-b-([\d]+)$/,
([, value]) => ({ "padding-bottom": getSizeValue(value) }),
{ autocomplete: "p-b-<num>" },
],
[
/^p-l-([\d]+)$/,
([, value]) => ({ "padding-left": getSizeValue(value) }),
{ autocomplete: "p-l-<num>" },
],
[
/^font-size-([\d]+)$/,
([, value]) => ({ "font-size": getSizeValue(value) }),
],
[/^font-bold$/, () => ({ "font-weight": "550" })],
[/^bold$/, () => ({ "font-weight": "550" })],
[/^weight-([\d]+)$/, ([, value]) => ({ "font-weight": value })],
[
/^line-height-([0-9].*)$/,
([, value]) => ({ "line-height": getSizeValue(value) }),
],
]
};

2.3 postcss.config.js

module.exports = {
plugins: [
"autoprefixer",
"postcss-preset-env",
require('postcss-px-to-viewport')({
unitToConvert: 'px',
viewportWidth: 750,
unitPrecision: 5,
propList: ['*'],
viewportUnit: 'vw',
fontViewportUnit: 'vw',
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false,
exclude: [/node_modules/]
})
],
};

2.4 webpack.config.js

const combineCssLoader = (params = {}) => {
const { modules = false, postcss = true, devMode = true } = params;

const baseLoaders = [
devMode ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: modules ? { namedExport: false } : false,
},
},
];

if (postcss) {
baseLoaders.push({
loader: "postcss-loader",
options: {
postcssOptions: {
config: Path.resolve(process.cwd(), "./postcss.config.js"),
},
},
});
}

return baseLoaders;
};