What caused the 1px problem?

In mobile Web development, the border is set to 1 pixel in the UI design draft. If border:1px appears in the front end during the development process, it will be found in the test that 1px is thicker in Retina models, which is the classic 1px pixel problem in mobile.

For the iphone6, for example, the width of the screen is 375px, and the designer’s artwork is usually 750px, which is 2x. In this case, the designer drew a 1px border on the artwork. 1px border problem arises.

For the designer it’s 1px relative to 750px (physical pixels), for you it’s 1px relative to 375px (CSS pixels), so you should actually border:0.5px.

How to solve it?

plan advantages disadvantages
0.5 px implementation Code is simple, use CSS Older IOS and Android devices are not supported
Border – image Compatible with all current models Changing the color is not convenient
Viewport + REM implementation A set of code for all pages Just like the 0.5px, the model is not compatible
Pseudo-element + transform implementation Compatible with all models Rounded corners are not supported
Box-shadow simulation border implementation Compatible with all models Box-shadow is not in the box model
SVG implementation Simple implementation, can achieve rounded corners Need to learnsvggrammar

0.5 px implementation

.border-1px { border: 1px solid # 999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border-1px { border: 0.5 px. solid # 999}}If DPR =2 and DPR =3, the border is the same
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border-1px { border: 0.333333 px. solid # 999}}Copy the code

However, in IOS7 and below and other systems like Android, 0.5px will be displayed as 0px. So we need to check with JS whether the browser can handle the 0.5px border

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);
}
Copy the code
  • Pros: Simple, no side effects
  • Cons: Support iOS 8+, Android to be compatible

Use a border-image implementation

Based on media query, judge different device pixel ratios to give different border-images:

.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 0stretch; }}Copy the code

Disadvantages: replacing the color requires replacing the picture, the rounded corners are blurred

Viewport + REM implementation

Set the scaling to make the CSS pixels equal to the actual physical pixels.

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);

// Set the root font size
var docEl = document.documentElement; 
var fontsize = 10 * (docEl.clientWidth / 320) + 'px'; 
docEl.style.fontSize = fontsize;

// Use rem in CSS
Copy the code

Disadvantages:

  • The document is modified through JS, so there is a performance impact
  • Will be used for all in the projectremUnit of the object to be affected. If it is an old project, all the CSS styles will be changed (not suitable for old project retrofits)

Pseudo-element + transform implementation

Why pseudo-elements? Because the pseudo-elements ::after or ::before are independent of the current element, they can be scaled independently without affecting the scaling of the element itself

Based on media query, different devices are judged to scale the pixel alignment lines:

.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); }}Copy the code

Note that if you want rounded corners, you need to add border-RADIUS to the pseudo-class as well

Advantages: Good compatibility, no side effects, recommended

Box-shadow simulation border implementation

box-shadow: 0  -1px 1px -1px # 999.1px  0  1px -1px # 999.0  1px  1px -1px # 999, 
            -1px 0  1px -1px # 999;
Copy the code

Disadvantages: Borders are shaded and light in color, compatibility issues as well, Safari does not support box-shadow Settings below 1px.

SVG implementation

Because SVG is vector graphics, 1px corresponds to 1px in physical pixels

You can use postCSs-write-SVG with PostCSS:

@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; 
}
Copy the code

The compiled:

.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; }
Copy the code
  • Advantages: Simple implementation, can achieve rounded corners,
  • Weakness: Need to learnsvggrammar

conclusion

In conclusion, it is recommended to use:

  • Pseudo-element + transform implementation
  • SVG implementation
  • New projects can be triedviewportplan

Solve the 1px problem of mobile Retina display

Three minutes a day, advance one