Ionic响应式布局 #

一、响应式概述 #

1.1 Ionic响应式系统 #

Ionic提供完整的响应式解决方案:

text
响应式系统
    │
    ├── Grid系统
    │   ├── 12列网格
    │   ├── 响应式断点
    │   └── 自动布局
    │
    ├── 断点系统
    │   ├── xs: 0-576px
    │   ├── sm: 576-768px
    │   ├── md: 768-992px
    │   ├── lg: 992-1200px
    │   └── xl: 1200px+
    │
    ├── 媒体查询
    │   ├── CSS媒体查询
    │   └── JavaScript检测
    │
    └── 平台适配
        ├── iOS适配
        ├── Android适配
        └── Web适配

1.2 断点定义 #

scss
// Ionic默认断点
$breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

二、Grid响应式布局 #

2.1 基本网格 #

html
<ion-grid>
  <ion-row>
    <ion-col size="12">
      <div>全宽列</div>
    </ion-col>
  </ion-row>
  
  <ion-row>
    <ion-col size="6">
      <div>半宽列</div>
    </ion-col>
    <ion-col size="6">
      <div>半宽列</div>
    </ion-col>
  </ion-row>
  
  <ion-row>
    <ion-col size="4">
      <div>三分之一</div>
    </ion-col>
    <ion-col size="4">
      <div>三分之一</div>
    </ion-col>
    <ion-col size="4">
      <div>三分之一</div>
    </ion-col>
  </ion-row>
</ion-grid>

2.2 响应式列宽 #

html
<ion-grid>
  <ion-row>
    <!-- 不同断点不同宽度 -->
    <ion-col 
      size="12" 
      size-sm="6" 
      size-md="4" 
      size-lg="3" 
      size-xl="2">
      <div>响应式列</div>
    </ion-col>
  </ion-row>
</ion-grid>

断点说明:

断点 宽度范围 size-sm size-md size-lg size-xl
xs 0-576px - - - -
sm 576-768px - - -
md 768-992px - -
lg 992-1200px -
xl 1200px+

2.3 自动布局 #

html
<ion-grid>
  <ion-row>
    <!-- 自动等分 -->
    <ion-col>
      <div>自动</div>
    </ion-col>
    <ion-col>
      <div>自动</div>
    </ion-col>
    <ion-col>
      <div>自动</div>
    </ion-col>
  </ion-row>
  
  <ion-row>
    <!-- 固定宽度 + 自动填充 -->
    <ion-col size="3">
      <div>固定宽度</div>
    </ion-col>
    <ion-col>
      <div>自动填充</div>
    </ion-col>
  </ion-row>
  
  <ion-row>
    <!-- 自动收缩 -->
    <ion-col size="auto">
      <div>内容宽度</div>
    </ion-col>
    <ion-col>
      <div>填充剩余</div>
    </ion-col>
  </ion-row>
</ion-grid>

2.4 偏移量 #

html
<ion-grid>
  <ion-row>
    <ion-col size="4" offset="4">
      <div>居中</div>
    </ion-col>
  </ion-row>
  
  <ion-row>
    <ion-col size="3" offset="3">
      <div>偏移3列</div>
    </ion-col>
    <ion-col size="3" offset="3">
      <div>偏移3列</div>
    </ion-col>
  </ion-row>
  
  <!-- 响应式偏移 -->
  <ion-row>
    <ion-col size="6" offset-md="3">
      <div>中等屏幕居中</div>
    </ion-col>
  </ion-row>
</ion-grid>

2.5 对齐方式 #

html
<ion-grid>
  <!-- 水平对齐 -->
  <ion-row class="ion-justify-content-start">
    <ion-col size="3"><div>左对齐</div></ion-col>
  </ion-row>
  
  <ion-row class="ion-justify-content-center">
    <ion-col size="3"><div>居中</div></ion-col>
  </ion-row>
  
  <ion-row class="ion-justify-content-end">
    <ion-col size="3"><div>右对齐</div></ion-col>
  </ion-row>
  
  <ion-row class="ion-justify-content-around">
    <ion-col size="3"><div>分散</div></ion-col>
    <ion-col size="3"><div>分散</div></ion-col>
  </ion-row>
  
  <ion-row class="ion-justify-content-between">
    <ion-col size="3"><div>两端</div></ion-col>
    <ion-col size="3"><div>两端</div></ion-col>
  </ion-row>
</ion-grid>

<ion-grid>
  <!-- 垂直对齐 -->
  <ion-row class="ion-align-items-start" style="height: 200px;">
    <ion-col><div>顶部</div></ion-col>
  </ion-row>
  
  <ion-row class="ion-align-items-center" style="height: 200px;">
    <ion-col><div>居中</div></ion-col>
  </ion-row>
  
  <ion-row class="ion-align-items-end" style="height: 200px;">
    <ion-col><div>底部</div></ion-col>
  </ion-row>
</ion-grid>

三、媒体查询 #

3.1 CSS媒体查询 #

scss
// 小屏幕
@media (max-width: 576px) {
  .container {
    padding: 8px;
  }
  
  ion-card {
    margin: 8px;
  }
}

// 中等屏幕
@media (min-width: 576px) and (max-width: 768px) {
  .container {
    padding: 16px;
  }
  
  ion-card {
    margin: 12px;
  }
}

// 大屏幕
@media (min-width: 768px) {
  .container {
    padding: 24px;
    max-width: 720px;
    margin: 0 auto;
  }
  
  ion-card {
    margin: 16px;
  }
}

// 超大屏幕
@media (min-width: 992px) {
  .container {
    max-width: 960px;
  }
}

@media (min-width: 1200px) {
  .container {
    max-width: 1140px;
  }
}

3.2 方向检测 #

scss
// 横屏
@media (orientation: landscape) {
  .landscape-only {
    display: block;
  }
  
  .portrait-only {
    display: none;
  }
}

// 竖屏
@media (orientation: portrait) {
  .landscape-only {
    display: none;
  }
  
  .portrait-only {
    display: block;
  }
}

3.3 设备检测 #

scss
// 手机
@media (max-width: 767px) {
  .mobile-only {
    display: block;
  }
  
  .tablet-up {
    display: none;
  }
}

// 平板及以上
@media (min-width: 768px) {
  .mobile-only {
    display: none;
  }
  
  .tablet-up {
    display: block;
  }
}

// 桌面
@media (min-width: 1024px) {
  .desktop-only {
    display: block;
  }
}

四、平台适配 #

4.1 平台类 #

Ionic自动添加平台类:

html
<!-- iOS设备 -->
<body class="ios platform-mobile">
  
<!-- Android设备 -->
<body class="md platform-mobile">
  
<!-- Web浏览器 -->
<body class="md platform-browser">

4.2 平台样式 #

scss
// iOS特定样式
.ios {
  ion-header ion-toolbar:first-of-type {
    padding-top: var(--ion-safe-area-top);
  }
  
  ion-content {
    --padding-top: var(--ion-safe-area-top);
  }
}

// Android特定样式
.md {
  ion-header ion-toolbar:first-of-type {
    padding-top: 0;
  }
}

// Web特定样式
.platform-browser {
  ion-content {
    --padding-bottom: 0;
  }
}

4.3 安全区域适配 #

scss
// 全局安全区域
:root {
  --ion-safe-area-top: env(safe-area-inset-top);
  --ion-safe-area-bottom: env(safe-area-inset-bottom);
  --ion-safe-area-left: env(safe-area-inset-left);
  --ion-safe-area-right: env(safe-area-inset-right);
}

// 工具栏适配
ion-toolbar {
  padding-top: var(--ion-safe-area-top);
}

// 标签栏适配
ion-tab-bar {
  padding-bottom: var(--ion-safe-area-bottom);
}

// 内容区域适配
ion-content {
  --padding-top: var(--ion-safe-area-top);
  --padding-bottom: var(--ion-safe-area-bottom);
}

五、响应式组件 #

5.1 响应式导航 #

html
<!-- 移动端:底部标签栏 -->
<ion-tabs *ngIf="isMobile">
  <!-- tabs内容 -->
</ion-tabs>

<!-- 桌面端:侧边菜单 -->
<ion-split-pane *ngIf="!isMobile" contentId="main-content">
  <ion-menu contentId="main-content">
    <!-- 菜单内容 -->
  </ion-menu>
  <ion-router-outlet id="main-content"></ion-router-outlet>
</ion-split-pane>
typescript
// 组件
export class AppComponent {
  isMobile = true;
  
  constructor(private platform: Platform) {
    this.checkScreenSize();
    
    window.addEventListener('resize', () => {
      this.checkScreenSize();
    });
  }
  
  checkScreenSize() {
    this.isMobile = this.platform.width() < 768;
  }
}

5.2 响应式列表 #

html
<!-- 移动端:列表 -->
<ion-list *ngIf="isMobile">
  <ion-item *ngFor="let item of items">
    <ion-label>{{ item.name }}</ion-label>
  </ion-item>
</ion-list>

<!-- 桌面端:网格 -->
<ion-grid *ngIf="!isMobile">
  <ion-row>
    <ion-col size="4" *ngFor="let item of items">
      <ion-card>
        <ion-card-content>{{ item.name }}</ion-card-content>
      </ion-card>
    </ion-col>
  </ion-row>
</ion-grid>

5.3 响应式卡片 #

