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
。完成后效果如下:
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");
}
};
<!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 文件,用于实现进度条与冰墩墩心情的交互逻辑。* {
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
:设置 “棒” 这种心情下冰墩墩手臂的位置和旋转角度。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 中定义的跳跃动画。四、工作流程▶️
not-satisfied
类名,使冰墩墩初始状态为 “不满意” 表情。change
事件被触发,JavaScript 中的事件处理函数开始执行。great
类名,冰墩墩的嘴巴和手臂样式会按照 .great .mouse
和 .great .arm2
的规则进行更新,同时冰墩墩元素会开始执行 jump
动画,从而实现冰墩墩心情随着进度条值变化而改变的效果。通过 HTML 提供页面结构、CSS 定义样式和动画、JavaScript 实现交互逻辑,三者协同工作,完成了冰墩墩心情刻度尺的效果展示。