Skip to content

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">&times;</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">&times;</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">&times;</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 最佳实践

  1. 移动端优先:Off-Canvas 最适合移动端导航
  2. 清晰的触发器:使用易识别的图标或按钮
  3. 关闭按钮:提供明显的关闭方式
  4. 适当宽度:不要占据太多屏幕空间
  5. 内容组织:合理组织面板内容

总结

本章我们学习了:

  • 基本 Off-Canvas 的创建
  • 不同位置(左、右、上、下)
  • 响应式 Off-Canvas
  • 带头部和嵌套菜单的 Off-Canvas
  • 双侧 Off-Canvas
  • JavaScript API

下一章,我们将学习 Foundation Magellan