preface

Recently, while writing a mobile H5 application, I came across a point worth recording. Now from its origin to implementation, let’s talk about mobile 1px. 1px is not accurate enough. It should be 1 physical pixel.

By reading the following passage, you will understand the following questions:

The problem

  • Why 1px?
  • What are the ways to implement 1px? What are the pros and cons of these approaches?
  • Which solutions are used in open source projects?
  • How to deal with 1px issues in a project?

origin

The basic concept

First of all, we need to understand two concepts, one is pixel or PX, and the other is device pixel ratio (DPR)

Pixel: Refers to the smallest unit of pixels in an image represented by a sequence of numbers that cannot be divisible again. Device Pixel ratio (DPR) : Device pixel ratio = device pixel/device independent pixel.Copy the code

So let me just explain a couple of concepts

  • CSS pixel (virtual pixel) : Refers to the logical pixel used in CSS style code. In the CSS specification, units of length can be divided into two types: absolute and relative units. Px is a relative unit, relative to the device pixel.
  • Device pixel (physical pixel) : Refers to the smallest physical unit that the device can control the display. It refers to the points on the display. From the day a screen is manufactured in a factory, the pixels on the device are fixed, depending on the size of the screen.
  • Device-independent pixel (logical pixel) : Can be thought of as a point in a computer coordinate system that represents a virtual pixel (e.g. CSS pixel) that can be used by a program. This point has no fixed size. The smaller the pixel, the clearer the pixel, and is then converted into a physical pixel by the relevant system.

That is, when the logical pixel is 1pt, it will display 2px physical pixels on a device with a DPR of 2

Reference data

Various types of iPhone screen device parameters

Note: The scale factor here is the value of DRP

Design draft comparison data

One might wonder, why is it 750×1334, because it’s a physical pixel

The pixels in our CSS are logical pixels 375x 667, with a custom width of 375px when writing the code

The 1px width on the design should actually represent a CSS parameter of 0.5px corresponding to a physical pixel of 1px. How to achieve a physical pixel of 1px

practice

Ultimately, there are two solutions. One is to use CSSTransfrom: scaleY (0.5), the other is to set the media query to scale according to different DPR

Solution 1

The principle of

Make use of CSS pseudo-elements::after + transfromTo zoom in

Why pseudo-elements? Because the pseudo-element ::after or ::before is independent of the current element, it can be scaled independently without affecting the element’s own scaling

Pseudo-element Single quotes are also used by default in most browsers, in the same form as pseudo-classes, and single quote compatibility (IE) is better

implementation

<div class="cell border-1px"> cell <div>

<style>
.cell {
    width: 100px;
    height: 100px;
}
<!--All borders-->
.border-1px:after {
    content: ' ';
    position: absolute;
    box-sizing: border-box;
    top: 0;
    left: 0;
    width: 200%;
    height: 200%;
    border: 1px solid # 000;
    border-radius: 4px;
    -webkit-transform: scale(0.5);
    transform: scale(0.5);
    -webkit-transform-origin: top left;
}

<!--Single frame, take the upper frame as an example-->
.border-1px-top:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    border-top: 1px solid red;
    transform: scaleY(5);transform-origin: left top;
}
</style>

Copy the code

Solution 2 (Upgrade Solution 1)

The principle of

Use less to encapsulate the common code (scheme 1), and increase the media query for different DPR devices, for different scaling

.border(
    @borderWidth: 1px; 
    @borderStyle: solid; 
    @borderColor: @lignt-gray-color; 
    @borderRadius: 0) {
    position: relative;
    &:before {
        content: ' ';
        position: absolute;
        width: 98%;
        height: 98%;
        top: 0;
        left: 0;
        transform-origin: left top;
        -webkit-transform-origin: left top;
        box-sizing: border-box;
        pointer-events: none;
    }
    @media (-webkit-min-device-pixel-ratio: 2) {
        &:before {
            width: 200%;
            height: 200%;
            -webkit-transform: scale(.5); }}@media (-webkit-min-device-pixel-ratio: 2.5) {
        &:before {
            width: 250%;
            height: 250%;
            -webkit-transform: scale(.4); }}@media (-webkit-min-device-pixel-ratio: 2.75) {
        &:before {
            width: 275%;
            height: 275%;
            -webkit-transform: scale(1 / 2.75); }}@media (-webkit-min-device-pixel-ratio: 3) {
        &:before {
            width: 300%;
            height: 300%;
            transform: scale(1 / 3);
            -webkit-transform: scale(1 / 3); }}.border-radius(@borderRadius);
    &:before {
        border-width: @borderWidth;
        border-style: @borderStyle;
        border-color: @borderColor; }}.border-all(
	@borderWidth: 1px; 
	@borderStyle: solid; 
	@borderColor: @lignt-gray-color; 
	@borderRadius: 0) {
    .border(@borderWidth; @borderStyle; @borderColor; @borderRadius);
}
Copy the code

