网站开发中,通常需要对不同的分辨率进行适配,但具体每个分辨率的临界点是啥呢?
确定临界点
直接参考 Bootstrap 的设置,其规则如下
< 576px
竖屏手机>= 576px
横屏手机>= 768px
平板>= 992px
桌面>= 1200px
大屏>= 1600px
超大屏
创建 mixins
确定临界点后,就是统一团队的使用规范,答案就是使用 mixins,既能方便使用,同时还能帮助我们达到统一的目的。我们注意到 Bootstrap 是提供了 SCSS 的一系列 mixins 的,使用规则如下
@include media-breakpoint-up(xs|sm|md|lg|xl) { ... }
@include media-breakpoint-down(xs|sm|md|lg|xl) { ... }
@include media-breakpoint-between(xs|sm|md|lg|xl, xs|sm|md|lg|xl) { ... }
在 Sass 的实现原理大概如下,通过 content 关键字即可
// Mixin
@mixin responsive($maxWidth) {
@media only screen and (max-width: $maxWidth) {
@content;
}
}
// Usage
.bacon {
width: 100%;
@include responsive(450px) {
float: left;
margin-top: 10px;
}
}
那么如何在 Less 中达到类似的目的呢。需要使用到 v.1.7 提供的 rulesets
// Mixin
.responsive(@maxWidth; @rules) {
@media only screen and (max-width: @maxWidth) {
@rules();
}
}
// Usage
.bacon {
width: 100%;
.responsive(450px, {
float: left;
margin-top: 10px;
});
}
Less Mixins
确定好边界点之后,就是创建 mixin util 啦,编写的过程中发现,xs 不好设置变量值,因为其是 < 576
的规则,因此直接翻看 Bootstrap 源码了。发现其定义如下
(xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
现在屏幕都变大了,因此还需要加上我们的超大屏 1600 的边界点,具体的 Less 代码如下
@offset: 0.02px;
/** 竖屏手机设备 (0, 576) */
@breakpoint-xs: 0;
/** 横屏手机设备 [576, 768) */
@breakpoint-sm: 576px;
/** 平板设备 [768, 992) */
@breakpoint-md: 768px;
/** 桌面设备 [992, 1200) */
@breakpoint-lg: 992px;
/** 大屏幕设备 [1200, 1600) */
@breakpoint-xl: 1200px;
/** 超大屏幕设备 [1600, +) */
@breakpoint-xxl: 1600px;
.media-breakpoint-up(@point; @rules) {
@var: 'breakpoint-@{point}';
@media (min-width: @@var) {
@rules();
}
}
.media-breakpoint-down(@point; @rules) {
@var: 'breakpoint-@{point}';
@media (max-width: @@var - @offset) {
@rules();
}
}
.media-breakpoint-between(@lower, @upper, @rules) {
@lowerVar: 'breakpoint-@{lower}';
@upperVar: 'breakpoint-@{upper}';
@media (min-width: @@lowerVar) and (max-width: @@upperVar - @offset) {
@rules();
}
}
响应式组件
有些情况,我们并不能通过 CSS 完全搞定我们的需要,可能还是需要借助 JS 来帮助。这里可以借助社区组件 react-media
,为统一团队规范,我们再套一层,如下
import React from 'react';
import Media from 'react-media';
const GLOBAL_MEDIA_QUERIES = {
xs: '(max-width: 575.98px)',
sm: '(min-width: 576px) and (max-width: 767.98px)',
md: '(min-width: 768px) and (max-width: 991.98px)',
lg: '(min-width: 992px) and (max-width: 1199.98px)',
xl: '(min-width: 1200px) and (max-width: 1599.98px)',
xxl: '(min-width: 1600px)',
};
type Matches = {
xs: boolean;
sm: boolean;
md: boolean;
lg: boolean;
xl: boolean;
xxl: boolean;
};
interface MediaQueryProps {
render?: () => React.ReactNode;
children?: (matches: Matches) => React.ReactNode | React.ReactNode;
}
function MediaQuery({ render, children }: MediaQueryProps) {
return (
<Media render={render} queries={GLOBAL_MEDIA_QUERIES}>
{children}
</Media>
);
}
export default MediaQuery;