第一个 扩展卡片

html

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>1_expandcards</title>

<link rel="stylesheet" href="style.css">

</head>

<body>

<div class="container">

<div class="panel active"

style="background-image: url('https://img.alicdn.com/imgextra/i1/3879597254/O1CN01qwoV4K23SO1ou7Xxw_!!3879597254.jpg');" >

<h3>茄子</h3>

</div>

<div class="panel"

style="background-image: url('https://img.alicdn.com/imgextra/i1/3879597254/O1CN01qwoV4K23SO1ou7Xxw_!!3879597254.jpg');" >

<h3>茄子</h3>

</div>

<div class="panel"

style="background-image: url('https://img.alicdn.com/imgextra/i1/3879597254/O1CN01qwoV4K23SO1ou7Xxw_!!3879597254.jpg');" >

<h3>茄子</h3>

</div>

<div class="panel"

style="background-image: url('https://img.alicdn.com/imgextra/i1/3879597254/O1CN01qwoV4K23SO1ou7Xxw_!!3879597254.jpg');" >

<h3>茄子</h3>

</div>

<div class="panel"

style="background-image: url('https://img.alicdn.com/imgextra/i1/3879597254/O1CN01qwoV4K23SO1ou7Xxw_!!3879597254.jpg');" >

<h3>茄子</h3>

</div>

<script src="script.js"></script>

</div>

</body>

</html>

css

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
* {

/* 标准盒模型 */

box-sizing: border-box;

}



body {

display: flex;

/* 让元素在容器里面上下居中。 */

align-items: center;

/* 让元素在容器里面左右居中。 */

justify-content: center;

/* 类似于100%宽度 */

/* 把这个元素的高度设置成100% 的视口高度(Viewport Height)。

vh 是一个单位,叫做 viewport height,意思是视口高度的1%。 */

height: 100vh;

overflow: hidden;

margin: 0;

}



.container {

display: flex;

width: 90vh;

}



.panel {

background-size: cover;

background-position: center;

height: 80vh;

border-radius: 50px; /* 圆角 */

color: white;

/* 鼠标变成手 */

cursor: pointer;

flex: 0.5;

margin: 10px;

/* 相对定位 */

position: relative;

transition: all 300ms ease-in;

/* 添加阴影只在右下角 */

/* 给元素添加一个向下偏移10px、宽松模糊20px、半透明黑色的阴影。 */

box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);

}



.panel h3 {

font-size: 2;

position: absolute;

bottom: 20px;

left: 20px;

margin: 0;

/* 透明度0 */

opacity: 0;

}



/* panel和active两个类支持用空格分开 */

.panel.active {

/* 布局等分 */

flex: 5;

}



.panel.active h3 {

/* 透明度1 */

opacity: 1;

/* 过渡时间 0.2s opacity透明度 ease-in 先慢后快 0.2 延迟0.2 */

transition: opacity 0.2s ease-in 0.2s;

}

js

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
37
38
39
40
41
42
43
44
45
// 获取所有有类名panel列的合集

const panels = document.querySelectorAll('.panel');// 获取所有panel元素类名而不是id名

// panels是一个NodeList对象,包含了所有具有类名panel的元素



// 遍历每个panel

panels.forEach(panel => {

// 为每个panel添加点击监听事件

panel.addEventListener('click', () => {

// 移除所有panel的active类

removeActiveClasses();

// 为当前点击的panel添加active类

panel.classList.add('active');

});

});



// 定义一个函数来移除所有panel的active类

function removeActiveClasses() {

// 遍历每个panel

panels.forEach(panel => {

// 移除active类

panel.classList.remove('active');

});

}

第二个 步骤器

html

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
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>第二个 步骤器</title>

<link rel="stylesheet" href="style.css">

</head>

<body>

<div class="container">

<div class="progress_container">

<div class="progress" id="progress"></div>

<div class="circle active">1</div>

<div class="circle">2</div>

<div class="circle">3</div>

<div class="circle">4</div>

</div>

<button class="btn" id="prev" disabled>prev</button>

<button class="btn" id="next">next</button>

</div>

<script src="script.js"></script>

</body>

</html>

css

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
:root {

/* :root 是 CSS 中的根选择器,表示文档的根元素,一般用于定义全局变量(如颜色) */

--line-border-fill: #3498db; /* 变量:激活状态下的蓝色 */

--line-border-empty: #e0e0e0; /* 变量:未激活状态下的灰色 */

}



* {

/* * 是通配选择器,表示“选中所有元素”,这里用来统一设置盒模型 */

box-sizing: border-box; /* 所有元素的宽度包含 padding 和 border,不会超出设定宽度 */

}



body {

background-color: #f6f7fb; /* 设置页面背景颜色 */

display: flex; /* 使用 flex 让内容居中 */

justify-content: center; /* 水平居中对齐 */

align-items: center; /* 垂直居中对齐 */

height: 100vh; /* 高度为 100% 视口高度 */

overflow: hidden; /* 防止内容溢出,隐藏滚动条 */

}



.container {

/* .container 是类选择器,表示 class="container" 的元素 */

text-align: center; /* 容器内文本居中 */

}



