Better

Ethan的博客,欢迎访问交流

如何进行响应式设计

网站开发中,通常需要对不同的分辨率进行适配,通常有哪些实现手段呢?

工具箱

兼容性无敌的传统老三样:float、auto margin、xxx-align

响应式开发工具箱

  • 单位(rem/v系)、媒体查询、设计尺寸、浮动套装、flex & grid 大法
  • 栅格系统、分而治之(结构复杂,分站点开发)、css预处理、js、调试工具

扩展一:CSS 命名的两种风格:语义化 or 结构化

扩展二:呼吸感是指你这个设计无法呼吸,看着憋死了,指太紧凑了

像素

  • 设备像素:不同设备物理像素各不相同
  • 逻辑像素:可自行设定的,比如一个设备像素当做个逻辑像素用,反之亦然
  • 设备像素密度:每英寸上拥有多少像素 pixels per inch(PPI)
  • 设备像素比:devicePixelRatio = 设备像素/css像素
  • 厂商和标准所做的努力
    • css像素:独立设备像素,浏览器厂商获取设备像素后,根据一个关系算换出来的单位,这个关系就是设备像素比
    • viewport

视口

  • 理想视口:设备厂商说了算
  • 布局视口:设备厂商说了算,早期为了 PC 端页面在手机端也能正常查看,通常布局视口会比理想视口大一些
  • 布局视口虽然是设备定义的的,但可以通过 viewport 标签调整布局视口大小,是布局视口成为理想视口

实现等比缩放效果:flexible.js

  • 根据设备的 dpr 值改变 viewport 的值
  • 使用 rem 实现等比缩放
  • 本质上使用用 hack 手段通过 rem 模拟 vw 特性

实践原则

  • 明确断点和对应显示内容
  • 大屏幕优先还是小屏幕优先

确定临界点

直接参考 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 utils 啦,编写的过程中发现,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;

react-responsive 目前是更好的方案

场景总结

针对经典的布局方式,如上中下布局、顶部-侧边布局、双飞翼布局,在响应式的表现主要体现在头部导航栏、侧边栏和内容区域。

header

  • 内容差异大,通过 js 渲染不同的 dom
  • float: left|none
  • flex-direction: column|row

siderbar

  • breakpoint + collapsed
  • 负 margin 或负 left

content 主要是控制列表的展示

  • inline-block
  • 栅格系统
  • flex
  • grid

资料



留言