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