Preface
Recently, I have been working on mobile development. Before this I have heard something strange but I didn’t pay attention to. Now, I have to figure out though there is a lot of stuff. I am trying to make it clear as possible as I can but I am not sure content below is completely right.
Basic concepts
Css Pixels, Physical Pixels and DPR
First, let’s start with demo:
<div class="cont"> |
* { |
Before switching to device mode, make sure you don’t have code starts with <meta name="viewport" ...
in your head
tag.
Normally, we will find everything seems ok when we switch to mobile. For example, Let’s switch to iPhone6.
According to chrome-devtools, width 375 and height 667 is css pixels
. And before we keep going , we need to figure out some terms first including css pixels
, physical pixels
and DPR
. Here is some related readings:
After reading those stuff, I am still confused. But I can figure something out at least. Well, here is the point based on iPhone6:
width 375 and height 667 is
css pixels
DPR
is 2DPR
=physical pixels
/css pixels
. Sophysical pixels
is 750*1334
Layout Viewport and Visual Viewport
Ok, let’s keep going. If you calculate the dimensions of viewport, you will get some strange values like below:
document.documentElement.clientWidth //980 |
Maybe you thought that the value should be 375*667 or 750*1334. However, it doesn’t. The result 980*1743 is the dimensions of layout viewport
. Well, before keep going we need to know something about layout viewport
and visual viewport
. Here is the related articles:
Based on the articles above we can be sure of something about layout viewport
and visual viewport
.
layout viewport
is the area that the browser uses to calculate the dimensions of elements with percentual width.visual viewport
is the part of the page that’s currently shown on-screen. The user may scroll to change the part of the page he sees, or zoom to change the size of thevisual viewport
.Zoom doesn’t change the size of
layout viewport
butvisual viewport
.The size of
visual viewport
<= the size oflayout viewport
.Both
layout viewport
andvisual viewport
are measured bycss pixels
.Many mobile browsers initially show any page in fully zoomed-out mode to show the complete content of the site. Thus
layout viewport
is equal to thevisual viewport
at that moment.
And in this case of iPhone6, here is something we can be sure of:
layout viewport
is 980*1743visual viewport
is also 980*1743The mobile browser show the page in fully zoomed-out mode to show the complete content of the site.
Meta Tag
Now, we know what happened when we switch to mobile mode. Also, we know why the size of font becomes smaller in visual.
However, we would want the layout viewport
to be 375*667 or 750*1334 if we want to build a adaptive website. Fortunately, we can control the size of layout viewport
by tag meta
. For instance,
<meta name="viewport" content="width=375,initial-scale=1,user-scalable=no"> |
You can find the doc of meta
in mdn-meta and viewport
in mdn-viewport_meta_tag. In the code above, we define 375px for the width
of layout viewport
. Here, 375px is css pixels
. Also, you can prove it by:
document.documentElement.clientWidth //375 |
So, any change? Yes. The change is the font-size is bigger in visual than before. Before we change the text is too small to read.
We can keep testing by change the width
in meta
. For example, let’s try 750:
<meta name="viewport" content="width=750,initial-scale=1,user-scalable=no"> |
Look what we got:
This makes sense. However, if you remove user-scalable=no
in the meta
the browser will again enter into the fully zoomed-out mode. In that case, you will find the font-size is smaller and the whole page is showed in the screen which make the effect of initial-scale=1
disappear.
To be compatible with mobiles with different sizes we need to switch the number to device-width
:
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"> |
It is easy to understand, I think. Until now, it seems that we can be compatible with mobiles. Is that true?
Adaptive Layout
In the case above, we set width:50%
for the .left
and .right
box. So, they could be adaptive. If we use px
the page will not be adaptive because the size of viewport varies between mobiles. So, we have two choices
- Use different size according to the size of viewport
- Use percentage always
Option2 will work if our page is simple enough. In most instances, it will bring a disaster because of nested elements. One modification in parent will bring changes to all descendant elements.
The problem of option1 is if we can avoid writing so many media queries codes to adapt to different sizes of viewport. For example:
/* ... other codes for .cont with different sizes */ |
Actually, we can avoid these codes. The key is rem
. For example, code above can be simplified like:
html { |
When the size of viewport is 400px, we just need to update the font-size
of html
with 40px
. That is easy with js. Seems wonderful with rem
? huh?
You can use rem
everywhere and js just run one time. Here is the related lib lib-flexible.
Latest Adaptive Layout
Basically, there is no big problem with rem
. Well, you might realize that we can totally use vw
instead. That’s correct! The compatibility has become better for most developers to use. The same thing happen to flex
and grid
layout. Until 2018.6, flex
is a choice but we still need to wait for grid
or look for some polyfill.
However, there is still some little problems when using vw
or rem
layout.
- We still use
px
forfont-size
andborder
.
I think it is easy to understand because decimals would appear. In that case, we may get 15px
or 17px
instead of 16px
. At the same time, border
may disappear because of some numbers like 0.9px
.
- Develop with plugins
For instance, if we are developing based on iPhone6 whose width is 375px in css pixel while the designer gives us a PSD whose width is 750px, how we are going to do? Calculate the div with 75px and transform to 10% and write 10vw? So we calculate and write it one by one? That’s terrible.
We can just write 75px and other numbers and leave the transform jobs to plugins(e.g. less
,postcss
.etc.).
Problems Caused by DPR
Blurry or Sharp in Different DPR Device
Until now, we haven’t talked about DPR which we mentioned in the beginning. As mdn-devicepixelratio says:
In simpler terms, this tells the browser how many of the screen’s actual pixels should be used to draw a single CSS pixel.
On the desktop or before the appearance of Retina display, DPR is always equal to 1. However, things became complicated after that. At present, there is a lot of DPR on the market which varies from 1 to 4. Sometimes, there comes 1.5 or 2.75. Anyway, we will talk about it later.
We can always get DPR by window.devicePixelRatio
. The problem is that on retina screens or device whose DPR more than 1 images will become blurry. Of course we will try to avoid that. So, we have to figure out what happened to those mobiles with bigger DPR.
According to towards-retina-web,
A bitmap pixel is the smallest unit of data in a raster image (PNG, JPG, GIF, etc). Each pixel contains information on how it is to be displayed, including its position in the image’s coordinate system and its color.
Beside its raster resolution, an image on the Web has an abstract size, defined in CSS pixels. The browser squeezes or stretches the image based on its CSS height or width during the rendering process.
When a raster image is displayed at full size on a standard-density display, 1 bitmap pixel corresponds to 1 device pixel, resulting in a full-fidelity representation. Because a bitmap pixel can’t be further divided, it gets multiplied by four on Retina displays(assuming DPR is 2) to preserve the same physical size of the image, losing detail along the way.
For example, we got iPhone6 whose DPR is 2. And img we have is:
<img src="example.png" width="2" height="2"> |
Assume that size of example.png is 2*2 and the picture is like:
With DPR of 2, the browser has to use 2 device pixels to draw a single css pixel. So img
with 2*2 css pixels will be drew by 4*4 device pixel which means example.png with 2*2 bitmap pixels will be drew by 4*4 device pixel. So, 4 bitmap pixels of example.png will be drew by 16 device pixels which means 1 bitmap pixel will be drew by 4 device pixels.
Let’s move on, normally what we want might be:
However, actual result will be:
The result happens because 1 bitmap pixel can’t be further divided into 4 device pixels. That’s what @smashingmagazine says:
A bitmap pixel is the smallest unit of data in a raster image (PNG, JPG, GIF, etc).
Because a bitmap pixel can’t be further divided, it gets multiplied by four on Retina displays(assuming DPR is 2) to preserve the same physical size of the image, losing detail along the way.
You can also find another example in mdn-devicepixelratio.
So, how can we fix this? Basically, choices we have are listed as below:
- Change the size of
img
, for example:
<img src="example.png" width="1" height="1"> |
In this case, img
with 1*1 css pixel will be drew by 2*2 device pixels which means example.png with 2*2 bitmap pixels will be divided by 2*2 device pixels. Hence, 1 bitmap pixel corresponds to 1 device pixel.
We might not use this alone because the layout would be affected.
- Replace example.png with 4*4 and keep the original
img
with 2*2 css pixels, same explanation as above.
In this case, we might do this by changing the src
attribute in the img
. For example, update the src
search param ?v=2*2
with ?v=4*4
or change from example.png to example@2x.png. retinajs will help you do this job.
Use icon-font instead of raster image.
Use svg instead.
Actually, what you need to care about is not only img
but also background-image
. So, option1 and option2 will be not convenient without any other help(e.g. retinajs).
Also, with DPR smaller than 2 we need to switch size back. If not, image will become sharp which is the opposite process of blur. For instance:
Compare with the process of blur:
Option3 and option4 would be easier. And with the help of iconfont, things can be easier and easier when you choose svg.
Personally, I would choose option4 in daily work. For more information about the options, click towards-retina-web.
Problems about Border
Assume that we are using iPhone6 whose DPR is 2, border with 1 css pixel will be drew with 2 device pixels. Does that matter?
Yeah, sometimes that make the border thicker in visual. When we set 1px on border, actually what we might want is to set the thinnest border that device support. Normally, on desktop browser 1px is the thinnest. So in this case, you might need to talk with designer if he wants the thinnest border(e.g. 0.5px) or still 1px.
Anyway, there is lots of ways if we want to set the thinnest border. So, I wrote another blog to talk about that. Please check border on mobile.
Ending
I have written too much about the basic knowledge about layout on mobile. However, there are still lots of problems I haven’t mentioned. Anyway, I will write them in other blogs.
Reference
what-exactly-is-device-pixel-ratio
converting-between-physical-pixels-and-css-pixels
Chinese: