Element Query
合理的单位搭配
Absolute Size & Percent:绝对尺寸与百分比尺寸
在移动端开发中,笔者最早是倾向于用百分比布局,这样相较于传统的绝对布局肯定是具有一定灵活性,
Responsive Size:响应式尺寸
Media Query:媒介查询
>
Media Query By SCSS
Responsive Menu
老实说,笔者一直觉得响应式开发最成功的应用就是菜单了,这里介绍简单的响应式菜单的开发方式,借鉴了W3School的教程,首先,HTML 的元素布局为:
<ul class="topnav">
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
<li class="icon">
<a href="javascript:void(0);" onclick="myFunction()">☰</a>
</li>
</ul>
然后我们可以添加基本的样式:
/* Remove margins and padding from the list, and add a black background color */
ul.topnav {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
/* Float the list items side by side */
ul.topnav li {float: left;}
/* Style the links inside the list items */
ul.topnav li a {
display: inline-block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
transition: 0.3s;
font-size: 17px;
}
/* Change background color of links on hover */
ul.topnav li a:hover {background-color: #111;}
/* Hide the list item that contains the link that should open and close the topnav on small screens */
ul.topnav li.icon {display: none;}
然后通过 Media Query 进行不同的尺寸下的样式设置:
/* When the screen is less than 680 pixels wide, hide all list items, except for the first one ("Home"). Show the list item that contains the link to open and close the topnav (li.icon) */
@media screen and (max-width:680px) {
ul.topnav li:not(:first-child) {display: none;}
ul.topnav li.icon {
float: right;
display: inline-block;
}
}
/* The "responsive" class is added to the topnav with JavaScript when the user clicks on the icon. This class makes the topnav look good on small screens */
@media screen and (max-width:680px) {
ul.topnav.responsive {position: relative;}
ul.topnav.responsive li.icon {
position: absolute;
right: 0;
top: 0;
}
ul.topnav.responsive li {
float: none;
display: inline;
}
ul.topnav.responsive li a {
display: block;
text-align: left;
}
}
对于实际的效果的例子在这里
Flexible Units:灵活的尺寸单位
Viewport Size:基于 Viewport 的单位
1vw = 1% of viewport width
1vh = 1% of viewport height
1vmin = 1vw or 1vh, whichever is smaller
1vmax = 1vw or 1vh, whichever is larger
总而言之,就笔者目前的认知而言,虽然在下文中也会介绍几种关于 Viewport Size 的 Polyfill,不过总体而言笔者还不是很建议现在就大范围地使用 Viewport Size。
viewport-units-buggyfill
vminpoly
整体而言该 Polyfill 的完善度与社区活跃度皆不如上者,建议有需要的还是参考下上面那个 Polyfill。
FontSize:字体
>
Fixed Size
To avoid mobile browsers (iOS Safari, et al.) from zooming in on HTML form elements when a ` drop-down is tapped, add
font-size` to the selector rule:
input[type="text"],
input[type="number"],
select,
textarea {
font-size: 16px;
}
Flexible Size
The type font size in a responsive layout should be able to adjust with each viewport. You can calculate the font size based on the viewport height and width using :root
:
:root {
font-size: calc(1vw + 1vh + .5vmin);
}
Now you can utilize the root em
unit based on the value calculated by :root
:
body {
font: 1em/1.6 sans-serif;
}
:root {
font-size: calc(1vw + 1vh + .25vmin);
}
body {
font: 1em/1.75 "Open Sans", sans-serif;
}
.container {
padding: 0 1em
}
code {
background: #eee;
border-radius: 3px;
}
em {
background: #ffeb3b;
font-style: normal;
}
code,
em {
padding: .1em .2em;
}
合理的单位搭配
在响应式开发中,很多人都会建议不要使用 px 这种绝对值作为尺寸,不过这也是因人而异的,过度的使用相对值也会导致开发的复杂度与不可预测性的增加。老实说,最傻瓜的开发方案就是在一套视觉稿基础上使用绝对值开发,然后使用下文介绍的按比例缩放的技巧去适应各个屏幕。
For layout type things like the sizes of boxes, you want to use % because you will typically have several columns sized as a percentage of their parent that will stack on top of each other at a certain breakpoint (width:100%). No other unit will allow you to fill 100% of the space like % does. But with the min-height, max-height, min-width, max-width CSS keys.
For padding/margins use em, normally you will want to space your elements out relative to the size of your text. With em (the with of an ‘M’ character) you can quite easily say I want approximately 1 character spacing here.
For borders you can use px or em, there is a difference though. If you want your border to look like it’s one pixel wide on all devices, use 1px. It may not be one pixel on all devices however, high density displays convert 1px into 2px for example. If you want your border to be a size based on your font, use em.
dabblet
For fonts use em (or %), the use of em carries through parents to children and it just a nicer unit to work with over px.
For the next generation
vw and vh. The vw is 1/100th of the window’s width and the vh is 1/100th of the window’s height. For responsiveness they are going to be the new units.
在阅读本文的时候,反而希望你能先忘却关于 CSS、Web 开发那些你已经知道的东西,我们今天讨论的并不是多么复杂深奥的内容,如果你觉得准备好了那我们可以从下面这个简单的点图开始:
上面这些点分布的有些随意,分分合合,有近有远,我们的问题就是如何将这些点划分入到五个组中。最简单的,我们可以在那些相距较远的两个点之间设置为分隔区划分到不同的组合中。
上面这几个圈都是我随手画出来的,你当然可以选择其他的划分方式,譬如将最右边的两个点划分到一个分组中。其实这个问题并无所谓错误答案,不过如果你以如下方式划分的话:
看上去是不是觉得怪怪的?我问这个问题也不是无中生有,当我们需要为不同尺寸的屏幕设置不同的 CSS 样式稿时,会有人喜欢按照最常见的尺寸作为分割点,即 320px,768px 与 1024px。
不知道你有没有听过或者说过下面这些话:中等屏幕的话是不是按照 768px 来划分?还是应该把 768px 也划分到中等屏幕的范围内?不过这个尺寸是 iPad 横屏状态下的尺寸,应该算是大屏幕了吧?唔那大屏幕就是 768px 和以上尺寸咯?然后 320px 左右的是小屏幕?那 319px 算啥?区分蚂蚁的吗?本文的主旨即使讨论如何选择合适的分割点与分隔组。
选择合适的分隔点
你在幼儿园里就会画上面的这些圆吧,我现在用矩形度量来详细阐述下:
我们在这里选择了 600px,900px,1200px 以及 1800px 作为分割点,这些分隔组包含了最常见的 14 个机型:
我们把这两张图合并下,可以得出下面这个更适合你的老板、设计师、开发者以及测试人员看的一张图:
用更直观的方式命名你的分组
你愿意的话,也可以使用Papa-Bear 与 Baby-Bear来称呼你选定的分割点。不过当你和设计师一起讨论网站在不同屏幕上的展示效果时,肯定希望双方都能够在脑海中形成感性直观的认知。如果你用平板竖屏尺寸来形容的话,那到底是 iPad 还是 Surface 呢?特别是现在这种手机越来越大,平板越来越小的情况,你很难用单纯的平板或者手机来划分尺寸。不过好消息是苹果已经不做新产品了,他们只是不断地将按钮、耳机口从现在的产品中移除。这边我也不好给出什么建议,只能说设计师和产品之间需要多多沟通。
声明式的设置响应式布局
声明式的概念在前端很是流行,譬如著名的 React 就是典型的声明式组件库。声明式编程应用到 CSS 中即是 CSS 应当定义 What it wants,而不是 How it should。我们上面讨论过的一个关于分割点的容易混淆之处就是分割点同时代表了某个范围。譬如$large:600px
这种定义在large
这个词本身代表一个范围值的时候就会混淆。因此在具体的某个组件或者标签中,我们应该对其隐藏具体的尺寸设置,譬如:
@mixin for-phone-only {
@media (max-width: 599px) { @content; }
}
@mixin for-tablet-portrait-up {
@media (min-width: 600px) { @content; }
}
@mixin for-tablet-portait-only {
@media (min-width: 600px) and (max-width: 899px) { @content; }
}
@mixin for-tablet-landscape-up {
@media (min-width: 900px) { @content; }
}
@mixin for-tablet-landscape-only {
@media (min-width: 900px) and (max-width: 1199px) { @content; }
}
@mixin for-desktop-up {
@media (min-width: 1200px) { @content; }
}
@mixin for-desktop-only {
@media (min-width: 1200px) and (max-width: 1799px) { @content; }
}
@mixin for-big-desktop-up {
@media (min-width: 1800px) { @content; }
}
// usage
.my-box {
padding: 10px;
@include for-desktop-up {
padding: 20px;
}
}
你会发现在上面的定义中我很推荐使用-up
与-only
后缀,在具体使用样式的地方:
.phone-only {
@include for-phone-only { background: purple; }
}
.tablet-portait-only {
@include for-tablet-portait-only { background: purple; }
}
.tablet-portrait-up {
@include for-tablet-portrait-up { background: purple; }
}
.tablet-landscape-only {
@include for-tablet-landscape-only { background: purple; }
}
.tablet-landscape-up {
@include for-tablet-landscape-up { background: purple; }
}
.desktop-only {
@include for-desktop-only { background: purple; }
}
.desktop-up {
@include for-desktop-up { background: purple; }
}
.big-desktop-up {
@include for-big-desktop-up { background: purple; }
}
不过这种方式在需要大量自定义媒介查询搭配的时候就显得不是那么灵活,我们可以提供更加细粒度的控制方式:
@mixin for-size($size) {
@if $size == phone-only {
@media (max-width: 599px) { @content; }
} @else if $size == tablet-portrait-up {
@media (min-width: 600px) { @content; }
} @else if $size == tablet-portait-only {
@media (min-width: 600px) and (max-width: 899px) { @content; }
} @else if $size == tablet-landscape-up {
@media (min-width: 900px) { @content; }
} @else if $size == tablet-landscape-only {
@media (min-width: 900px) and (max-width: 1199px) { @content; }
} @else if $size == desktop-up {
@media (min-width: 1200px) { @content; }
} @else if $size == desktop-only {
@media (min-width: 1200px) and (max-width: 1799px) { @content; }
} @else if $size == big-desktop-up {
@media (min-width: 1800px) { @content; }
}
}
// usage
.my-box {
padding: 10px;
@include for-size(desktop-up) {
padding: 20px;
}
}
这种方式自然能够达成预期的效果,不过如果某个粗心的开发者传入了某个未预定义的范围名,那么久尴尬了,因此我们还是建议不要传入某个具体的尺寸,而是传入某个范围:
@mixin for-size($range) {
$phone-upper-boundary: 600px;
$tablet-portrait-upper-boundary: 900px;
$tablet-landscape-upper-boundary: 1200px;
$desktop-upper-boundary: 1800px;
@if $range == phone-only {
@media (max-width: #{$phone-upper-boundary - 1}) { @content; }
} @else if $range == tablet-portrait-up {
@media (min-width: $phone-upper-boundary) { @content; }
} @else if $range == tablet-portait-only {
@media (min-width: $phone-upper-boundary) and (max-width: #{$tablet-portrait-upper-boundary - 1}) { @content; }
} @else if $range == tablet-landscape-up {
@media (min-width: $tablet-landscape-upper-boundary) { @content; }
} @else if $range == tablet-landscape-only {
@media (min-width: $tablet-portrait-upper-boundary) and (max-width: $tablet-landscape-upper-boundary - 1px) { @content; }
} @else if $range == desktop-up {
@media (min-width: $tablet-landscape-upper-boundary) { @content; }
} @else if $range == desktop-only {
@media (min-width: $tablet-landscape-upper-boundary) and (max-width: $desktop-upper-boundary - 1px) { @content; }
} @else if $range == big-desktop-up {
@media (min-width: $desktop-upper-boundary) { @content; }
}
}
// usage
.my-box {
padding: 10px;
@include for-size(desktop-up) {
padding: 20px;
}
}