目录😋
背景介绍
2022 奥运会满意度调查开始了,冰墩墩作为奥运吉祥物以亲民可爱的形象火遍全球,这次,冰墩墩请你打分,满分 5 分,最低 1 分,快来评分吧!
准备步骤
本题已经内置了初始代码,打开实验环境,目录结构如下:
├── css │ └── index.css ├── index.html └── js └── index.js
其中:
index.html
是主页面。css
是存放页面样式的文件夹。js/index.js
是需要补充代码的 js 文件。注意:打开环境后发现缺少项目代码,请手动键入下述命令进行下载:
wget https://labfile.oss.aliyuncs.com/courses/16474/dundun.zip && unzip dundun.zip && rm dundun.zip
选中
index.html
右键启动 Web Server 服务(Open with Live Server),让项目运行起来。接着,打开环境右侧的【Web 服务】,就可以在浏览器中看到如下效果:
在初始化的时候拖拽红色心情条,冰墩墩脸上并没有效果
目标效果
请在
js/index.js
文件中补全代码,具体需求如下:不同心情下的冰墩墩对应类名如下表:
心情: 类名 不满意 .not-satisfied
有点不满意 .a-little-unsatisfied
普通 .ordinary
满意 .satisfied
棒 .great
- 在初始状态下,冰墩冰墩元素 (
class=BingDunDun
) 处于不满意状态。- 进度条在不同位置时,通过给冰墩冰墩元素 (
class=BingDunDun
) 添加对应类名完成不同心情的效果切切换。- 不能修改原有类名和添加多余类名,例:满意状态下的对应该是
class=BingDunDun satisfied
。完成后效果如下:
要求规定
- 请按照给出的步骤操作,切勿修改默认提供的文件名称、文件夹路径等。
- 满足需求后,保持 Web 服务处于可以正常访问状态,点击「提交检测」系统会自动检测。
判分标准
- 本题完全实现题目目标得满分,否则得 0 分。
通关代码✔️
const range = document.getElementById("range"); // 获取进度条
const BingDunDun = document.querySelector(".BingDunDun"); //获取冰墩墩元素
// 初始状态下,冰墩墩处于不满意状态
BingDunDun.classList.add("not-satisfied");
range.onchange = (e) => {
let value = Number(e.target.value); // value 进度条的值
// 移除冰墩墩元素上所有可能的心情类名
BingDunDun.classList.remove("not-satisfied", "a-little-unsatisfied", "ordinary", "satisfied", "great");
// 根据进度条的值添加对应的心情类名
if (value < 20) {
BingDunDun.classList.add("not-satisfied");
} else if (value < 40) {
BingDunDun.classList.add("a-little-unsatisfied");
} else if (value < 60) {
BingDunDun.classList.add("ordinary");
} else if (value < 80) {
BingDunDun.classList.add("satisfied");
} else {
BingDunDun.classList.add("great");
}
};
代码解析📑
一、HTML 部分
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>2022奥运会满意度调查-冰墩墩心情刻度尺</title> <link rel="stylesheet" type="text/css" href="css/index.css" /> </head> <body> <div id="app"> <!-- 冰墩墩绘制区域 --> <div class="BingDunDun"> <div class="body"></div> <div class="ear1"></div> <div class="ear2"></div> <div class="eye1"></div> <div class="eye2"></div> <div class="face c_blue"></div> <div class="face c_red"></div> <div class="face c_purple"></div> <div class="face c_yellow"></div> <div class="face c_green"></div> <div class="nose"></div> <div class="mouse"></div> <div class="leg1"></div> <div class="leg2"></div> <div class="arm1"></div> <div class="arm2" id="armStyle"></div> <div class="logo"></div> </div> <!-- 进度条 --> <div class="box"> <h1>2022奥运会满意度调查</h1> <div class="diyRange"> <input type="range" max="100" min="0" step="25" value="0" id="range" class="range" /> <span class="gray"></span> <span class="gray1"></span> <span class="gray2"></span> <span class="gray3"></span> <span class="gray4"></span> </div> <div class="ditText"> <span>不满意</span> <span>有点不满意</span> <span>普通</span> <span>满意</span> <span>棒</span> </div> </div> </div> <script src="./js/index.js"></script> </body> </html>
1. 文档声明与头部信息:
<!DOCTYPE html>
:声明文档类型为 HTML5。<meta charset="utf-8" />
:设置字符编码为 UTF - 8,确保页面能正确显示各种字符。<title>2022奥运会满意度调查-冰墩墩心情刻度尺</title>
:设置页面标题,显示在浏览器标签栏。<link rel="stylesheet" type="text/css" href="css/index.css" />
:引入外部 CSS 文件,用于设置页面样式。2. 主体部分:
<div id="app">
:作为整个页面内容的容器。<div class="BingDunDun">
:冰墩墩的绘制区域,包含多个子元素,用于构建冰墩墩的各个部分,如身体、耳朵、眼睛等。<div class="box">
:进度条及其相关说明的容器。
<h1>2022奥运会满意度调查</h1>
:显示调查标题。<input type="range" max="100" min="0" step="25" value="0" id="range" class="range" />
:创建一个范围输入框(进度条),最小值为 0,最大值为 100,步长为 25,初始值为 0。<span class="gray"></span>
到<span class="gray4"></span>
:用于装饰进度条的灰色圆点。<div class="ditText">
:包含五个<span>
元素,分别显示不同的满意度描述,如 “不满意”“有点不满意” 等。3. 脚本引入:
<script src="./js/index.js"></script>
:引入外部 JavaScript 文件,用于实现进度条与冰墩墩心情的交互逻辑。
二、CSS 部分
* { margin: 0; padding: 0; } body { background-color: #5D75B3; color: #000000; display: flex; justify-content: center; align-items: center; } .BingDunDun { width: 600px; height: 500px; position: relative; } .body { width: 362px; height: 410px; border: 8px solid #393939; border-radius: 88% 88% 62% 68% / 82% 82% 95% 84%; position: absolute; left: 109px; top: 10px; background: #fff; } .ear1, .ear2 { width: 81px; height: 115px; background-color: #393939; border-radius: 50%; position: absolute; z-index: -1; } .ear1 { left: 150px; top: 16px; transform: rotate(-10deg); } .ear2 { left: 362px; top: 18px; transform: rotate(10deg); } /* 眼睛 */ .eye1, .eye2 { background-color: #393939; width: 83px; height: 115px; border-radius: 50%; position: absolute; } .eye1 { left: 185px; top: 102px; transform: rotate(45deg); } .eye2 { left: 329px; top: 102px; transform: rotate(-45deg); } .eye1:before, .eye2:before { content: ""; width: 40px; height: 40px; border: #fff 5px solid; border-radius: 100%; position: absolute; } .eye1:before { right: 10px; top: 22px; } .eye2:before { left: 10px; top: 22px; } .eye1:after, .eye2:after { content: ""; width: 10px; height: 10px; background: #9b9b9b; border-radius: 100%; position: absolute; } .eye1:after { right: 27px; top: 31px; } .eye2:after { left: 41px; top: 44px; } .face { position: absolute; border-radius: 48% 48% 44% 49%/ 53% 54% 45% 47%; } .c_blue { border: #6bcdf3 5px solid; width: 280px; height: 224px; left: 150px; top: 58px; } .c_red { border: #af2350 5px solid; width: 287px; height: 233px; left: 146px; top: 53px; } .c_purple { border: #5d75b3 5px solid; width: 295px; height: 240px; left: 142px; top: 50px; } .c_yellow { border: #ffc346 5px solid; width: 305px; height: 248px; left: 137px; top: 45px; } .c_green { border: #7fcb58 5px solid; width: 313px; height: 256px; left: 133px; top: 41px; } .nose { position: absolute; left: 284px; top: 167px; width: 28px; height: 18px; background-color: #333333; border-radius: 42px 42px 60px 61px/ 30px 30px 50px 46px; } .mouse { position: absolute; left: 265px; top: 185px; width: 68px; height: 25px; border-radius: 48%; border: #393939 7px solid; border-top: none; border-left: 0; border-right: 0; } .leg1, .leg2 { background: #333; position: absolute; width: 83px; height: 80px; border-radius: 0 0 30px 30px; z-index: -1; } .leg1 { left: 187px; top: 403px; } .leg2 { left: 328px; top: 403px; } .leg1::before, .leg2:before { content: ""; width: 43px; height: 30px; position: absolute; background: #363636; border-radius: 30px; } .leg1:before { bottom: 0; right: -3px; } .leg2:before { bottom: 0; left: -3px; } .arm1, .arm2 { background: #333; position: absolute; z-index: -1; } .arm1 { width: 75px; height: 118px; left: 64px; top: 224px; transform: rotate(45deg); border-radius: 24% 69% 68% 76%/ 53% 95% 40% 52%; } .arm2 { width: 75px; height: 148px; left: 444px; top: 204px; transform: rotate(135deg); border-radius: 56% 62% 98% 6%/ 40% 46% 80% 58%; } .arm2:before { content: ""; width: 16px; height: 24px; background: #bc242c; position: absolute; border-top-left-radius: 50%; border-top-right-radius: 50%; transform: rotate(45deg); left: 32px; top: 20px; } .arm2:after { content: ""; width: 16px; height: 24px; background: #bc242c; position: absolute; border-top-left-radius: 50%; border-top-right-radius: 50%; transform: rotate(-45deg); left: 26px; top: 20px; } .logo { width: 70px; height: 100px; background-size: cover; position: absolute; left: 265px; top:310px } .box { height: 100px; max-width: 450px; width: 100%; background-color: whitesmoke; border-radius: 5px; padding: 25px; margin: -15px auto; box-shadow: 0 2px 10px 1px bisque; text-align: center; } .diyRange { padding: 10px 0; position: relative; } .ditText { width: 450px; position: relative; } .ditText > span { position: absolute; } .ditText > span:nth-of-type(1) { left: 10px; } .ditText > span:nth-of-type(2) { left: 90px; } .ditText > span:nth-of-type(3) { left: 210px; } .ditText > span:nth-of-type(4) { left: 306px; } .ditText > span:nth-of-type(5) { left: 405px; } .range { position:relative ; z-index: 100; -webkit-appearance: none; appearance: none; margin: 0; outline: 0; background-color: transparent; width: 400px; } .range::-webkit-slider-runnable-track { height: 4px; background: #eee; } [type="range" i]::-webkit-slider-container { height: 20px; overflow: hidden; } .range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%; background-color: #f44336; border: 1px solid transparent; margin-top: -8px; border-image: linear-gradient(#f44336,#f44336) 0 fill / 8 20 8 0 / 0px 0px 0 2000px; } .mouseStyleInit { top: 185px; height: 25px; border-radius: 48%; } @keyframes jump { 50% { transform: translateY(-20px); } } .gray, .gray1, .gray2, .gray3, .gray4 { position: absolute; display: inline-block; width: 20px; height: 20px; background-color: #e1e2ec; border-radius: 50%; z-index: 1; } .gray { left: 24px; top: 10px; } .gray1 { left: 120px; top: 10px; } .gray2 { left: 215px; top: 10px; } .gray3 { left: 310px; top: 10px; } .gray4 { left: 405px; top: 10px; } .not-satisfied .arm2,.a-little-unsatisfied .arm2,.ordinary.arm2, .satisfied .arm2 { left: 444px; top: 204px; transform: rotate(135deg); } /* 不满意 */ .not-satisfied .mouse { height: 80px!important; transform: rotate(180deg); top: 195px!important;; } /* 有点不满意 */ .a-little-unsatisfied .mouse{ height: 40px!important; top: 215px!important;; transform: rotate(180deg); } /* 普通 */ .ordinary .mouse{ height: 25px!important; border-radius: 20%!important; top: 185px!important; } /* 满意 */ .satisfied .mouse{ height: 25px!important; } /* 棒 */ .great{ animation: jump 0.5s infinite } .great .mouse{ height : 70px!important; } .great .arm2{ left: 463px; top: 142px; transform: rotate(37deg); }
1. 满意度描述文字样式:
.ditText
:设置包含满意度描述文字的容器宽度和定位方式。.ditText > span
:对每个描述文字元素进行绝对定位。.ditText > span:nth - of-type(n)
:分别设置每个描述文字元素的水平位置,使其均匀分布在进度条下方。2. 自定义范围输入框(进度条)样式:
.range
:设置进度条的基本样式,包括去除默认样式、设置宽度和背景透明等。.range::-webkit - slider - runnable - track
:设置进度条轨道的高度和背景颜色。[type="range" i]::-webkit - slider - container
:设置进度条容器的高度和溢出处理。.range::-webkit - slider - thumb
:设置进度条滑块的样式,包括大小、颜色和边框等。3. 嘴巴初始样式:
.mouseStyleInit
:定义冰墩墩嘴巴的初始样式,包括位置、高度和圆角。4. 跳跃动画:
@keyframes jump
:定义一个名为jump
的动画,在 50% 的时间点,冰墩墩向上移动 20px,实现跳跃效果。5. 进度条上的灰色圆点样式:
.gray
到.gray4
:设置进度条上五个灰色圆点的样式,包括位置、大小、颜色和圆角。6.不同心情下的样式:
.not - satisfied .arm2
,.a - little - unsatisfied .arm2
,.ordinary.arm2
,.satisfied .arm2
:设置不满意、有点不满意、普通和满意这几种心情下冰墩墩手臂的位置和旋转角度。.not - satisfied .mouse
:设置不满意心情下冰墩墩嘴巴的高度、旋转角度和位置。.a - little - unsatisfied .mouse
:设置有点不满意心情下冰墩墩嘴巴的高度、旋转角度和位置。.ordinary .mouse
:设置普通心情下冰墩墩嘴巴的高度、圆角和位置。.satisfied .mouse
:设置满意心情下冰墩墩嘴巴的高度。.great
:设置 “棒” 这种心情下冰墩墩元素应用jump
动画,使其不断跳跃。.great .mouse
:设置 “棒” 这种心情下冰墩墩嘴巴的高度。.great .arm2
:设置 “棒” 这种心情下冰墩墩手臂的位置和旋转角度。
三、JavaScript 部分
const range = document.getElementById("range"); // 获取进度条 const BingDunDun = document.querySelector(".BingDunDun"); //获取冰墩墩元素 // 初始状态下,冰墩墩处于不满意状态 BingDunDun.classList.add("not-satisfied"); range.onchange = (e) => { let value = Number(e.target.value); // value 进度条的值 // 移除冰墩墩元素上所有可能的心情类名 BingDunDun.classList.remove("not-satisfied", "a-little-unsatisfied", "ordinary", "satisfied", "great"); // 根据进度条的值添加对应的心情类名 if (value < 20) { BingDunDun.classList.add("not-satisfied"); } else if (value < 40) { BingDunDun.classList.add("a-little-unsatisfied"); } else if (value < 60) { BingDunDun.classList.add("ordinary"); } else if (value < 80) { BingDunDun.classList.add("satisfied"); } else { BingDunDun.classList.add("great"); } };
1. 获取元素:
const range = document.getElementById("range");
:通过id
获取进度条元素,后续可以监听其值的变化。const BingDunDun = document.querySelector(".BingDunDun");
:通过类名获取冰墩墩元素,用于后续动态修改其样式类。2. 设置初始状态:
BingDunDun.classList.add("not-satisfied");
:在页面加载完成后,给冰墩墩元素添加not-satisfied
类名,使其初始状态呈现为 “不满意” 的表情,这是根据 CSS 中定义的.not-satisfied
相关样式来实现外观展示的。3. 监听进度条变化事件:
range.onchange = (e) => {...}
:为进度条元素绑定change
事件监听器。当用户拖动进度条,改变其值时,会触发这个箭头函数。let value = Number(e.target.value);
:在事件处理函数中,首先获取当前进度条的值,并将其转换为数字类型,存储在value
变量中,方便后续根据该值进行逻辑判断。4. 移除已有心情类名:
BingDunDun.classList.remove("not-satisfied", "a-little-unsatisfied", "ordinary", "satisfied", "great");
:每次进度条值改变时,先移除冰墩墩元素上所有与心情相关的类名。这样做是为了避免多个心情类名同时存在于元素上,保证每次只应用一个心情状态对应的样式。5. 根据进度条值添加对应心情类名:
- 通过一系列的
if - else if - else
条件判断语句,根据进度条的当前值value
来决定给冰墩墩元素添加哪个心情类名:- 当
value < 20
时,添加not-satisfied
类名,冰墩墩呈现 “不满意” 的表情。- 当
20 <= value < 40
时,添加a-little-unsatisfied
类名,冰墩墩呈现 “有点不满意” 的表情。- 当
40 <= value < 60
时,添加ordinary
类名,冰墩墩呈现 “普通” 的表情。- 当
60 <= value < 80
时,添加satisfied
类名,冰墩墩呈现 “满意” 的表情。- 当
value >= 80
时,添加great
类名,冰墩墩呈现 “棒” 的表情,同时会触发 CSS 中定义的跳跃动画。
四、工作流程▶️
页面加载阶段
- 浏览器解析 HTML 文件,构建 DOM 树,同时加载外部 CSS 文件和 JavaScript 文件。
- HTML 中的元素按照结构进行布局,冰墩墩的各个部分(身体、耳朵、眼睛等)根据 CSS 样式规则进行绘制,进度条及其相关说明也显示在页面上。
- JavaScript 代码开始执行,获取进度条元素和冰墩墩元素,然后给冰墩墩元素添加
not-satisfied
类名,使冰墩墩初始状态为 “不满意” 表情。用户交互阶段
- 用户拖动进度条,改变其值。
- 进度条的
change
事件被触发,JavaScript 中的事件处理函数开始执行。- 事件处理函数首先获取进度条的当前值,接着移除冰墩墩元素上所有与心情相关的类名。
- 根据进度条的值,通过条件判断给冰墩墩元素添加对应的心情类名。
样式更新阶段
- 当冰墩墩元素的类名发生变化后,浏览器会根据新的类名重新计算并应用 CSS 样式。
- 例如,如果添加了
great
类名,冰墩墩的嘴巴和手臂样式会按照.great .mouse
和.great .arm2
的规则进行更新,同时冰墩墩元素会开始执行jump
动画,从而实现冰墩墩心情随着进度条值变化而改变的效果。通过 HTML 提供页面结构、CSS 定义样式和动画、JavaScript 实现交互逻辑,三者协同工作,完成了冰墩墩心情刻度尺的效果展示。