.progress_container {

/* .progress_container 也是类选择器,表示进度条区域的容器 */

display: flex; /* 使用 flex 横向排列圆圈 */

justify-content: space-between;/* 圆圈平均分布在两侧,中间留空(有问题的核心点) */

align-items: center; /* 垂直方向居中对齐 */

position: relative; /* 设置相对定位,供子元素使用绝对定位 */

margin-bottom: 30px; /* 与下方按钮留出空隙 */

max-width: 100%; /* 最大宽度为父元素宽度 */

width: 350px; /* 设置固定宽度 */

}



/* ::before 是伪元素选择器,在 .progress_container 元素“前”插入内容,常用于背景线 */

.progress_container::before {

content: ''; /* 必须有 content 属性,哪怕是空字符串 */

background-color: var(--line-border-empty); /* 灰色背景线 */

position: absolute; /* 使用绝对定位铺满容器 */

top: 50%; /* 距顶部 50%,配合 transform 做垂直居中 */

left: 0; /* 从左边开始 */

transform: translateY(-50%); /* 向上移动自身高度的 50%,实现完美垂直居中 */

height: 4px; /* 设置线条的高度 */

width: 100%; /* 宽度为容器宽度 */

z-index: -1; /* 层级调低,放在圆圈下方 */

}



.progress {

/* .progress 是“当前进度”线条,动态变化宽度 */

background-color: var(--line-border-fill); /* 蓝色表示已完成部分 */

position: absolute; /* 使用绝对定位铺在伪元素上面 */

top: 50%; /* 同样居中 */

left: 0; /* 从最左侧开始 */

transform: translateY(-50%); /* 垂直居中 */

height: 4px; /* 线条高度 */

width: 0%; /* 初始宽度为 0,会由 JS 控制变化 */

z-index: -1; /* 层级在圆圈后面 */

}



.circle {

/* .circle 是表示步骤圆圈的类选择器 */

background-color: #fff; /* 圆圈背景色为白色 */

color: #999; /* 数字颜色为灰色 */

border-radius: 50%; /* 设置成圆形 */

width: 30px; /* 圆圈宽度 */

height: 30px; /* 圆圈高度 */

display: flex; /* 使用 flex 实现文字居中 */

justify-content: center; /* 水平居中 */

align-items: center; /* 垂直居中 */

border: 3px solid var(--line-border-empty); /* 默认灰色边框 */

transition: all 0.4s ease; /* 添加过渡动画,切换时更平滑 */

}



/* .circle.active 是带有 active 状态的圆圈的选择器,表示当前步骤 */

.circle.active {

border-color: var(--line-border-fill); /* 激活状态使用蓝色边框 */

}



.btn:active {

/* :active 是伪类选择器,表示按钮被点击时的状态 */

transform: scale(0.98); /* 按钮被按下时稍微缩小,增强点击感 */

}



.btn:focus {

/* :focus 是伪类选择器,表示按钮获得焦点时 */

outline: 0; /* 去掉默认的浏览器焦点边框 */

}



.btn:disabled {

/* :disabled 是伪类选择器,表示按钮不可点击状态 */

background-color: var(--line-border-empty); /* 灰色背景表示不可用 */

cursor: not-allowed; /* 鼠标变为禁止符号 */

}

js

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 获取进度条元素

const progress = document.getElementById('progress');



// 获取“上一步”按钮元素

const prev = document.getElementById('prev');



// 获取“下一步”按钮元素

const next = document.getElementById('next');



// 获取所有步骤圆圈(节点)

const circles = document.querySelectorAll('.circle');



// 当前处于第几个步骤,初始为第 1 步

let currentActive = 1;



// 监听“下一步”按钮点击事件

next.addEventListener('click', () => {

currentActive++; // 当前步骤 +1

if (currentActive > circles.length) {

// 如果超过总步骤数量,限制在最大值

currentActive = circles.length;

}

update(); // 调用更新函数,刷新界面

});



// 监听“上一步”按钮点击事件

prev.addEventListener('click', () => {

currentActive--; // 当前步骤 -1

if (currentActive < 1) {

// 如果小于 1,限制为最小值

currentActive = 1;

}

update(); // 调用更新函数,刷新界面

});



// 更新进度条和圆圈状态的函数

function update() {

// 遍历每个圆圈,控制是否添加 .active 类

circles.forEach((circle, index) => {

if (index < currentActive) {

circle.classList.add('active'); // 当前及之前的步骤添加激活样式

} else {

circle.classList.remove('active'); // 后面的步骤去掉激活样式

}

});



// 获取当前所有处于激活状态的圆圈(有 .active 类)

const actives = document.querySelectorAll('.active');



// 根据当前激活圆圈数量,动态设置进度条宽度

// 比如共 4 个圆圈,当前是第 2 个: (2-1)/(4-1) = 1/3 = 33.3%

progress.style.width = ((actives.length - 1) / (circles.length - 1)) * 100 + '%';



// 控制按钮的禁用状态

if (currentActive === 1) {

prev.disabled = true; // 当前是第一步时,禁用“上一步”按钮

} else if (currentActive === circles.length) {

next.disabled = true; // 当前是最后一步时,禁用“下一步”按钮

} else {

// 中间步骤时,两个按钮都可用

prev.disabled = false;

next.disabled = false;

}

}