昨天晚上做攜程的筆試,第三道題是讓模擬一個排序的步驟,也就是將排序的過程以動畫的形式表現出來。
即這種效果
首先贊一下攜程的這道題目,這才是前端該做的題目,既有意思,又考察了排序演算法,還考察了部分動畫及 dom 操作。話不多說,分析一下這道題目。首先是頁面基本結構
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>排序動畫演示</title>
</head>
<body>
<div class="container">
</div>
</body>
</html>
複製程式碼
然後給待排序元素加個樣式
.container{
text-align:center;
}
.sort{
position:absolute;
width:50px;
height:50px;
line-height:50px;
border:1px solid black;
transition:1s;
}
複製程式碼
為什麼絕對定位呢,首先絕對定位可以讓元素脫離文件流,能夠儘量減少重排,而且絕對定位的位置方便計算,所以這裡採用絕對定位,當然 fixed 也是可以的。然後最重要的就是動畫的持續時間設定為 1s
然後首先將待排序元素輸出到介面
var arr = [5,4,8,9,6,5,4,12,3,6,7,8,56];
var container = document.querySelector('.container');
var fragment = document.createDocumentFragment(); // 建立文件片段,儘量減少重繪和重排
var len = arr.length;
for(let i = 0; i < len; i++ ){
var node = document.createElement('div');
node.className = 'sort';
node.id = i; // 這個後面移動位置的時候需要用到
node.style.left = i * 60 + 'px';
fragment.append(node);
}
container.append(fragment);
複製程式碼
至此,把待排序元素先輸出到頁面上了。
然後開始處理排序,這裡採用氣泡排序,正常的氣泡排序結構是這樣的for(let i = 0; i < len; i++){
for(let j = 0; j < len - i; j++){
if(arr[j] > arr[j+1]){
// 這裡使用了 ES6 的解構賦值,即交換兩個元素的值
[arr[j],arr[j+1]] = [arr[j+1],arr[j]];
// 也可以這樣
/*
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
*/
}
}
}
複製程式碼
解構賦值還是很好用的,推薦使用結構賦值
那麼我們要實現氣泡排序的動畫該怎麼辦呢。
首先我們要獲取交換的兩個元素距離左邊長度,然後交換這兩個元素的位置,還記得我們之前給元素賦值了 ID 嗎,我們可以通過 ID 來找到這兩個元素。
var x = document.getElementById(j)
var y = document.getElementById(j+1);
// 這裡同樣採用解構賦值
[x.style.left,y.style.left] = [y.style.left,x.style.left];
// 記得 id 也要交換
[x.id,y.id]=[y.id,x.id];
複製程式碼
至此,我們做完了該做的一切,但是直接把這段程式碼加入到氣泡排序裡面的話那我們直接看到的就是排序完成的效果了,看不到中間的過程,那要怎麼樣才能看到排序的過程呢,這個時候我們可以使用 setTimeout。
冒泡部分的程式碼如下
var time = 1;
for(let i = 0; i < len; i++){
for(let j = 0; j < len - i; j++){
if(arr[j] > arr[j+1]){
[arr[j],arr[j+1]] = [arr[j+1],arr[j]];
setTimeout(function(){
var x = document.getElementById(j)
var y = document.getElementById(j+1);
[x.style.left,y.style.left] = [y.style.left,x.style.left];
[x.id,y.id] = [y.id,x.id];
},time * 1000)
time++;
}
}
}
複製程式碼
time 是為了讓每次的效果都顯示出來,如果只是 1000 的話,那麼這個動畫 1s 之內就會完成,如果不清楚可以複習一下事件迴圈的相關知識。
至此,程式碼結束
全部程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>排序</title>
<style>
.container{
text-align: center;
}
.sort{
transition: 1s;
height: 50px;
width: 50px;
border: 1px solid black;
line-height: 50px;
position: absolute;
}
</style>
</head>
<body>
<div class="container">
</div>
<script>
var arr = [5,4,8,9,6,5,4,12,3,6,7,8,56];
var container = document.querySelector('.container');
var fragment = document.createDocumentFragment();
var len = arr.length;
for(let i = 0; i < len;i++){
var temp = document.createElement('div');
temp.className = 'sort';
temp.style.left = i*60 +'px';
temp.id = i;
temp.innerHTML = arr[i];
fragment.append(temp);
}
container.append(fragment);
var time = 1;
for(let i = 0; i < len; i++){
for(let j = 0; j < len - i; j++){
if(arr[j] > arr[j+1]){
[arr[j],arr[j+1]] = [arr[j+1],arr[j]];
setTimeout(function(){
var x = document.getElementById(j)
var y = document.getElementById(j+1);
[x.style.left,y.style.left] = [y.style.left,x.style.left];
[x.id,y.id] = [y.id,x.id];
},time * 1000)
time++;
}
}
}
</script>
</body>
</html>
複製程式碼