文远知行前端笔试题

题目要求

使用html + js写一个马赛克画板(2.1版):

  1. 页面加载时,要求用户输入两个整数,对应长和宽,例如100, 200
  2. 页面显示100*200个格子,要求占满整个窗口,不能有滚动条
  3. 当窗口大小变化时,依然要满足上述条件
  4. 鼠标左键点击任意一个格子时,填入一个随机颜色,即#000000-#FFFFFF中的一种
  5. 鼠标右键点击此格子时,擦除颜色
  6. 每次进行上述的第4点或者第5点的操作时,显示“颜色岛“的总数量。一个颜色岛就是相连的所有已着色区域块,如下图中,总数量是3。要求这个数字以美观的形式展示,1秒后自动消失,具体样式和动画可以自行设计。
  7. 敲击键盘c键时,对每一个已着色的格子随机产生另一个颜色,并且设计一个渐变动画,每个格子要从原来的颜色渐变成新颜色

实现方案

Snipaste_2021-03-18_18-19-05

Snipaste_2021-03-18_18-19-24

思路

  • 对于要求1、2、3,可以通过动态创建 table 元素或者使用 Grid 实现;
  • 对于要求4和5可以通过事件委托实现,避免绑定太多事件;
  • 对于要求7,可以开一个全局数组保存当前已经设置了颜色的元素,每次进行4和5的操作时就修改该数组;
  • 对于要求6,可以通过上面定义的全局数组采用DFS遍历实现。

代码

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>马赛克画板 2.1</title>
</head>
<body>
<div class="container">
<div class="init">
<form>
<h1>马赛克画板</h1>
<div class="form-item">
<label for="rows">行数:</label>
<input type="number" name="rows" style="width: 200px;" />
</div>
<div class="form-item">
<label for="cols">列数:</label>
<input type="number" name="cols" style="width: 200px;" />
</div>
<div class="form-item">
<button id="init-submit">初始化网格</button>
</div>
</form>
</div>
<div class="stage"></div>
</div>
<script src="script.js"></script>
</body>
</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
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
@import "normalize.css";

html, body {
height: 100%;
}

.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
overflow: hidden;
}

.container .init {
padding: 25px 20px;
border-radius: 20px;
border-color: rgba(0,0,0,.5);
box-shadow: 0 2px 8px rgba(0,0,0,.3);
z-index: 999;
}

.container .init h1 {
margin-bottom: 30px;
text-align: center;
color: rgba(0,0,0,.75);
}

.container .init .form-item {
margin-top: 20px;
}

.container .init label {
margin-right: 8px;
text-align: right;
color: rgba(0,0,0,.75);
}

.container .init input[type=number] {
box-sizing: border-box;
display: inline-block;
height: 32px;
padding: 4px 11px;
outline: 0;
font-size: 14px;
line-height: 1.5;
color: rgba(0,0,0,.65);
background-color: #fff;
border: 1px solid #d9d9d9;
border-radius: 4px;
transition: all .3s;
}

.container .init input[type=number]:hover {
border-color: #40a9ff;
border-right-width: 1px !important;
}

.container .init #init-submit {
height: 36px;
width: 100%;
padding: 0 15px;
outline: none;
font-size: 14px;
text-align: center;
color: #fff;
background-color: #1890ff;
border-width: 0;
border-color: #1890ff;
border-radius: 5px;
cursor: pointer;
}

.container .stage {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}

.container .stage table {
height: 100%;
width: 100%;
border-collapse: collapse;
}

.container .stage td {
box-sizing: border-box;
border: 1px solid #cccccc;
transition: all .5s;
}

.container p.message {
position: absolute;
top: 10px;
margin: 0 auto;
padding: 5px 12px;
font-size: 20px;
background-color: #fff;
border: 1px solid #999;
border-radius: 6px;
z-index: 999;
animation: fadeIn 1s forwards;
}

.container p.message span {
font-size: 26px;
font-weight: 600;
color: #ff6060;
}

