原理
先讲重点,我们使用的方案是在淘宝高清方案的基础上修改的方案,因为淘宝高清方案使用 meta 使用 0.5
,这个设定在将移动端页面嵌套到其他平台的 iframe 中,会有内外 viewport 不一致导致的缩放问题,因此我们将 initial-scale 默认为 1。
设置为1也会有内外不一致的问题,只是现在对接的系统多数都写1,跨域部署的时候,没办法获取到,所以这里我暂时没有更好的解法。
我们的实现原理其实很简单,将项目中写的 px 值,通过 postcss 转换成 rem 单位。然后通过动态修改 html 上的 fontsize 大小,来实现在不同的屏幕上 px 的缩放比一致的效果。
在讲的简单一点,比如我们在一个宽度为 50 的屏幕上,看到一个宽度为40的按钮。我们期望在宽度100的屏幕上,看到它时,它的宽度应该是 80.
实现
转换所有的 px 为 rem
实现原理比较简单,就是通过 postcss 插件,匹配正则然后做一个值除与100的转化,比如 32px
转为 0.32rem
。 为什么是除 100 而不是其他的值呢,是为了便于计算和直观表现做的约定。比如 css 文件中我们通过 postcss 转换,而在内联样式中,我们需要手动写明 rem
单位。这时候除与100,就只是简单的小数点缩进。
上面的理由其实还是不够充分说明为什么是 100。这其实还和设备的像素点和像素倍率dpr等知识点有关,这里我不详细的展开,只做简要说明。在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rem =100px = 100物理像素。所以就相当于 iPhone6 上面取一整个屏幕宽度为 7.5rem 。设计师在输出设计稿的时候,只要以 iPhone6 的标准输出,我们就可以直接使用设计师标注的尺寸进行开发了。
我们使用 postcss 转换我们之前使用 esbuild 构建后的样式产物。
安装需要的模块
cd packages/malita pnpm i postcss @alitajs/postcss-plugin-px2rem 复制代码
@alitajs/postcss-plugin-px2rem 是在 postcss-plugin-px2rem 的基础上修改的,我增加了一个配置
selectorDoubleRemList: [/.adm-/, /.ant-/]
被匹配中的样式中 px 会被转化成两倍的 rem,比如32px
转为0.64rem
。 这在使用一些没有使用高清方案编写的组件时非常有用。
修改 packages/malita/src/styles.ts
,以下仅仅摘录了本次的修改。我们将 esbuild 构建后的产物,使用 postcss 再构建一次。
+ import postcss from 'postcss'; + // @ts-ignore + import px2rem from '@alitajs/postcss-plugin-px2rem'; const { errors, warnings, outputFiles } = await esbuild.build( { entryPoints: [args.path], logLevel: 'silent', bundle: true, write: false, charset: 'utf8', minify: true, loader: { '.svg': 'dataurl', '.ttf': 'dataurl', }, } ); + if (errors.length > 0) { + return { + errors, + warnings, + contents: outputFiles![0].text, + loader: 'text', + }; + } + try { + const result = await postcss( + [ + px2rem({ + rootValue: 100, + minPixelValue: 2, + selectorDoubleRemList: [/.adm-/, /.ant-/], + }), + ], + ).process(outputFiles![0].text, { + from: args.path, + to: args.path, + }); + return { + errors, + warnings, + contents: result.css, + loader: 'text', + }; + } catch (error){ + return { + errors, + warnings, + contents: outputFiles![0].text, + loader: 'text', + }; + } 复制代码
运行验证
cd examples/app pnpm dev > malita dev App listening at http://127.0.0.1:8888 复制代码
查看产物文件 examples/app/dist/malita.js
全文搜索 malita-home
,我们将会看到 font-size
被转换成 0.32rem
。
var home_default = ".malita-home{font-size:0.32rem;background:blue;width:1rem}\n"; 复制代码
动态修改 html 的 font-size
新建文件 packages/malita/src/hd.ts
,实现复制了 alita 中的实现,这个在很多文章中都有体现,我这里就列出来的了。 主要需要注意的几个点如下:
1、安卓dpr乱标
2、非淘宝高清方案,默认的 initial-scale 为 1
3、有些兼容环境下, fontSize为100px的时候, 结果1rem=86px; 需要纠正viewport
4、在 iframe 中打开
5、手机横屏需要使用高作为标准而不是宽
6、部分安卓手机转屏之后,需要延迟获取可视宽高
7、横屏模式时手机软件盘键盘弹起事件
在框架中引入 hd 脚本,修改我们的主入口文件,添加 import '/Users/congxiaochen/Documents/malita/packages/malita/lib/hd';
实现很简单就是修改我们之前的新建主入口文件的方法,在返回的 content 的 import 部分增加 import '${path.resolve(__dirname,'hd')}';
。如果你没有想到这里的实现,可能你之前的 generateEntry
的逻辑还没有很理解。
运行验证
cd examples/app pnpm dev > malita dev App listening at http://127.0.0.1:8888 复制代码
使用谷歌浏览器的手机模拟器,Dimensions: iPhone 6/7/8
访问 http://127.0.0.1:8888
,打开控制台查看 elements ,需要关注的信息在 html 的上方
1、 html 上的 style
<html lang="en" style="font-size: 50px;" data-scale="true"> 复制代码
2、meta 信息
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,viewport-fit=cover"> 复制代码
感谢阅读,今天的内容做了部分的简略,因为关于单位都是标准的文档,我就没有再拷贝一份了。你可以简单的理解,使用高清方案的时候,主要就是有 rem 、动态的 fontsize 和 meta 组合控制的,这样当你在项目中遇到整体的适配有误时,你就知道该从这些地方着手调查了。