根本原因是WebKit内核在CPU渲染路径下亚像素插值抗锯齿失效,正确写法需同时满足transform含translateZ(0)且同选择器声明will-change: transform,缺一不可。

transform旋转/缩放动画出现毛边、发虚
根本不是CSS写错了,是WebKit内核(Safari、微信iOS版WebView)在CPU渲染路径下对亚像素做插值时抗锯齿失效。尤其rotate(1deg)、scale(0.99)这类非整数值,或配合overflow: hidden、border-radius使用时,边缘直接糊成一片。
真正起效的写法必须同时满足两个条件:transform里带translateZ(0)(或translate3d(0,0,0)),且同一选择器中声明will-change: transform。缺一不可,顺序也不能颠倒。
- 错误写法:
transform: rotate(45deg); will-change: transform;→ 不触发GPU合成 - 正确写法:
transform: rotate(45deg) translateZ(0); will-change: transform; - 角度务必取整:
Math.round(angle)后再拼进style,避免rotate(37.23deg)这种值 - 若元素含
box-shadow,改用filter: drop-shadow();含border且发虚,优先用伪元素模拟
文字在动画中忽清忽糊
别急着加-webkit-font-smoothing——它只在未升层的静态文字上微调有效,一旦元素进了GPU合成层(比如加了transform),这个属性就彻底失效。
先打开Chrome DevTools → Layers面板,看文字区域是否被标红。如果是,说明已进独立图层,此时模糊根源是纹理采样精度,不是字体渲染策略。
立即学习“前端免费学习笔记(深入)”;
- 检查
translateX/Y值是否含小数:如容器宽201px,translateX(50%)= 100.5px → 强制舍入必糊;应改用calc(50% - 0.5px)或固定整数偏移 - 删掉所有父级上的
backface-visibility: hidden和opacity: 0.99,这两个比-webkit-font-smoothing更容易引发降级 - 容器宽高设为偶数物理像素(可通过
uni.getSystemInfoSync().pixelRatio动态换算),配合transform-origin: 50% 50%确保旋转中心落在像素中心
渐变背景滚动/缩放后色带明显
这不是颜色停靠点没写好,是Safari GPU通道默认用16-bit色深处理渐变,在DPR=2设备上采样跳帧,导致#f5f5f5→#ffffff之间出现灰白断层。
直接在元素上写background: linear-gradient()是重灾区。必须把渐变挪到伪元素,并强制升层。
- 主元素只留
position: relative和background: transparent - 伪元素用
::before,content: ""+inset: 0撑满,再加transform: translateZ(0)和will-change: transform - 避免两个颜色在同一百分比硬切,比如
#ff758c 50%, #ff7eb3 50%→ 改成#ff758c 49.5%, #ff7eb3 50.5% - 禁用
rgba()和hsl()函数色值,全用十六进制,降低采样误差
clip-path锯齿边框真机偏移1px
uni-app的rpx按750设计稿换算,但clip-path: polygon()坐标是逻辑像素,不会随DPR自动缩放。iPhone 12上每个齿宽变成0.5px物理像素,浏览器强制插值模糊。
不能靠transform: scale(2)硬拉,会放大整个元素。必须用设备真实像素比动态修正坐标。
- 在
onLoad里调用uni.getSystemInfoSync().pixelRatio拿到当前DPR - 所有
polygon()坐标值乘以该DPR,比如20rpx齿距 → 实际写成${20 * pixelRatio}px - 用
%替代rpx写宽高基准,比如100%比750rpx更安全 - 锯齿密度控制在每20rpx一个齿,太密会触发微信小程序Canvas降级,导致掉帧
真机调试时最常被忽略的是:DPR不参与clip-path计算,也不影响transform中像素值的渲染精度,但它决定了你写的“1px”到底占几个物理像素——而所有锯齿问题,本质都是物理像素没对齐。


















