viewport.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * viewport 适配工具 - 移动端适配方案
  3. *
  4. * 支持两套设计稿(当前统一为 393 × 852):
  5. * - mvp:393 × 852(默认)
  6. * - v2:393 × 852(与 mvp 一致,便于旧页面显式传参)
  7. *
  8. * 适配策略:
  9. * - CSS 文件:使用 postcss-px-to-viewport 自动转换 px 为 vw
  10. * - 内联样式:使用工具函数手动转换,可按页面传入 design 指定设计稿
  11. *
  12. * 单位选择规则:
  13. * - 横向属性 → px2vw(px, design?)
  14. * - 纵向属性 → px2vh(px, design?)
  15. * - 宽高皆用 → px2vmin(px, design?) / px2vmax(px, design?)
  16. * - 字体 → px2fluidFontSize(px, options?)
  17. */
  18. /** 设计稿规格:mvp / v2 均为 393×852 */
  19. export type ViewportDesign = 'mvp' | 'v2';
  20. const DESIGN_SPECS: Record<ViewportDesign, { width: number; height: number }> = {
  21. mvp: { width: 393, height: 852 },
  22. v2: { width: 393, height: 852 },
  23. };
  24. /**
  25. * px 转 vw 辅助函数(用于内联样式)
  26. * - 视口宽度 > 500px 时按 500px 封顶
  27. * - 使用 --vw(visualViewport)解决曲面屏问题
  28. *
  29. * @param px 设计稿上的 px 值
  30. * @param design 设计稿规格,默认 'mvp'(393×852)
  31. */
  32. export const px2vw = (px: number, design: ViewportDesign = 'mvp'): string => {
  33. const baseWidth = DESIGN_SPECS[design].width;
  34. return `calc(${px} * min(var(--vw, 1vw) * 100, 500PX) / ${baseWidth})`;
  35. };
  36. /**
  37. * px 转 vh 辅助函数(用于内联样式中的高度)
  38. * 使用 --vh 避免移动端地址栏/工具栏影响
  39. *
  40. * @param px 设计稿上的 px 值
  41. * @param design 设计稿规格,默认 'mvp'(393×852)
  42. */
  43. export const px2vh = (px: number, design: ViewportDesign = 'mvp'): string => {
  44. const baseHeight = DESIGN_SPECS[design].height;
  45. const ratio = (px / baseHeight) * 100;
  46. return `calc(${ratio} * var(--vh, 1vh))`;
  47. };
  48. /**
  49. * px 转最小视口单位(min(vw, vh)),确保任意宽高比下不过度放大
  50. *
  51. * @param px 设计稿上的 px 值
  52. * @param design 设计稿规格,默认 'mvp'
  53. */
  54. export const px2vmin = (px: number, design: ViewportDesign = 'mvp'): string => {
  55. return `min(${px2vw(px, design)}, ${px2vh(px, design)})`;
  56. };
  57. /**
  58. * px 转最大视口单位(max(vw, vh)),确保能填满视口
  59. *
  60. * @param px 设计稿上的 px 值
  61. * @param design 设计稿规格,默认 'mvp'
  62. */
  63. export const px2vmax = (px: number, design: ViewportDesign = 'mvp'): string => {
  64. return `max(${px2vw(px, design)}, ${px2vh(px, design)})`;
  65. };
  66. /**
  67. * px 转流体字体大小(clamp + min(vw, vh))
  68. *
  69. * @param px 设计稿上的 px 值
  70. * @param options 可选:minRatio、maxRatio;design 指定设计稿(默认 'mvp')
  71. */
  72. export const px2fluidFontSize = (
  73. px: number,
  74. options?: { minRatio?: number; maxRatio?: number; design?: ViewportDesign }
  75. ): string => {
  76. const design = options?.design ?? 'mvp';
  77. const { width: baseWidth, height: baseHeight } = DESIGN_SPECS[design];
  78. const minRatio = options?.minRatio ?? 0.8;
  79. const maxRatio = options?.maxRatio ?? 1.2;
  80. const minSize = px * minRatio;
  81. const maxSize = px * maxRatio;
  82. const vwSize = (px / baseWidth) * 100;
  83. const vhSize = (px / baseHeight) * 100;
  84. return `clamp(${minSize}px, min(${vwSize}vw, ${vhSize}vh), ${maxSize}px)`;
  85. };