scss
// 响应式卡片网格
.card-grid {
  display: grid;
  gap: 16px;
  
  // 手机:单列
  grid-template-columns: 1fr;
  
  // 平板:双列
  @media (min-width: 576px) {
    grid-template-columns: repeat(2, 1fr);
  }
  
  // 桌面:三列
  @media (min-width: 768px) {
    grid-template-columns: repeat(3, 1fr);
  }
  
  // 大屏:四列
  @media (min-width: 992px) {
    grid-template-columns: repeat(4, 1fr);
  }
}

六、断点服务 #

6.1 断点检测服务 #

typescript
// breakpoint.service.ts
import { Injectable } from '@angular/core';
import { Platform } from '@ionic/angular';
import { BehaviorSubject } from 'rxjs';

export interface Breakpoint {
  name: string;
  minWidth: number;
  maxWidth: number;
}

@Injectable({
  providedIn: 'root'
})
export class BreakpointService {
  private breakpoints: Breakpoint[] = [
    { name: 'xs', minWidth: 0, maxWidth: 576 },
    { name: 'sm', minWidth: 576, maxWidth: 768 },
    { name: 'md', minWidth: 768, maxWidth: 992 },
    { name: 'lg', minWidth: 992, maxWidth: 1200 },
    { name: 'xl', minWidth: 1200, maxWidth: Infinity }
  ];
  
  private currentBreakpoint$ = new BehaviorSubject<string>('xs');
  
  constructor(private platform: Platform) {
    this.detectBreakpoint();
    window.addEventListener('resize', () => this.detectBreakpoint());
  }
  
  get breakpoint$() {
    return this.currentBreakpoint$.asObservable();
  }
  
  get currentBreakpoint(): string {
    return this.currentBreakpoint$.value;
  }
  
  is(breakpoint: string): boolean {
    return this.currentBreakpoint === breakpoint;
  }
  
  isUp(breakpoint: string): boolean {
    const bp = this.breakpoints.find(b => b.name === breakpoint);
    if (!bp) return false;
    return this.platform.width() >= bp.minWidth;
  }
  
  isDown(breakpoint: string): boolean {
    const bp = this.breakpoints.find(b => b.name === breakpoint);
    if (!bp) return false;
    return this.platform.width() < bp.maxWidth;
  }
  
  isMobile(): boolean {
    return this.isDown('sm');
  }
  
  isTablet(): boolean {
    return this.isUp('sm') && this.isDown('lg');
  }
  
  isDesktop(): boolean {
    return this.isUp('lg');
  }
  
  private detectBreakpoint() {
    const width = this.platform.width();
    const bp = this.breakpoints.find(b => 
      width >= b.minWidth && width < b.maxWidth
    );
    if (bp) {
      this.currentBreakpoint$.next(bp.name);
    }
  }
}

6.2 使用断点服务 #

typescript
// 组件
import { Component, OnInit } from '@angular/core';
import { BreakpointService } from './breakpoint.service';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage implements OnInit {
  isMobile = false;
  isTablet = false;
  isDesktop = false;
  
  constructor(private breakpoint: BreakpointService) {}
  
  ngOnInit() {
    this.breakpoint.breakpoint$.subscribe(bp => {
      this.isMobile = this.breakpoint.isMobile();
      this.isTablet = this.breakpoint.isTablet();
      this.isDesktop = this.breakpoint.isDesktop();
    });
  }
}

七、最佳实践 #

7.1 移动优先 #

scss
// 推荐:移动优先
.container {
  padding: 8px;
  
  @media (min-width: 576px) {
    padding: 16px;
  }
  
  @media (min-width: 768px) {
    padding: 24px;
  }
}

// 不推荐:桌面优先
.container {
  padding: 24px;
  
  @media (max-width: 768px) {
    padding: 16px;
  }
  
  @media (max-width: 576px) {
    padding: 8px;
  }
}

7.2 弹性布局 #

scss
// 使用flexbox
.flex-container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.flex-item {
  flex: 1 1 300px; // 最小300px,自动伸缩
  max-width: 100%;
}

@media (min-width: 768px) {
  .flex-item {
    max-width: calc(50% - 8px);
  }
}

@media (min-width: 992px) {
  .flex-item {
    max-width: calc(33.333% - 11px);
  }
}

7.3 图片响应式 #

scss
// 响应式图片
img {
  max-width: 100%;
  height: auto;
}

// 背景图片
.hero {
  background-image: url('mobile.jpg');
  background-size: cover;
  
  @media (min-width: 768px) {
    background-image: url('tablet.jpg');
  }
  
  @media (min-width: 1200px) {
    background-image: url('desktop.jpg');
  }
}

八、总结 #

8.1 响应式要点 #

要点 说明
Grid系统 12列网格,响应式列宽
断点系统 xs/sm/md/lg/xl
媒体查询 CSS和JavaScript检测
平台适配 iOS/Android/Web
安全区域 刘海屏适配

8.2 下一步 #

掌握了响应式布局后,接下来让我们学习 动画效果,了解Ionic的动画系统!

最后更新:2026-03-28