108
社区成员
这个作业属于哪个课程 | https://bbs.csdn.net/forums/2401_CS_SE_FZU?typeId=7771625 |
---|---|
这个作业要求在哪里 | https://bbs.csdn.net/topics/619333839 |
这个作业的目标 | 使用web技术来实现原型中的功能 |
其他参考文献 | 构建之树 |
1.1仓库地址
https://devcloud.cn-north-4.huaweicloud.com/codehub/project/01f4bfbc0ab440c9b5d85f4658a68554/codehub/2708696/home?ref=master
1.2代码规范地址
https://github.com/ecomfe/spec/blob/master/html-style-guide.md
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 35 | 35 |
Estimate | 估计这个任务需要多少时间 | 30 | 20 |
Development | 开发 | 1000 | 1050 |
Analysis | 需求分析(包括学习新技术) | 70 | 85 |
Design Spec | 生成设计文档 | 65 | 70 |
Design Review | 设计复审 | 30 | 25 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 80 | 85 |
Design | 具体设计 | 130 | 125 |
Coding | 具体编码 | 500 | 520 |
Code Review | 代码复审 | 35 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 130 | 150 |
Reporting | 报告 | 180 | 200 |
Test Report | 测试报告 | 60 | 70 |
Size Measurement | 计算工作量 | 35 | 40 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 80 | 90 |
合计 | 1110 | 1200 |
展示巴黎奥运会logo和标题信息,提供奖牌榜、每日赛程、了解更多的功能入口,用轮播图展示了巴黎奥运会相关的图片,图片下面有关于开幕式盛况的文字介绍。
奖牌榜页面采用简约风,用简约的奖牌icon和卡片列表直观地展现各国奖牌数量与排名,可上下滚动查看奖牌信息。
为男子足球比赛实现了对阵图功能,鼠标划入参赛国家时会有蓝色边框。
顶部提供了日期切换功能,可以通过点击左右按钮滚动。
点击每日赛程中的一项比赛,将会跳转到该比赛大类的赛程详情界面,并定位到所点击的比赛。以表格形式展示了这场比赛的参赛人员和替补成员。
我们实现了了解更多功能,通过展示介绍巴黎奥运会开幕式的盛况,让用户对本次奥运会有更丰富的了解。
拿到题目后,由于后端知识有限,我们进行讨论后决定用HTML、CSS等前端技术实现,将各个模块作为一个独立的项目开发。我们讨论后决定实现一个有查看首页、奖牌榜、每日赛程、对阵表、详细赛况功能的网页项目。接着由赛比海同学进行所需的素材整理和基础的页面设计,设计完成后然后再一起讨论进行搭建项目框架,然后由林予萱同学进行主要的代码编写来实现网站内的各种功能,例如轮播图、页面跳转等效果。然后一起进行配置git仓库,最后一起修改完善所存在的问题和页面设计。由于我们是同个宿舍的舍友,所以我们主要是通过直接在宿舍进行讨论和开发,部分内容在线上进行讨论。
首页模块
主页提供四个导航按钮,分别可以跳转到对阵表、每日赛程、奖牌榜和了解更多。
用轮播图展示巴黎奥运会相关图片
奖牌榜模块
这个模块展示了各国金牌、银牌、铜牌和总计的奖牌数量和对应的排名。
每日赛程模块
此模块提供日期选择器,可以跳转到不同日期查看当日的赛程信息,卡片点击可以跳转详细赛程
详细赛程模块
此模块展示比赛双方比分信息和出赛名单
对阵表
此模块有1/4决赛、半决赛、决赛对阵信息展示,鼠标移入高亮显示
了解更多
此模块有巴黎奥运会相关信息展示
此为奖牌榜样式的代码,用表格清晰呈现各个国家的排名和所获得的奖牌数量,鼠标悬停时,对应国家行列背景变为深灰色,更加醒目。
table {
width: 60%;
border-collapse: collapse;
margin: 20px auto;
}
/* 表头和单元格样式 */
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}
/* 表头背景色 */
th {
background-color: #f2f2f2;
}
/* 奇数行和偶数行的背景色 */
tr:nth-child(even) {
background-color: #f9f9f9;
}
/* 鼠标悬停效果 */
tr:hover {
background-color: #ddd;
}
日期选择栏(#daybox):
显示一组日期按钮,使用ul和li标签来布局每个日期项。
用户可以左右点击按钮(.btntomove)移动显示的日期项。
每个日期项的样式设计为具有悬停效果和选中(.active)时的不同背景色。
内容展示区(#res_data):
展示与选择日期相关的详细内容,每个日期对应一个li项。
只有当前活动的li项(.active)是可见的,其余的通过设置opacity: 0隐藏。
每个内容块(.contain)内有若干个条目(.item),每个条目可以包含多行内容(.itemline),如标题(.head)、选手信息(.player)等。
布局和样式:
使用flex布局和calc()函数来控制元素的尺寸和布局。
为不同的条目提供了圆角、背景色、悬停效果和其他样式细节。
元素显示/隐藏通过透明度和过渡效果来实现。
#daybox{
height:50px;
padding:10px 0;
display:flex;
}
#daybox>.btntomove{
width:70px;
height:100%;
line-height:50px;
text-align: center;
background-color:#09c!important;
border-radius:5px;
margin:0 10px;
user-select: none;
color:#fff;
}
#daybox>.dayitembox{
width:calc(100% - 180px);
overflow-x:auto;
overflow-y:hidden;
}
#daybox ul{
list-style:none;
padding:0;
margin:0;
overflow-x:visible;
overflow-y:hidden;
white-space: nowrap;
}
#daybox ul>li{
display:inline-block;
height:100%;
width:100px;
line-height:50px;
text-align:center;
border-radius:5px;
background-color: #a7a6a6;
color:#f2f2f2;
transition:all 0.3s;
user-select:none;
}
#daybox ul>li.active{
background-color: #09c!important;
}
#daybox ul>li:hover{
background-color:#09c;
color:#fff;
}
#daybox ul>li:not(:last-child){
margin-right:20px;
}
#res_data{
width: 100%;
height: calc(100% - 360px);
/* background-color: #0099cc; */
}
#res_data>ul{
width:100%;
height:100%;
padding:0;
margin:0;
list-style: none;
}
#res_data>ul>li{
height:100%;
width:100%;
position: absolute;
opacity:1;
transition:opacity 1s;
}
#res_data>ul>li:not(.active){
opacity:0;
transition:opacity 1s;
pointer-events:none;
}
#res_data>ul>li .contain{
height:calc(100% - 20px);
width:calc(100% - 20px);
padding:10px;
}
#res_data>ul>li .contain>.item{
width:calc(50% - 50px);
height:auto;
float:left;
margin:15px 25px;
border-radius:5px;
/* box-sizing:border-box; */
overflow:hidden;
background-color: #f2f2f2f0;
}
#res_data>ul>li .contain>.item>.itemline{
width:100%;
border:solid 1px rgba(106, 106, 106, 0.717);
border-radius:5px;
box-sizing:border-box;
}
#res_data>ul>li .contain>.item>.itemline:not(:first-child){
border-top-left-radius:0 !important;
border-top-right-radius: 0 !important;
border-top:none !important;
}
#res_data>ul>li .contain>.item>.itemline:not(:last-child){
border-bottom-left-radius:0 !important;
border-bottom-right-radius: 0 !important;
}
.contain>.item>.itemline.head{
text-align:center;
font-weight:bold;
height:40px;
line-height:40px;
font-size:20px;
}
.contain>.item>.itemline.head>.type{
text-align:center;
font-weight:bold;
height:40px;
line-height:40px;
font-size:20px;
}
.contain>.item>.itemline.player{
height:50px;display:flex;
}
.contain>.item>.itemline.player>*{
vertical-align:middle;
}
.contain>.item>.itemline.player>.flag{
height:50px;
}
.contain>.item>.itemline.player>.flag>img{
border-radius:5px;
height:40px;
width: 80px;
margin:5px;
}
.contain>.item>.itemline.player>.name{
width:auto;
line-height:50px;
height:50px;
}
.win{
font-weight:bold !important;
}
.contain>.item>.itemline.player>.score{
margin:0;
margin-left:auto;
padding:0;
list-style: none;
height:50px;
}
.contain>.item>.itemline.player>.score>li{
display:inline-block;
line-height:50px;
margin-right:5px;
height:50px;
}
这段代码用于实现一个响应式导航栏,具备水平居中、下拉菜单、以及带滑动下划线的悬停效果。
.navbar{
width: 100%;
height: 70px;
background-color: #fff;
/* 盒子阴影 */
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
margin: 0;
z-index: 999;
}
.navbar .nav{
width: 1200px;
height: 100%;
/* 让元素自动水平居中 */
margin: 0 auto;
z-index: 999;
}
.navbar .nav ul{
/* 相对定位 */
position: relative;
/* 弹性布局 */
display: flex;
/* 让子元素平均分配宽度 */
justify-content: space-around;
width: 100%;
height: 100%;
z-index: 999;
}
.navbar .nav ul > li{
width: 100%;
height: 100%;
z-index: 999;
}
.navbar .nav ul > li > a{
/* 因为a元素是行内元素 必须将其转为行内块或者块级才能设置宽度和高度 */
display: block;
width: 100%;
height: 100%;
line-height: 70px;
text-align: center;
z-index: 999;
}
.navbar .nav ul > li ol{
width: 100%;
background-color: #fff;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
/* 让盒子先沿着y轴缩放到0 也就是隐藏了 */
transform: scaleY(0);
/* 我们需要将盒子从上面滑动下来 设置一下缩放的中心点即可 设置到最上面的中间位置 */
transform-origin: 50% 0;
/* 设置过渡 */
transition: all 0.6s;
}
#sign{
position: relative;
margin-top: -50px;
margin-right: 50px;
float: right;
width: 100px;
height: 40px;
border-radius: 10px;
border: 1px solid #a7a6a6;
color: blue;
font-size: 15px;
display: flex;
align-items: center;
justify-content: center;
}
#sign:hover{
border: 1px solid blue;
}
.logo{
float: left;
width: 100px;
height: 70px;
}
.navbar .nav ul > li ol li{
height: 70px;
border-bottom: 1px solid rgb(245, 245, 245);
}
.navbar .nav ul > li ol li a{
display: block;
width: 100%;
height: 100%;
line-height: 70px;
text-align: center;
}
.navbar .nav ul > li ol li:hover{
background-color: rgba(0, 0, 0, 0.03);
}
.navbar .nav ul > li:hover ol{
transform: scaleY(1);
}
.navbar .nav ul .underline{
/* 绝对定位 */
position: absolute;
bottom: 0;
left: 0;
width: 240px;
height: 6px;
/* 设置一下盒子的左上角和右上角的圆角 */
border-top-left-radius: 6px;
border-top-right-radius: 6px;
background-color: #cc3333;
/* 加上过渡 */
transition: all 0.5s;
pointer-events: none;
}
.navbar .nav ul .underline::before{
content: "";
/* 利用::before伪元素设置三角 */
position: absolute;
top: -10px;
/* calc方法自动计算数值 让盒子居中 */
left: calc(50% - 9px);
width: 18px;
height: 10px;
/* inherit可以继承父元素的属性值 */
background-color: inherit;
clip-path: polygon(0 100%,50% 0,100% 100%);
}
.navbar .nav ul > li:nth-child(2):hover ~ .underline{
left: 240px;
background-color: #ff9933;
}
.navbar .nav ul > li:nth-child(3):hover ~ .underline{
left: 480px;
background-color: #339933;
}
.navbar .nav ul > li:nth-child(4):hover ~ .underline{
left: 720px;
background-color: #0099cc;
}
.navbar .nav ul > li:nth-child(5):hover ~ .underline{
left: 960px;
background-color: #9966cc;
}
<div class="navbar">
<img src="img/logo.png" class="logo">
<div class="nav">
<ul>
<li>
<a href="首页.html">首页</a>
</li>
<li>
<a href="晋级图.html">晋级表</a>
</li>
<li>
<a href="每日赛况.html">每日赛程</a>
</li>
<li>
<a href="排名.html">奖牌榜</a>
</li>
<li>
<a href="了解.html">了解更多</a>
</li>
<!-- 这个元素定义滑动的线条 -->
<li class="underline"></li>
</ul>
</div>
</div>
这段代码定义了一个轮播图的类Slide,实现了图片的自动轮播和手动控制功能。核心功能如下:
构造函数 (constructor):
初始化DOM元素,如轮播容器(slideBoxDOM)、左右切换按钮、图片容器(bannerBoxDOM)、分页容器(paginationBoxDOM)。
定义了图片列表(banners),包含若干图片的文件名。
初始化定时器和当前图片索引(_currentIndex)。
图片切换逻辑:
通过currentIndex属性的getter和setter方法实现图片的切换。每次更改索引时,更新当前显示的图片以及分页指示器。
showCurrentBanner()方法用于展示当前、中间、左侧、右侧三张图片,并为左、右两侧图片绑定点击事件,切换到上一张或下一张。
定时器控制:
openTimer()和stopTimer()用于启动和停止定时器,每隔3秒自动切换到下一张图片。
鼠标悬停在轮播图上时,定时器暂停,鼠标移出时,定时器继续。
用户交互:
通过点击左右切换按钮或分页指示器手动切换图片。
鼠标悬停在轮播图时,自动轮播暂停,移开后恢复。
//轮播图类
class Slide{
constructor(){
this.slideBoxDOM = document.querySelector('.slide-box');
this.slideLeftBtnDOM = this.slideBoxDOM.querySelector(".slide-left-btn");
this.slideRightBtnDOM = this.slideBoxDOM.querySelector(".slide-right-btn");
this.bannerBoxDOM = this.slideBoxDOM.querySelector(".banner-box");
this.paginationBoxDOM = this.slideBoxDOM.querySelector(".pagination-box");
// 计数器
this._currentIndex = 0;
this._bannerItemDOMs = null;
// bannerItemDOMs length
this._bannerItemDOMsLen = 0;
// 图片对象数据
this.banners = [
{
imageName: '背景1.jpg',
},
{
imageName: '背景2.jpeg',
},
{
imageName: '背景3.jpg',
},
{
imageName: '背景4.jpg',
},
{
imageName: '背景5.jpeg',
},
{
imageName: '背景6.jpg',
},
{
imageName: '背景7.jpg',
},
];
this.imageUrl = './img/';
//定时器
this.timer = null;
};
get currentIndex(){
return this._currentIndex;
}
//用来监听计数器变化,根据变换来改变当前的横幅
set currentIndex(num){
// 将所有横幅归初始
Object.values(this.bannerItemDOMs).forEach((item,i) =>{
item.classList.remove('left','middle','right');
item.onclick = null;
this.paginationBoxDOM.children[i].classList.remove('chose');
});
if(num < 0){
// this._currentIndex = this._bannerItemDOMsLen - 1;
this._currentIndex = this.bannerItemDOMsLen - 1;
// }else if(num >= this._bannerItemDOMsLen){
} else if (num >= this.bannerItemDOMsLen) {
this._currentIndex = 0;
}else{
this._currentIndex = num;
}
this.paginationBoxDOM.children[this._currentIndex].classList.add('chose');
if(this._currentIndex === 0){
this.showCurrentBanner(this.bannerItemDOMsLen - 1 , this._currentIndex,this._currentIndex + 1);
}else if(this._currentIndex === this.bannerItemDOMsLen - 1){
this.showCurrentBanner(this._currentIndex - 1 , this._currentIndex,0)
}else{
this.showCurrentBanner(this._currentIndex - 1 ,this._currentIndex,this._currentIndex + 1);
}
}
showCurrentBanner(leftIndex,mindleIndex,rightIndex){
this.bannerItemDOMs[leftIndex].classList.add('left');
this.bannerItemDOMs[mindleIndex].classList.add('middle');
this.bannerItemDOMs[rightIndex].classList.add("right");
this.bannerItemDOMs[leftIndex].onclick =() => {
this._currentIndex--;
}
this.bannerItemDOMs[rightIndex].onclick = () =>{
this._currentIndex++;
}
}
getBannerItemDOMs(){
// return this.slideBoxDOM.querySelector('.banner-item');
return this.slideBoxDOM.querySelectorAll('.banner-item');
}
// 获取 banner-itemDOM 字符串,用来渲染 DOM
// getBannerHTML(imageName){
getBannerItemHTML(imageName){
return `<div class="banner-item"><img src="${this.imageUrl+imageName}"></div>`
}
//渲染DOM
drawDOM(banners){
this.bannerBoxDOM.innerHTML = banners.reduce((html,item) => {
// return html + this.getBannerHTML(item.imageName);
return html + this.getBannerItemHTML(item.imageName);
},'');
this.banners.forEach((item,i) => {
const span = document.createElement('span');
span.addEventListener('mouseover',()=>{
this._currentIndex = 1;
});
this.paginationBoxDOM.append(span);
});
}
//启动定时器
openTimer(){
this.timer = setInterval(() => {
this.currentIndex++;
},3000);
}
stopTimer(){
clearInterval(this.timer);
}
init(){
//初始化
this.drawDOM(this.banners);
this.bannerItemDOMs = this.getBannerItemDOMs();
this.bannerItemDOMsLen = this.bannerItemDOMs.length;
this.currentIndex = 0;
//监听事件
this.slideLeftBtnDOM.addEventListener('click',() =>{
this.currentIndex--;
})
this.slideRightBtnDOM.addEventListener('click', () =>{
this.currentIndex++;
})
//自动轮播
this.openTimer();
this.slideBoxDOM.addEventListener('mouseover',() =>{
this.stopTimer();
});
this.slideBoxDOM.addEventListener('mouseout',() =>{
this.openTimer();
})
}
}
new Slide().init();
作为这个奥运会网站项目的成员之一,我们一起经历了从无到有搭建网站的整个过程,这段时间的合作让我学到了很多,也有不少心得想要分享。