preface

With the increasing number of mobile web projects, UI designers often have higher and higher requirements for pixels in the development process. 1px in Retina screen is much thicker than that in Web, because 1px in CSS is not equal to 1px in mobile devices, because different mobile phones have different pixel densities.

What is the problem with 1px pixels on mobile

For example, the screen width of iphone6 is 375px, and the designer’s visual image is usually 750px, i.e. 2x image. The designer draws a 1px border on the design. Let’s take a look at the problems

div { width: 200px; height: 200px; } .box1 { border-top: 1px solid #000; } .box2 { position: relative; border-top: 1px solid #000; } <div class="box1">1px top box </div>Copy the code

Effect:

So why did this change happen?

The reason:

  • inretinaScreen on my phone,dprfor2or3.cssWriting in the1pxThe width is mapped to a physical pixel2pxor3pxWidth.
  • iPhone6thedprfor2, the physical pixel is750(X-axis), its logical pixels are375. In other words, 1 logical pixel atxAxis andyIn the axial direction, two physical pixels are required for display. That is, when DPR =2, one CSS pixel consists of four physical pixels.

Maybe a lot of people don’t understand what DPR is, what is a physical pixel, what is a logical pixel

In fact, devicePixelRatio: DPR = window.devicepixelratio, which is the ratio of physical pixels to logical pixels of the device

Let’s talk about what a physical pixel is and what a logical pixel is (boring stuff)

Physical pixel

Physical pixels refer to the number of pixels, not the specific physical size. The display is made up of physical pixels that are fixed from the day the screen comes out of the factory, and the color of each pixel is controlled to make the screen display a different image.

When we talk about resolution, we mean physical pixels. For example, the iphone6s plus has a resolution of 1920×1080, which means 1920 physical pixels horizontally and 1080 vertical.

Logical pixels (device-independent pixels)

A device-independent pixel is a virtual, device-independent, logical unit of length that is used by the underlying system’s programs and converted to a physical pixel by the related system.

A visual representation of the px in CSS. Device independent pixels = logical pixels = CSS pixels

What is the use of device-independent pixels?

For example, the iphone3 and iphone4 are both 3.5 inches, but the iphone3 has a 320×480 resolution and the iphone4 has a 640×960 resolution, which means the same screen length, the iphone3 has 320 physical pixels, The iphone4 has 640 physical pixels.

If we lay it out with real physical pixels, say 320 physical pixels, the phone with 640 physical pixels will be half empty. To avoid this problem, virtual pixel units (device-independent pixels) are created.

We agree that iphone3 and iphone4 both have 320 virtual pixels, except that on iphone3, one virtual pixel eventually translates to one physical pixel, and on iphone4, one virtual pixel eventually translates to two physical pixels.

One virtual pixel is converted into several physical pixels, which we call the device pixel ratio.

prompt

The iPhone6 has 750 x 1334 resolution (physical pixels), 375 x 667 device-independent pixels, and a device pixel ratio of 2.

The iPhone6 plus has a resolution of 1242 x 2208 (physical pixels), device-independent pixels of 414 x 736, and a device-pixel ratio of 3.

The solution

Zoom using CSS pseudo-elements ::after + transfrom

Implement all borders

/* all frames */. Box3 {position: relative; } .box3::after { content: ''; position: absolute; box-sizing: border-box; top: 0; left: 0; width: 200%; height: 200%; border: 1px solid #000; border-radius: 4px; The transform: scale (0.5); transform-origin: left top; } .box4 { margin-top: 20px; border: 1px solid #000; } <div class="box3">0.5px all borders </div> <div class="box4"> 1px all borders </div>Copy the code

Transform-origin: left top; , otherwise there will be a blur effect.

The effect is as follows:

Implement single upper border:

.box2 { position: relative; /* border-top: 1px solid #000; Box2 ::after {content: ''; box-sizing: border-box; position: absolute; top: 0; left: 0; width: 200px; height: 1px; background-color: #000; The transform: scaleY (0.5); transform-origin: left top; } <div class="box1">1px top box </div>Copy the code

The effect is as follows:

Open source component library solution

Ant – design – mobile component library

@mixin scale-hairline-common($color, $top, $right, $bottom, $left) { content: ''; position: absolute; display: block; z-index: 1; top: $top; right: $right; bottom: $bottom; left: $left; background-color: $color; } @mixin hairline($direction, $color: #000, $radius: 0) { @if $direction == top { border-top: 1px solid $color; @media (min-resolution: 2dppx) {border-top: none; // Min-resolution: 2dppx; &::before { @include scale-hairline-common($color, 0, auto, auto, 0); width: 100%; height: 1px; transform-origin: 50% 50%; The transform: scaleY (0.5); @media (min-resolution: 3dppx) {transform: scaleY(0.33); } } } } @else if $direction == right { border-right: 1px solid $color; @media (min-resolution: 2dppx) { border-right: none; &::after { @include scale-hairline-common($color, 0, 0, auto, auto); width: 1px; height: 100%; background: $color; transform-origin: 100% 50%; The transform: scaleX (0.5); @media (min-resolution: 3dppx) {transform: scaleX(0.33); } } } } @else if $direction == bottom { border-bottom: 1px solid $color; @media (min-resolution: 2dppx) { border-bottom: none; &::after { @include scale-hairline-common($color, auto, auto, 0, 0); width: 100%; height: 1px; transform-origin: 50% 100%; The transform: scaleY (0.5); @media (min-resolution: 3dppx) {transform: scaleY(0.33); } } } } @else if $direction == left { border-left: 1px solid $color; @media (min-resolution: 2dppx) { border-left: none; &::before { @include scale-hairline-common($color, 0, auto, auto, 0); width: 1px; height: 100%; transform-origin: 100% 50%; The transform: scaleX (0.5); @media (min-resolution: 3dppx) {transform: scaleX(0.33); } } } } @else if $direction == all { border: 1px solid $color; border-radius: $radius; @media (min-resolution: 2dppx) { position: relative; border: none; &::before { content: ''; position: absolute; left: 0; top: 0; width: 200%; height: 200%; border: 1px solid $color; border-radius: $radius * 2; transform-origin: 0 0; The transform: scale (0.5); box-sizing: border-box; pointer-events: none; }}}} // remove border @mixin hairline-remove($position: all) {@if $position == left {border-left: 0; &::before { display: none ! important; } } @else if $position == right { border-right: 0; &::after { display: none ! important; } } @else if $position == top { border-top: 0; &::before { display: none ! important; } } @else if $position == bottom { border-bottom: 0; &::after { display: none ! important; } } @else if $position == all { border: 0; &::before { display: none ! important; } &::after { display: none ! important; }}}Copy the code

vant

.hairline-common() { position: absolute; box-sizing: border-box; content: ' '; pointer-events: none; } .hairline(@color: @border-color) { .hairline-common(); top: -50%; right: -50%; bottom: -50%; left: -50%; border: 0 solid @color; The transform: scale (0.5); }Copy the code

recommended

More details can be found at juejin.cn/post/684490…