Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何解决移动端 Retina 屏 1px 像素问题 ? #117

Open
sisterAn opened this issue Jul 20, 2021 · 0 comments
Open

如何解决移动端 Retina 屏 1px 像素问题 ? #117

sisterAn opened this issue Jul 20, 2021 · 0 comments

Comments

@sisterAn
Copy link
Owner

什么导致了 1px 问题?

在移动端 Web 开发中,UI 设计稿中设置边框为 1 像素,前端在开发过程中如果出现 border:1px ,测试会发现在 Retina 屏机型中,1px 会比较粗,即是较经典的移动端 1px 像素问题。

以 iphone6 为例,iphone6 的屏幕宽度为 375px ,设计师做的视觉稿一般是750px ,也就是 2x ,这个时候设计师在视觉稿上画了 1px 的边框,于是你就写了 border:1px ,so...1px边框问题产生了。

对设计师来说它的 1px 是相对于 750px 的(物理像素),对你来说你的 1px 是相对于 375px 的(css像素),实际上你应该是 border:0.5px

如何解决?

方案 优点 缺点
0.5px实现 代码简单,使用css即可 IOS及Android老设备不支持
border-image实现 兼容目前所有机型 修改颜色不方便
viewport + rem 实现 一套代码,所有页面 和0.5px一样,机型不兼容
伪元素 + transform实现 兼容所有机型 不支持圆角
box-shadow模拟边框实现 兼容所有机型 box-shadow不在盒子模型,需要注意预留位置
svg 实现 实现简单,可以实现圆角 需要学习 svg 语法

0.5px 实现

.border-1px { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border-1px { border: 0.5px solid #999 }
}
/* dpr=2 和 dpr=3 情况下 border 相差无几,下面代码可以省略*/
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border-1px { border: 0.333333px solid #999 }
}

但在 IOS7 及以下和 Android 等其他系统里,0.5px 将会被显示为 0px 。所以我们需要通过 JS 检测浏览器能否处理 0.5px 的边框

if (window.devicePixelRatio && devicePixelRatio >= 2) {
  var testElem = document.createElement('div');
  testElem.style.border = '.5px solid transparent';
  document.body.appendChild(testElem);
}
if (testElem.offsetHeight == 1) {
  document.querySelector('html').classList.add('hairlines');
}
  document.body.removeChild(testElem);
}
  • 优点:简单,没有副作用
  • 缺点:支持 iOS 8+,安卓待兼容

使用 border-image 实现

基于 media 查询判断不同的设备像素比给定不同的 border-image

.border-1px{
    border-bottom: 1px solid #000;
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
    .border_1px{
        border-bottom: none;
        border-width: 0 0 1px 0;
        border-image: url(../img/1pxline.png) 0 0 2 0 stretch;
    }
}

缺点:更换颜色需要更换图片,圆角模糊

viewport + rem 实现

通过设置缩放,让 CSS 像素等于真正的物理像素。

const scale = 1 / window.devicePixelRatio;
const viewport = document.querySelector('meta[name="viewport"]');
if (!viewport) {
    viewport = document.createElement('meta');
    viewport.setAttribute('name', 'viewport');
    window.document.head.appendChild(viewport);
}

viewport.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale);

// 设置根字体大小
var docEl = document.documentElement; 
var fontsize = 10 * (docEl.clientWidth / 320) + 'px'; 
docEl.style.fontSize = fontsize;

// 在CSS中用rem单位就行了

缺点:

  • 通过 JS 对文档进行修改,所以性能上有一定影响
  • 会对项目中所有使用 rem 单位的对象进行影响。如果是老项目,则会全部更改 css 样式(不适合老项目改造)

伪元素 + transform 实现

为什么用伪元素? 因为伪元素 ::after::before 是独立于当前元素,可以单独对其缩放而不影响元素本身的缩放

基于 media 查询判断不同的设备像素比对线条进行缩放:

.border-1px:before{
    content: '';
    position: absolute;
    top: 0;
    height: 1px;
    width: 100%;
    background-color: #999;
    transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
    .border-1px:before{
        transform: scaleY(0.5);
    }
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
    .border-1px:before{
        transform: scaleY(0.33);
    }
}

注意如果需要满足圆角,需要给伪类也加上 border-radius

优点:兼容性好,无副作用,推荐使用

box-shadow 模拟边框实现

box-shadow: 0  -1px 1px -1px #999, 
            1px  0  1px -1px #999, 
            0  1px  1px -1px #999, 
            -1px 0  1px -1px #999;

缺点:边框有阴影,颜色浅,同样也有兼容性问题,Safari 不支持 1px 以下的 box-shadow。

svg 实现

因为 svg 是矢量图形,它的 1px 对应的物理像素就是 1px

可以搭配 PostCSSpostcss-write-svg 使用:

@svg border-1px { 
  height: 2px; 
  @rect { 
    fill: var(--color, black); 
    width: 100%; 
    height: 50%; 
    } 
  } 
.svg { 
    border: 1px solid transparent; 
    border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; 
}

编译后:

.svg { border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch; }
  • 优点:实现简单,可以实现圆角,
  • 缺点:需要学习 svg 语法

总结

综上,推荐使用:

  • 伪元素 + transform 实现
  • svg 实现
  • 新项目可以尝试使用 viewport 方案

解决移动端 Retina 屏 1px 像素问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant