Solution to 1px display exception on mobile terminal

preface

In the web development of the mobile terminal, the border is set to 1 pixel in the UI design draft. If the border:1px appears in the development process of the front end, the test will find that in the retina screen model, 1px is relatively thick, which is a classic 1px pixel problem on the mobile terminal.

We should have a certain understanding of the basic concepts of mobile terminals such as viewport, physical pixel, logical pixel, device pixel ratio, css pixel, etc.

Cause

  • Device pixel ratio: dpr=window Devicepixelratio, that is, the ratio of physical pixels to logical pixels of the device.

  • On the mobile phone with retina screen, the dpr is 2 or 3. When the 1px width written in css is mapped to the physical pixel, it has 2px or 3px width.

  • For example, the DPR of iPhone6 is 2, the physical pixel is 750 (x-axis), and its logical pixel is 375. In other words, one logical pixel needs two physical pixels to display in the x-axis and y-axis directions, that is, when dpr=2, it means that one CSS pixel consists of four physical pixels, as shown in the following figure:

Solutions

1. 0.5px scheme

In IOS8+, the apple series already supports 0.5px, which can be handled with the help of media queries.

/*This is css mode*/
.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
/*ios dpr=2 It is almost the same as the border when dpr=3. The following code can be omitted*/
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}

In IOS7 and below and other Android systems, 0.5px will be displayed as 0px. Then we need to find a solution. To be honest, we need to find Hack.

The solution is to check whether the browser can handle 0.5px borders through JavaScript. If it can, add a class to the html tag element.

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);
}
// Script should be placed in body Inside, if it runs inside, it needs to be packed $(document).ready(function() {})

 

Then, the extremely thin border style is easy:
div {
  border: 1px solid #bbb;
}
.hairlines div {
  border-width: 0.5px;  
}
 

Advantages: simple, no need for too much code.
Disadvantages: it is not compatible with Android devices, iOS 7 and below.

2. pseudo class +transform

Principle: remove the border of the original element, and then use: before or: after to redo the border, and reduce the scale of the transform by half. The original element is relatively positioned, and the new border is absolutely positioned.

/*Real one pixel border on mobile phone*/
.border-1px, .border-bottom-1px, .border-top-1px, .border-left-1px, .border-right-1px {
    position: relative;
}

/*Line color black*/
.border-1px::after, .border-bottom-1px::after, .border-top-1px::after, .border-left-1px::after, .border-right-1px::after {
    background-color: #000;
}

/*Bottom border 1 pixel*/
.border-bottom-1px::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    transform-origin: 0 0;
}

/*Top border 1 pixel*/
.border-top-1px::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 1px;
    transform-origin: 0 0;
}

/*Left border 1 pixel*/
.border-left-1px::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 1px;
    height: 100%;
    transform-origin: 0 0;
}

/*Right border 1 pixel*/
.border-right-1px::after {
    content: "";
    box-sizing: border-box;
    position: absolute;
    right: 0;
    top: 0;
    width: 1px;
    height: 100%;
    transform-origin: 0 0;
}

/*Border one pixel*/
.border-1px::after {
    content: "";
    box-sizing: border-box;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border: 1px solid gray;
}


/*Device pixel ratio*/
/*The minimum dpr of the display is 2*/
@media (-webkit-min-device-pixel-ratio: 2) {
    .border-bottom-1px::after, .border-top-1px::after {
        transform: scaleY(0.5);
    }

    .border-left-1px::after, .border-right-1px::after {
        transform: scaleX(0.5);
    }

    .border-1px::after {
        width: 200%;
        height: 200%;
        transform: scale(0.5);
        transform-origin: 0 0;
    }
}

/*Device pixel ratio*/
@media (-webkit-min-device-pixel-ratio: 3)  {
    .border-bottom-1px::after, .border-top-1px::after {
        transform: scaleY(0.333);
    }

    .border-left-1px::after, .border-right-1px::after {
        transform: scaleX(0.333);
    }

    .border-1px::after {
        width: 300%;
        height: 300%;
        transform: scale(0.333);
        transform-origin: 0 0;
    }
}
/*Note that <input type= "button" > does not have: before,: after pseudo elements*/

Advantages: all scenarios can be met, and rounded corners are supported (border radius is required for pseudo classes and ontology classes).
Disadvantages: there is also a large amount of code. For elements that have used pseudo classes (such as clearfix), multiple levels of nesting may be required.

3. viewport + rem

At the same time, by setting the rem benchmark value of the corresponding viewport, you can write 1px as easily and happily as before.
When devicePixelRatio=2, set meta:

<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
 

When devicePixelRatio=3, set meta:

<meta name="viewport" content="width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">

Instance verification:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Mobile terminal 1 px problem</title>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <meta name="viewport" id="WebViewport"
        content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
    <style>
        html {
            font-size: 11px;
        }
        body {
            padding: 1rem;
        }
        * {
            padding: 0;
            margin: 0;
        }
        .item {
            padding: 1rem;
            border-bottom: 1px solid gray;
            font-size: 1.2rem;
        }
    </style>
    <script>
        var viewport = document.querySelector("meta[name=viewport]");
        var dpr = window.devicePixelRatio || 1;
        var scale = 1 / dpr;
        //The following is based on the equipment dpr set up viewport
        viewport.setAttribute(
            "content", +
            "width=device-width," +
            "initial-scale=" +
            scale +
            ", maximum-scale=" +
            scale +
            ", minimum-scale=" +
            scale +
            ", user-scalable=no"
        );

        var docEl = document.documentElement;
        var fontsize = 10 * (docEl.clientWidth / 320) + "px";
        docEl.style.fontSize = fontsize;
    </script>
</head>
<body>
    <div class="item">border-bottom: 1px solid gray;</div>
    <div class="item">border-bottom: 1px solid gray;</div>
</body>
</html>
 

Advantages: all scenarios can be met. A set of code can be compatible with all basic layouts.
Disadvantages: the cost of modifying old projects is too high, and it is only applicable to new projects.

4. border-image

First, prepare a border image that meets your requirements

 

 

 

 

Usually, the page design draft on the mobile phone is doubled. For example, in order to adapt to the iphone retina, the design draft will be designed with a resolution of 750*1334, and the picture will be cut out according to the size of twice. It will not be blurred and very clear when viewed on the mobile phone. Similarly, when using border image, the border is designed as physical 1px, as follows:

Style settings:

.border-image-1px {
    border-width: 0 0 1px 0;
    border-image: url(linenew.png) 0 0 2 0 stretch;
}
 

The border is set at the bottom of the border above, so the image used is 2px high, the upper 1px color is transparent, and the lower 1px uses the color of the border specified visually. If border is required at both the bottom and top of the border.

Style settings:

.border-image-1px {
    border-width: 1px 0;
    border-image: url(linenew.png) 2 0 stretch;
}
 

So far, we have been able to show the effect of 1px border on the iPhone. However, we found that the border cannot be displayed on the non retina screen, so we used Media Query for some compatibility. The style settings are as follows:

.border-image-1px {
    border-bottom: 1px solid #666;
} 

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

Advantages: single or multiple borders can be set without performance bottleneck
Disadvantages: it is troublesome to modify the color, and the picture needs to be replaced; Fillets require special treatment and edges are blurred

5. background-image

The method of background image is the same as that of border image. You should first prepare a picture that meets your requirements.

This example is to set the border to the bottom style setting:

.background-image-1px {
  background: url(../img/line.png) repeat-x left bottom;
  background-size: 100% 1px;
}
 

Advantages: single or multiple borders can be set without performance bottleneck.
Disadvantages: it is troublesome to modify the color, and the picture needs to be replaced; Fillets require special treatment and the edges are blurred.

6. postcss-write-svg

Using border image needs to adjust the image every time, which always requires cost. For the above reasons, we can use the PostCSS write SVG plug-in of PostCSS to help us. If you already use PostCSS in your project, you only need to install this plug-in in your project. Then use in your code:

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

In this way, PostCSS will automatically help you compile CSS:

.example {
    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;
  }
 

This scheme is simple and easy to use, which is what I need. At present, after testing, I can basically meet the requirements I need. In the latest adaptation scheme, I also used this plug-in to deal with the problem of 1px borders.

summary

  • 0.5px. I believe the browser will slowly support it. At present, if it can be used, you can hack it.

  • For old projects, it is recommended to use transform+ pseudo classes.

  • New projects can set the scale value of the viewport. This method has good compatibility.

  • Postcss write SVG is simple and easy to use. It is only suitable for straight lines. It is recommended to use transform+ pseudo class to implement rounded corners.

 


This article is shared here to recommend a front-end official account to friends

Posted by craigerjs on Tue, 31 May 2022 18:22:23 +0530