Foundation 滑动导航 (Off-Canvas)
Off-Canvas 是一种滑动式导航模式,常用于移动端界面。当触发时,导航面板从屏幕边缘滑入。本章将介绍 Off-Canvas 的各种用法。
基本 Off-Canvas
html
<div class="off-canvas-wrapper">
<!-- Off-Canvas 面板 -->
<div class="off-canvas position-left" id="offCanvas" data-off-canvas>
<ul class="vertical menu">
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">服务</a></li>
<li><a href="#">联系</a></li>
</ul>
</div>
<!-- 主内容区域 -->
<div class="off-canvas-content" data-off-canvas-content>
<!-- 触发按钮 -->
<button type="button" data-toggle="offCanvas">打开菜单</button>
<!-- 页面内容 -->
<div class="grid-container">
<h1>主页面内容</h1>
<p>点击按钮打开侧边导航。</p>
</div>
</div>
</div>位置选项
Off-Canvas 可以从不同方向滑入:
html
<!-- 左侧滑入 -->
<div class="off-canvas position-left" id="offCanvasLeft" data-off-canvas>
内容
</div>
<!-- 右侧滑入 -->
<div class="off-canvas position-right" id="offCanvasRight" data-off-canvas>
内容
</div>
<!-- 顶部滑入 -->
<div class="off-canvas position-top" id="offCanvasTop" data-off-canvas>
内容
</div>
<!-- 底部滑入 -->
<div class="off-canvas position-bottom" id="offCanvasBottom" data-off-canvas>
内容
</div>遮罩层
使用 data-reveal 控制遮罩层行为:
html
<!-- 显示遮罩层(默认) -->
<div class="off-canvas position-left" id="offCanvas1" data-off-canvas data-reveal="push">
内容
</div>
<!-- 覆盖模式 -->
<div class="off-canvas position-left" id="offCanvas2" data-off-canvas data-transition="overlap">
内容
</div>响应式 Off-Canvas
只在特定屏幕尺寸显示:
html
<div class="off-canvas-wrapper">
<!-- 仅在小屏幕显示 -->
<div class="off-canvas position-left reveal-for-medium" id="offCanvasResponsive" data-off-canvas>
<ul class="vertical menu">
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>
<div class="off-canvas-content" data-off-canvas-content>
<!-- 仅在小屏幕显示的触发按钮 -->
<button type="button" class="hide-for-medium" data-toggle="offCanvasResponsive">
菜单
</button>
<main>内容</main>
</div>
</div>带头部的 Off-Canvas
html
<div class="off-canvas position-left" id="offCanvasWithHeader" data-off-canvas>
<div style="background: #1779ba; color: white; padding: 15px;">
<div class="grid-x align-middle">
<div class="cell auto">
<strong>导航菜单</strong>
</div>
<div class="cell shrink">
<button class="close-button" aria-label="关闭菜单" type="button" data-close>
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
<ul class="vertical menu">
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">服务</a></li>
<li><a href="#">联系</a></li>
</ul>
</div>嵌套菜单
html
<div class="off-canvas position-left" id="offCanvasNested" data-off-canvas>
<ul class="vertical menu" data-drilldown>
<li>
<a href="#">产品</a>
<ul class="menu vertical nested">
<li><a href="#">产品 A</a></li>
<li><a href="#">产品 B</a></li>
<li>
<a href="#">产品 C</a>
<ul class="menu vertical nested">
<li><a href="#">C-1</a></li>
<li><a href="#">C-2</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#">服务</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>双侧 Off-Canvas
html
<div class="off-canvas-wrapper">
<!-- 左侧面板 -->
<div class="off-canvas position-left" id="leftPanel" data-off-canvas>
<h4>导航菜单</h4>
<ul class="vertical menu">
<li><a href="#">首页</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>
<!-- 右侧面板 -->
<div class="off-canvas position-right" id="rightPanel" data-off-canvas>
<h4>购物车</h4>
<ul class="no-bullet">
<li>商品 1 - ¥99</li>
<li>商品 2 - ¥199</li>
</ul>
<hr>
<p><strong>总计:¥298</strong></p>
<a class="button expanded" href="#">结算</a>
</div>
<!-- 主内容 -->
<div class="off-canvas-content" data-off-canvas-content>
<div class="top-bar">
<div class="top-bar-left">
<button type="button" data-toggle="leftPanel">菜单</button>
</div>
<div class="top-bar-right">
<button type="button" data-toggle="rightPanel">购物车</button>
</div>
</div>
<main>内容</main>
</div>
</div>配置选项
html
<div class="off-canvas position-left" id="configOffCanvas" data-off-canvas
data-transition="overlap"
data-close-on-click="true"
data-content-overlay="true"
data-force-top="true">
内容
</div>| 选项 | 说明 | 默认值 |
|---|---|---|
data-transition | 过渡效果 (push/overlap) | push |
data-close-on-click | 点击遮罩关闭 | true |
data-content-overlay | 显示内容遮罩 | true |
data-force-top | 强制滚动到顶部 | true |
JavaScript API
javascript
// 打开
$('#offCanvas').foundation('open');
// 关闭
$('#offCanvas').foundation('close');
// 切换
$('#offCanvas').foundation('toggle');
// 事件
$('#offCanvas').on('opened.zf.offCanvas', function() {
console.log('Off-Canvas 已打开');
});
$('#offCanvas').on('closed.zf.offCanvas', function() {
console.log('Off-Canvas 已关闭');
});完整示例
html
<!DOCTYPE html>
<html class="no-js" lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Foundation Off-Canvas 示例</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.5/dist/css/foundation.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css">
<style>
.off-canvas {
background: #2c3e50;
}
.off-canvas .menu a {
color: #ecf0f1;
padding: 15px 20px;
}
.off-canvas .menu a:hover {
background: rgba(255,255,255,0.1);
}
.off-canvas .menu a i {
margin-right: 10px;
}
.off-canvas-header {
background: #1779ba;
color: white;
padding: 15px 20px;
}
.off-canvas-header h4 {
margin: 0;
color: white;
}
.user-profile {
padding: 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.user-profile img {
width: 80px;
height: 80px;
border-radius: 50%;
margin-bottom: 10px;
}
.user-profile h5 {
color: white;
margin: 0;
}
.user-profile p {
color: #bdc3c7;
margin: 0;
font-size: 14px;
}
.main-content {
min-height: 100vh;
padding: 20px;
}
</style>
</head>
<body>
<div class="off-canvas-wrapper">
<!-- 左侧 Off-Canvas -->
<div class="off-canvas position-left" id="leftOffCanvas" data-off-canvas>
<div class="off-canvas-header">
<div class="grid-x align-middle">
<div class="cell auto">
<h4>导航菜单</h4>
</div>
<div class="cell shrink">
<button class="close-button" aria-label="关闭" type="button" data-close style="color: white;">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
<div class="user-profile">
<img src="https://via.placeholder.com/80" alt="用户头像">
<h5>张三</h5>
<p>前端开发工程师</p>
</div>
<ul class="vertical menu">
<li><a href="#"><i class="fi-home"></i> 首页</a></li>
<li><a href="#"><i class="fi-torso"></i> 个人资料</a></li>
<li><a href="#"><i class="fi-mail"></i> 消息</a></li>
<li><a href="#"><i class="fi-widget"></i> 设置</a></li>
<li><a href="#"><i class="fi-info"></i> 帮助</a></li>
<li><a href="#"><i class="fi-power"></i> 退出</a></li>
</ul>
</div>
<!-- 右侧 Off-Canvas (购物车) -->
<div class="off-canvas position-right" id="rightOffCanvas" data-off-canvas style="background: white; width: 300px;">
<div style="background: #3adb76; color: white; padding: 15px 20px;">
<div class="grid-x align-middle">
<div class="cell auto">
<h4 style="margin: 0; color: white;">购物车</h4>
</div>
<div class="cell shrink">
<button class="close-button" aria-label="关闭" type="button" data-close>
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
<div style="padding: 20px;">
<div class="grid-x" style="margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #eee;">
<div class="cell shrink">
<img src="https://via.placeholder.com/60" alt="商品图片" style="border-radius: 4px;">
</div>
<div class="cell auto" style="padding-left: 10px;">
<strong>商品名称 1</strong>
<p style="margin: 0; color: #666; font-size: 14px;">数量: 1</p>
<p style="margin: 0; color: #cc4b37; font-weight: bold;">¥99.00</p>
</div>
</div>
<div class="grid-x" style="margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #eee;">
<div class="cell shrink">
<img src="https://via.placeholder.com/60" alt="商品图片" style="border-radius: 4px;">
</div>
<div class="cell auto" style="padding-left: 10px;">
<strong>商品名称 2</strong>
<p style="margin: 0; color: #666; font-size: 14px;">数量: 2</p>
<p style="margin: 0; color: #cc4b37; font-weight: bold;">¥199.00</p>
</div>
</div>
<div style="background: #f4f4f4; padding: 15px; border-radius: 4px; margin-bottom: 15px;">
<div class="grid-x">
<div class="cell auto">小计</div>
<div class="cell shrink"><strong>¥298.00</strong></div>
</div>
</div>
<a class="button expanded success" href="#">去结算</a>
<a class="button hollow expanded" href="#">继续购物</a>
</div>
</div>
<!-- 主内容区域 -->
<div class="off-canvas-content" data-off-canvas-content>
<!-- 顶部导航 -->
<div class="top-bar" style="background: #2c3e50;">
<div class="top-bar-left">
<ul class="menu">
<li>
<button class="button clear" type="button" data-toggle="leftOffCanvas" style="color: white;">
<i class="fi-list"></i>
</button>
</li>
<li class="menu-text" style="color: white;">网站名称</li>
</ul>
</div>
<div class="top-bar-right">
<ul class="menu">
<li>
<button class="button clear" type="button" data-toggle="rightOffCanvas" style="color: white; position: relative;">
<i class="fi-shopping-cart"></i>
<span class="badge alert" style="position: absolute; top: 0; right: 0; font-size: 10px;">2</span>
</button>
</li>
</ul>
</div>
</div>
<!-- 主内容 -->
<div class="main-content">
<div class="grid-container">
<h1>Off-Canvas 导航示例</h1>
<p>点击左上角的菜单图标打开导航菜单,点击右上角的购物车图标查看购物车。</p>
<div class="callout">
<h4>Off-Canvas 特点</h4>
<ul>
<li>从屏幕边缘滑入</li>
<li>支持多个方向(左、右、上、下)</li>
<li>适合移动端导航</li>
<li>可以包含任何内容</li>
</ul>
</div>
<div class="grid-x grid-margin-x small-up-1 medium-up-3">
<div class="cell">
<div class="card">
<img src="https://via.placeholder.com/300x200" alt="产品">
<div class="card-section">
<h4>产品 1</h4>
<p>产品描述...</p>
<a class="button" href="#">查看详情</a>
</div>
</div>
</div>
<div class="cell">
<div class="card">
<img src="https://via.placeholder.com/300x200" alt="产品">
<div class="card-section">
<h4>产品 2</h4>
<p>产品描述...</p>
<a class="button" href="#">查看详情</a>
</div>
</div>
</div>
<div class="cell">
<div class="card">
<img src="https://via.placeholder.com/300x200" alt="产品">
<div class="card-section">
<h4>产品 3</h4>
<p>产品描述...</p>
<a class="button" href="#">查看详情</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.5/dist/js/foundation.min.js"></script>
<script>$(document).foundation();</script>
</body>
</html>Off-Canvas 最佳实践
- 移动端优先:Off-Canvas 最适合移动端导航
- 清晰的触发器:使用易识别的图标或按钮
- 关闭按钮:提供明显的关闭方式
- 适当宽度:不要占据太多屏幕空间
- 内容组织:合理组织面板内容
总结
本章我们学习了:
- 基本 Off-Canvas 的创建
- 不同位置(左、右、上、下)
- 响应式 Off-Canvas
- 带头部和嵌套菜单的 Off-Canvas
- 双侧 Off-Canvas
- JavaScript API
下一章,我们将学习 Foundation Magellan。