本案例构建一个多页面相册博客项目,包含首页(横滑相册)、分类页(按年 Tab 切换)、博客页(双栏布局)、联系页(带 Popover 提示的表单)。重点练习导航栏、Tab 切换、灯箱插件(Swipebox)、表单校验提示(Popover)等组件的组合使用。
一、案例概述
1.1 业务背景
相册博客是摄影爱好者展示作品的常见形态。本项目模拟一个”作品集 + 博客”混合站点:首页以横滑跑马灯展示精选作品,分类页用Tab 按年份切换,博客页采用左侧文章 + 右侧推荐的经典双栏,联系页通过 Popover 给表单加即时提示。
1.2 涉及知识点
| 知识点 |
在本案例中的作用 |
导航栏 .navbar |
多页面顶部导航 |
选项卡 .nav-pills + .tab-content |
分类页按年份切换 |
| Swipebox 灯箱插件 |
相册图片点击放大 |
| Popover 提示 |
联系页表单即时提示 |
列表组 .list-group |
推荐区 / 联系方式 |
| 原生 JS 动画 |
首页跑马灯自动滚动 |
表单 .form-group |
联系页输入校验 |
1.3 页面结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 相册博客 ├── 首页 index.html │ ├── 顶部导航栏 │ ├── 横滑跑马灯 │ └── 跑马灯左右控制按钮 ├── 分类页 class.html │ ├── 顶部导航栏 │ └── 年份 Tab(2020 / 2019 / 2018) ├── 博客页 blog.html │ ├── 左侧:文章主体 │ └── 右侧:推荐旅游胜地 └── 联系页 contact.html ├── 左侧:留言表单 └── 右侧:联系方式
|
二、实现步骤拆解
2.1 顶部导航栏
四个页面共用同一段导航代码,包含品牌 logo、菜单、搜索框。折叠按钮用于移动端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <nav class="navbar navbar-expand-lg navbar-light bg-light"> <a href="index.html" class="navbar-brand"> <img src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4031816500,2465166800&fm=26&gp=0.jpg" width="45" alt=""> </a> <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbarContent"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"><a href="index.html" class="nav-link">首页</a></li> <li class="nav-item"><a href="class.html" class="nav-link">分类</a></li> <li class="nav-item"><a href="blog.html" class="nav-link">博客</a></li> <li class="nav-item"><a href="contact.html" class="nav-link">联系</a></li> </ul> <form class="form-inline my-2 my-lg-0"> <input type="search" class="form-inline mr-sm-2" placeholder="搜索"> <button class="btn btn-outline-success my-2 my-sm-0" type="submit">搜索</button> </form> </div> </nav>
|
2.2 首页跑马灯
通过 JS 克隆 <li> 实现无缝循环,再给 oUl 一个大宽度让它横向铺开:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <div class="container"> <div id="div1"> <ul id="ul1" class="py-3"> <li> <a href="http://pic1.win4000.com/wallpaper/3/587084e3946ee.jpg" class="swipebox" title="2028年"> <img src="http://pic1.win4000.com/wallpaper/3/587084e3946ee.jpg" alt="image" class="img-fluid"> </a> </li> <li> <a href="http://pic1.win4000.com/wallpaper/c/54783c8bb636f.jpg" class="swipebox" title="2028年"> <img src="http://pic1.win4000.com/wallpaper/c/54783c8bb636f.jpg" alt="image" class="img-fluid"> </a> </li> </ul> <div class="btn-box text-center mb-2"> <a href="javascript:void(0);" id="btn1" class="mr-5"><i class="fa fa-hand-o-left fa-2x"></i></a> <a href="javascript:void(0);" id="btn2"><i class="fa fa-hand-o-right fa-2x"></i></a> </div> </div> </div>
|
2.3 引入 Swipebox 灯箱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <link rel="stylesheet" href="./swipebox-master/src/css/swipebox.css"> <script src="swipebox-master/lib/jquery-2.1.0.min.js"></script> <script src="swipebox-master/src/js/jquery.swipebox.js"></script> <script> window.onload = function () { var oDiv = document.getElementById('div1'); var oUl = document.getElementById('ul1'); var speed = 2; oUl.innerHTML += oUl.innerHTML; var oLi = document.getElementsByTagName('li'); oUl.style.width = oLi.length * 160 + 'px'; var oBtn1 = document.getElementById('btn1'); var oBtn2 = document.getElementById('btn2');
function move() { if (oUl.offsetLeft < -(oUl.offsetWidth / 2)) oUl.style.left = 0; if (oUl.offsetLeft > 0) oUl.style.left = -(oUl.offsetWidth / 2) + 'px'; oUl.style.left = oUl.offsetLeft + speed + 'px'; } oBtn1.addEventListener('click', function () { speed = -2; }, false); oBtn2.addEventListener('click', function () { speed = 2; }, false); setInterval(move, 30); }; jQuery(function ($) { $('.swipebox').swipebox({ useCSS: true, useSVG: true, initialIndexOnArray: 0, hideCloseButtonOnMobile: false, removeBarsOnMobile: true, hideBarsDelay: 3000, loopAtEnd: false }); }); </script>
|
2.4 分类页:年份 Tab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <div class="container"> <div class="menu bg-dark"> <ul class="nav nav-pills my-4 p-2" id="myTab"> <li><a href="#pills-home" class="ab" data-toggle="pill">2020年</a></li> <li><a href="#pills-profile" data-toggle="pill">2019年</a></li> <li><a href="#pills-contact" data-toggle="pill">2018年</a></li> <li><a href="javascript:void(0);">更多</a></li> </ul> </div>
<div class="tab-content"> <div class="tab-pane fade show active" id="pills-home"> <div class="row list"> <div class="col-4"> <a href="http://pic1.win4000.com/wallpaper/4/53a2af0995d64.jpg" class="swipebox" title="2020年"> <img src="http://pic1.win4000.com/wallpaper/4/53a2af0995d64.jpg" alt="image" class="img-fluid"> </a> </div> </div> </div> <div class="tab-pane fade" id="pills-profile"></div> <div class="tab-pane fade" id="pills-contact"></div> </div> </div>
|
2.5 博客页:双栏布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <div class="border row bg-white m-0 px-3 pt-4 pb-5 blog-border"> <div class="col-8"> <h4><i class="fa fa-smile-o mr-2"></i><span>我的足迹</span></h4> <hr> <div class="mb-3"> <i class="fa fa-user-o"></i><span class="ml-1 mr-2">欢欢</span> <i class="fa fa-clock-o"></i><span class="ml-1 mr-2">15天前</span> <a href="javascript:void(0);" class="ml-1 mr-2"><i class="fa fa-commenting-o">156条</i></a> </div> <img src="http://img0.imgtn.bdimg.com/it/u=1101084170,1274803972&fm=26&gp=0.jpg" alt="" class="img-fluid mb-3"> <p class="retract"> 一个人旅行,一台相机足以、不理会繁杂的琐事,自由自在地,去体验一个城市、一段故事、留下一片欢笑。 </p> </div> <div class="col-4"> <h4 class="shadow mb-4"><span class="mx-2">推荐旅游胜地</span><i class="fa fa-bicycle"></i></h4> <ul class="list-group list-group-flush"> <li class="list-group-item border-top-0"><i class="fa fa-hand-o-right mr-3"></i>神秘奇幻、佳景荟萃的九寨沟</li> <li class="list-group-item"><i class="fa fa-hand-o-right mr-3"></i>奇伟俏丽、灵秀多姿的黄山</li> <li class="list-group-item"><i class="fa fa-hand-o-right mr-3"></i>青山碧水、银滩巨浪的三亚</li> <li class="list-group-item"><i class="fa fa-hand-o-right mr-3"></i>山青、水秀、洞奇、石美的桂林山水</li> <li class="list-group-item"><i class="fa fa-hand-o-right mr-3"></i>山水秀丽、景色宜人的杭州西湖</li> </ul> </div> </div>
|
2.6 联系页:Popover 表单
通过 data-toggle="popover" 给每个输入框附加即时提示,初始化时调用 popover() 激活:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <div class="row border bg-white m-0 px-3 pt-4 pb-5 blog-border"> <div class="col-12 col-lg-8 pb-5"> <h4><i class="fa fa-volume-control-phone mr-2"></i><span>你的联系方式</span></h4> <hr> <form> <div class="form-group"> <input type="text" class="form-control w-75" placeholder="姓名" data-toggle="popover" data-placement="right" data-content="请输入你的姓名"> </div> <div class="form-group"> <input type="email" class="form-control w-75" placeholder="邮箱" data-toggle="popover" data-placement="right" data-content="请输入你的邮箱"> </div> <div class="form-group"> <input type="tel" class="form-control w-75" placeholder="手机号" data-toggle="popover" data-placement="right" data-content="请输入你的手机号"> </div> <div class="form-group"> <textarea class="form-control w-75" rows="5" placeholder="留言板" data-toggle="popover" data-placement="right" data-content="留言板"></textarea> </div> <button type="submit" class="btn btn-primary">提交</button> </form> </div> <div class="col-12 col-lg-4"> <h4 class="shadow mb-4"><i class="fa fa-phone-square mx-2"></i><span>联系我们</span></h4> <div class="alert alert-primary"><i class="fa fa-qq mr-3"></i><span>867940410</span></div> <div class="alert alert-info"><i class="fa fa-weixin mr-3"></i><span>刘飞</span></div> <div class="alert alert-success"><i class="fa fa-mobile fa-2x mr-3"></i><span>12345678912</span></div> <div class="alert alert-danger"><i class="fa fa-map-maker fa-2x mr-3"></i><span>北京千古摄影工作室</span></div> </div> </div>
<script> $(function () { $('[data-toggle="popover"]').popover(); }); </script>
|
三、关键 CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| html{ background-color: lavender; } #div1{ width: 100%; height: 300px; margin: 150px auto; position: relative; overflow: hidden; border: 2px solid white; background-color: white; } #div1 ul{ height: 240px; position: absolute; left: 0; top: 0; overflow: hidden; list-style: none; background-color: white; } #div1 ul li{ float: left; width: 360px; list-style: none; margin-left: 1.1rem; } #div1 ul li img{ width: 360px; } .btn-box{ position: relative; left: 0; top: 255px; } .menu{ width: 275px; border-radius: 10px; } #myTab{ list-style: none; } #myTab li{ float: left; margin-left: 15px; } #myTab li a{ color: #919191; } .ab{ color: #00A862 !important; } .list{ min-width: 600px; } .list div{ margin-bottom: 20px; } .blog-border{ border-radius: 10px; } .retract{ text-indent: 2rem; } .shadow{ line-height: 48px; padding: 0 10px; margin-bottom: 20px; border-top: 2px solid #d7d7d7; border-bottom: 2px solid #ffffff; border-radius: 5px; }
|
四、关键技术解析
4.1 跑马灯无缝循环
核心三步:
oUl.innerHTML += oUl.innerHTML 克隆一份图片。
- 当
oUl.offsetLeft < -oUl.offsetWidth/2 时把 left 归零(视觉上无感知)。
- 当
oUl.offsetLeft > 0(向右滑过头)时跳到负半幅起始位置。
4.2 Swipebox 灯箱
Swipebox 是一个轻量 jQuery 灯箱插件,绑定 .swipebox 类后点击图片即可弹出全屏大图,支持左右切换、关闭按钮、自动隐藏工具栏等。配置项在 swipebox({...}) 中传入。
4.3 Tab + data-toggle=”pill”
nav-pills + tab-content + tab-pane 三个组合是 Bootstrap 4 的胶囊型 Tab:
data-toggle="pill" 绑定点击行为。
.tab-pane 用 fade show active 切换淡入淡出与可见性。
- 父容器
id 决定 href 跳转目标。
4.4 Popover 提示
Bootstrap 的 Popover 组件比 Tooltip 更强大,支持标题、内容、位置、触发方式。给输入框加 data-toggle="popover" 即可声明提示,初始化时调用 .popover() 激活。
五、小结
本案例通过四类典型页面演练了相册博客的核心交互:
- 跑马灯 + 灯箱展示作品。
- Tab 按年份组织相册。
- 双栏博客 + 推荐区。
- Popover 表单即时提示。
建议扩展练习:
- 把跑马灯改为 CSS
@keyframes 动画版本。
- 给博客页加评论列表与分页。
- 把联系页表单改用 Bootstrap 的
.was-validated + :invalid 实现原生校验。