Other schemes:

  • Use pictures: compatibility best, flexible line worst, cannot change color, length
  • useviewportrem.jsThe dynamic changeviewportscaleThe downside of scaling is that it does not work for existing projects, such as usingvhvwThe layout of the
        <meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    Copy the code
  • Using CSS gradientslinear-gradientorbox-shadow

The above three schemes have fatal defects and are not recommended for use

compatibility

Finally, how about compatibility, mainly pseudo-elements, transform:scale and min-device-Pixel-ratio

Open source library solutions

Vant component library

Jump to Github to see the code

Written in less


.hairline-common() {
  position: absolute;
  box-sizing: border-box;
  content: ' ';
  pointer-events: none;
}

.hairline(@color: @border-color) {
  .hairline-common(a);top: -50%;
  right: -50%;
  bottom: -50%;
  left: -50%;
  border: 0 solid @color;
  transform: scale(0.5);
}
Copy the code

The first solution is also adopted

Ant – design – mobile component library

Jump to Github to see the code

.scale-hairline-common(@color.@top.@right.@bottom.@left) {
  content: ' ';
  position: absolute;
  background-color: @color;
  display: block;
  z-index: 1;
  top: @top;
  right: @right;
  bottom: @bottom;
  left: @left;
}

.hairline(@direction.@color: @border-color-base) when (@direction = 'top') {
  border-top: 1PX solid @color;

  html:not([data-scale]) & {
    @media (min-resolution: 2dppx) {
      border-top: none;

      &::before {
        .scale-hairline-common(@color.0, auto, auto, 0);
        width: 100%;
        height: 1PX;
        transform-origin: 50% 50%;
        transform: scaleY(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleY(0.33);
        }
      }
    }
  }
}

.hairline(@direction.@color: @border-color-base) when (@direction = 'right') {
  border-right: 1PX solid @color;

  html:not([data-scale]) & {
    @media (min-resolution: 2dppx) {
      border-right: none;

      &::after {
        .scale-hairline-common(@color.0.0, auto, auto);
        width: 1PX;
        height: 100%;
        background: @color;
        transform-origin: 100% 50%;
        transform: scaleX(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleX(0.33);
        }
      }
    }
  }
}
.hairline(@direction.@color: @border-color-base) when (@direction = 'bottom') {
  border-bottom: 1PX solid @color;
  html:not([data-scale]) & {
    @media (min-resolution: 2dppx) {
      border-bottom: none;
      &::after {
        .scale-hairline-common(@color, auto, auto, 0.0);
        width: 100%;
        height: 1PX;
        transform-origin: 50% 100%;
        transform: scaleY(0.5);
        @media (min-resolution: 3dppx) {
          transform: scaleY(0.33);
        }
      }
    }
  }
}

.hairline(@direction.@color: @border-color-base) when (@direction = 'left') {
  border-left: 1PX solid @color;

  html:not([data-scale]) & {
    @media (min-resolution: 2dppx) {
      border-left: none;

      &::before {
        .scale-hairline-common(@color.0, auto, auto, 0);
        width: 1PX;
        height: 100%;
        transform-origin: 100% 50%;
        transform: scaleX(0.5);

        @media (min-resolution: 3dppx) {
          transform: scaleX(0.33);
        }
      }
    }
  }
}

.hairline(@direction.@color: @border-color-base.@radius: 0) when (@direction = 'all') {
  border: 1PX solid @color;
  border-radius: @radius;

  html:not([data-scale]) & {
    @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;
        transform: scale(0.5);
        box-sizing: border-box;
        pointer-events: none;

        // @media (min-resolution: 3dppx) {
        // width: 300%;
        // height: 300%;
        // border-radius: @radius * 3;
        / / the transform: scale (0.33);
        // }}}}}Copy the code

This is worth investigating, and it is a little different from vant and the first solution, mainly because it handles two cases of DPR of 2 and 3, and is more perfect than vant.

This is capitalized PX to prevent the plugin from converting PX to REM units

conclusion

If this article solves any of your problems or problems at work, please click the “like” button.

Due to the limited technical level, if there is any mistake in the article, please point out in the comment area, thank you!

Next, I will summarize the mobile terminal H5 layout and some tramps, so PLEASE pay attention to it.


Update: H5 layout issues on mobile have been completed

Update: The MOBILE H5 pit series is now complete