@keyframes fadeIn {
0% {
transform: translateY(0);
opacity: 0;
}
50% {
transform: translateY(50px);
opacity: 1;
}
100% {
transform: translateY(50px);
opacity: 0;
}
}
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
const tools = {
$: function (selector) {
let elems = document.querySelectorAll(selector);
if (elems.length === 1) {
return elems[0];
}
return elems;
},
randomIntRange: function (min, max) {
return parseInt(Math.random() * (max - min) + min, 10);
},
randomColor: function () {
return (
"rgb(" +
randomIntRange(0, 255) +
", " +
randomIntRange(0, 255) +
", " +
randomIntRange(0, 255) +
")"
);
},
compareGrid(g1, g2) {
if (g1.dataset.col === g2.dataset.col && g1.dataset.row === g2.dataset.row) {
return true;
} else {
return false;
}
},
};
// 合并工具函数到 window
Object.assign(window, tools);

// 全局变量
var coloredGrids = [];

window.onload = function () {
$("#init-submit").onclick = function (event) {
event.preventDefault();
let rows = $("input[name=rows]").value;
let cols = $("input[name=cols]").value;
if (rows && cols) {
$(".init").style.display = "none";
initGrids(rows, cols);
} else {
alert("请输入正确的数字");
}
};
};

function initGrids(rows, cols) {
let table = document.createElement("table");
table.className = "sketchpad";

// 监听按键
document.addEventListener("keyup", handleKeyPress);

// 屏蔽鼠标右键默认菜单事件
document.oncontextmenu = function (event) {
event.preventDefault();
};

table.addEventListener("mousedown", handleClick, false);

for (let i = 0; i < rows; i++) {
let tr = document.createElement("tr");
tr.className = "sketchpad-grid-row";
for (let j = 0; j < cols; j++) {
let td = document.createElement("td");
td.className = "sketchpad-grid-col";
td.setAttribute("data-row", i);
td.setAttribute("data-col", j);
tr.appendChild(td);
}
table.appendChild(tr);
}

$(".stage").appendChild(table);
}

function handleKeyPress(event) {
if (event.keyCode !== 67) {
return;
}

if (coloredGrids.length) {
coloredGrids.forEach(function (grid, index) {
grid.style.backgroundColor = randomColor();
});
}
}

function handleClick(event) {
if (event.button === 0) {
event.target.style.backgroundColor = randomColor();
coloredGrids.push(event.target);
}
if (event.button === 2) {
event.target.style.backgroundColor = "";
let index = coloredGrids.indexOf(event.target);
coloredGrids.splice(index, 1);
}


let colorIslands = countColorIslands();
let message = document.createElement("p");
message.className = "message";
message.innerHTML = "当前颜色岛数量为: <span>" + colorIslands + "<span>";
$(".container").appendChild(message);
setTimeout(function() {
$(".container").removeChild(message);
}, 1000);
}

function countColorIslands() {
let temp = coloredGrids.slice(0);
let visited = Array(temp.length).fill(false);
let result = [];

function dfs(grid, index) {
visited[index] = true;
getColoredNeighbors(grid).forEach((i) => {
if (!visited[i]) dfs(coloredGrids[i], i);
});
};

temp.forEach((grid, index) => {
if (!visited[index]) {
result.push(grid);
dfs(grid, index);
}
});

return result.length;
}

function getColoredNeighbors(elem) {
let x = +elem.dataset.col;
let y = +elem.dataset.row;
let neighbors = [];

for (let i = (x - 1 < 0 ? 0 : x - 1); i <= x + 1; i++) {
for (let j = (y - 1 < 0 ? 0 : y - 1); j <= y + 1; j++) {
if (!(i === x && j === y) && (i === x || j === y)) {
if ($("td[data-col='" + i + "'][data-row='" + j + "']").style.backgroundColor !== "") {
coloredGrids.forEach((grid, index) => {
if (compareGrid($("td[data-col='" + i + "'][data-row='" + j + "']"), grid)) {
neighbors.push(index);
}
});
}
}
}
}

return neighbors;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!