排序算法动画演示-----(含完整代码)

最近在搞一个好玩的东西,算法、前端学习两不误,哈哈,不废话,让大家看看效果
排序算法动画演示-----(含完整代码)_第1张图片
排序算法动画演示-----(含完整代码)_第2张图片
虽然有点不好看,css还需要优化一下,但是功能都齐全了,下面来看看代码
index部分:


<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>排序算法动画演示</title>
<link rel="stylesheet" href="css/style.css" />
<script type="text/javascript" src="js/SortAnimate.js"></script>
<script type="text/javascript" src="js/cocktail.js"></script>
<script type="text/javascript" src="js/bubble.js"></script>
<script type="text/javascript" src="js/comb.js"></script>
<script type="text/javascript" src="js/insert.js"></script>
<script type="text/javascript" src="js/oddEven.js"></script>
<script type="text/javascript" src="js/quick.js"></script>
<script type="text/javascript" src="js/selection.js"></script>
<script type="text/javascript" src="js/shell.js"></script>
</head>
<body>
<div style="margin:50px;">
    <h1>排序算法动画演示</h1>
    <div>
        <p> 动画速度:
            <input value="170" max="200" min="1" id="speed" type="range" onchange="srotanimte && srotanimte.changeSpeed(200-this.value)" >
        </p>
        <p> 数组长度:
            <input value="50" width="50" id="arrlen">
            <input type="button" value="随即数组" onclick="randomArrayText(document.getElementById('arrlen').value)" class="btn" />
        </p>
        <p> 待排序数组:
            <input id="arrayText" value="[43,27,39,17,32,21,14,42,22,15,18,12,46,1,16,10,4,29,3,23,24,49,0,5,36,13,34,6,19,47,9,33,31,20,28,44,38,37,41,40,8,35,30,7,11,45,2,25,48,26]" style="width:80%;">
        </p>
        <p></p>
    </div>
    <div>
        <input type="button" value="冒泡排序" onclick="playSortAnimate(sortMethodMap.bubble)" class="btn" />
        <input type="button" value="快速排序" onclick="playSortAnimate(sortMethodMap.quick)" class="btn" />
        <input type="button" value="插入排序" onclick="playSortAnimate(sortMethodMap.insert)" class="btn" />
        <input type="button" value="希尔排序" onclick="playSortAnimate(sortMethodMap.shell)" class="btn" />
        <input type="button" value="选择排序" onclick="playSortAnimate(sortMethodMap.selection)" class="btn" />
        <input type="button" value="鸡尾酒排序" onclick="playSortAnimate(sortMethodMap.cocktail)" class="btn" />
        <input type="button" value="奇偶排序" onclick="playSortAnimate(sortMethodMap.oddEven)" class="btn" />
        <input type="button" value="梳排序" onclick="playSortAnimate(sortMethodMap.comb)" class="btn" />
    </div>
    <div>
        <div class="sort-animate-box" id="sortAnimateBox" style="height:100px;"></div>
        <div id="sortText"></div>
    </div>
</div>
<script type="text/javascript">
	
		var srotanimte = null;
		
		var sortMethodMap = {
			bubble:{
				sort:"bubbleSort",
				animateSort:"bubbleSortAnimate",
				text:"冒泡排序"
			},
			quick:{
				sort:"quickSort",
				animateSort:"quickSortAnimate",
				text:"快速排序"
			},
			insert:{
				sort:"insertSort",
				animateSort:"insertSortAnimate",
				text:"插入排序"
			},
			shell:{
				sort:"shellSort",
				animateSort:"shellSortAnimate",
				text:"希尔排序"
			},
			selection:{
				sort:"selectionSort",
				animateSort:"selectionSortAnimate",
				text:"选择排序"
			},
			cocktail:{
				sort:"cocktailSort",
				animateSort:"cocktailSortAnimate",
				text:"鸡尾酒排序"
			},
			oddEven:{
				sort:"oddEvenSort",
				animateSort:"oddEvenSortAnimate",
				text:"奇偶酒排序"
			},
			comb:{
				sort:"combSort",
				animateSort:"combSortAnimate",
				text:"梳排序"
			}
		};		
		
		function $(id){
			return document.getElementById(id);//.value;
		}
		
		function getSpeed(){
			return 200-document.getElementById('speed').value;
		}
		
		function randomArrayText( len ){
			len = len || 50;
			var arr = [];
			for(var i=0; i<len;i++){
				arr.push( i );
			}
			arr.sort(function(){
				return Math.random() - 0.5;
			});
			document.getElementById( "arrayText" ).value = "["+ arr.toString() +"]";
		};
		
		function getArray(){
			var text = document.getElementById( "arrayText" ).value;
			try{
				return JSON.parse( text );
			}catch(e){
				alert("数组有错误")
			}
		}
		
		
		function playSortAnimate( key ){
			var arr = getArray();
			srotanimte = new SortAnimate( arr, $('sortAnimateBox') );
			$('sortText').innerHTML = key.text;
			window[key.animateSort](arr, srotanimte);
			srotanimte.play(getSpeed());
		}
		
		//正确性测试
		function test(){
			var arrays = [
				[2,0,1,4,3,5,7,8,6,9],
				[2,4,6,0,1,3,8,5,7,9],
				[0,6,2,4,3,1,8,5,7,9],
				[5,2,0,1,6,7,8,3,4,9],
				[0,6,1,2,3,7,8,5,4,9],
				[0,2,3,8,5,1,7,6,4,9],
				[0,4,1,2,3,6,7,8,9,5],
				[0,1],
				[1,0],
				[0],
				[1],
				[]
			];
			
			console.log("排序正确性测试");
			for(var i=0; i<arrays.length; i++){
				srotanimte = new SortAnimate( arrays[i], $('sortAnimateBox') );
				for(var key in sortMethodMap){
					console.log(sortMethodMap[key].text, window[sortMethodMap[key].sort](arrays[i]));
					console.log(sortMethodMap[key].text, window[sortMethodMap[key].animateSort](arrays[i], srotanimte));
				}
			}
			console.log("结束");
		}
		//console.log("选择:",cocktailSort([9,1,3,2,4,6,8,5,0,7], srotanimte));
		test();
	</script> 

</div>
</body>
</html>

css部分:

.column{
	width:7px;
	height:100%;
	background:#cccccc;
	display:inline-block;
	margin:0 1px;
}
.column:hover{
	background:#f30;
}
.active{
	background:#888888;
}
.focus{
	background:#ffcccc;
}
.highlight{
	background:#ff6666;
}

bubble.js部分:

/**
	冒泡排序
*/
function bubbleSort(array, srotanimte){
	var len = array.length,i,j, d;
	for(i=len;i--;){
		for(j=0; j<i; j++){
			var z = j+1;
			if(array[j] > array[z]){
				d = array[j];
				array[j] = array[z];
				array[z] = d;
			}
		}
	}
	return array;
};

function bubbleSortAnimate(array, srotanimte){
	var len = array.length,i,j, d;
	srotanimte.activeFragment(0,len);
	for(i=len;i--;){
		for(j=0; j<i; j++){
			var z = j+1;
			srotanimte.activeOne(j);
			if(array[j] > array[z]){
				d = array[j];
				array[j] = array[z];
				array[z] = d;
				srotanimte.exchange(j, z);
			}
			srotanimte.blurOne(j);
		}
		srotanimte.blurFragment(j,len);
	}
	return array;
};

cocktail.js部分:

/**
	鸡尾酒
*/
function cocktailSort(array){
	
	var len = array.length;
	var item = array[0];
	var i = 0;
	var j = len;
	var k=0;
	var index = 0;
	var ischange = false;
	
	while(j - i > 1){
		//寻找最大
		ischange = false;
		item = array[i];
		index = i;
		for(k=i; k<j; k++){
			if(item > array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
			}else{
				item = array[k];

			}
			index = k;
		}
		j--;
		
		//寻找最小
		item = array[j];
		index = j;
		for(k=j; k>i-1; k--){
			if(item < array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
			}else{
				item = array[k];

			}
			index = k;
		}
		
		i++;
		//没有任何交换跳出
		if( ischange == false ){
			break;
		}
	}
	
	return array;
}

function cocktailSortAnimate(array, srotanimte){
	
	var len = array.length;
	var item = array[0];
	var i = 0;
	var j = len;
	var k=0;
	var index = 0;
	var ischange = false;
	
	while(j - i > 1){
		//寻找最大
		ischange = false;
		item = array[i];
		index = i;
		srotanimte.activeFragment(i, j);
		for(k=i; k<j; k++){
			srotanimte.activeOne(k);
			if(item > array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
				srotanimte.exchange(index, k);
			}else{
				item = array[k];

			}
			index = k;
			srotanimte.blurOne(k);
		}
		srotanimte.blurFragment(i, j);
		j--;
		
		//寻找最小
		item = array[j];
		index = j;
		srotanimte.activeFragment(i-1, j);
		for(k=j; k>i-1; k--){
			srotanimte.activeOne(k);
			if(item < array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
				srotanimte.exchange(index, k);
			}else{
				item = array[k];

			}
			index = k;
			srotanimte.blurOne(k);
		}
		srotanimte.blurFragment(i-1, j);
		i++;
		//没有任何交换跳出
		if( ischange == false ){
			break;
		}
	}
	
	return array;
}
		

comb.js部分:

/**
	鸡尾酒
*/
function cocktailSort(array){
	
	var len = array.length;
	var item = array[0];
	var i = 0;
	var j = len;
	var k=0;
	var index = 0;
	var ischange = false;
	
	while(j - i > 1){
		//寻找最大
		ischange = false;
		item = array[i];
		index = i;
		for(k=i; k<j; k++){
			if(item > array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
			}else{
				item = array[k];

			}
			index = k;
		}
		j--;
		
		//寻找最小
		item = array[j];
		index = j;
		for(k=j; k>i-1; k--){
			if(item < array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
			}else{
				item = array[k];

			}
			index = k;
		}
		
		i++;
		//没有任何交换跳出
		if( ischange == false ){
			break;
		}
	}
	
	return array;
}

function cocktailSortAnimate(array, srotanimte){
	
	var len = array.length;
	var item = array[0];
	var i = 0;
	var j = len;
	var k=0;
	var index = 0;
	var ischange = false;
	
	while(j - i > 1){
		//寻找最大
		ischange = false;
		item = array[i];
		index = i;
		srotanimte.activeFragment(i, j);
		for(k=i; k<j; k++){
			srotanimte.activeOne(k);
			if(item > array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
				srotanimte.exchange(index, k);
			}else{
				item = array[k];

			}
			index = k;
			srotanimte.blurOne(k);
		}
		srotanimte.blurFragment(i, j);
		j--;
		
		//寻找最小
		item = array[j];
		index = j;
		srotanimte.activeFragment(i-1, j);
		for(k=j; k>i-1; k--){
			srotanimte.activeOne(k);
			if(item < array[k]){
				array[index] = array[k];
				array[k] = item;
				ischange = true;
				srotanimte.exchange(index, k);
			}else{
				item = array[k];

			}
			index = k;
			srotanimte.blurOne(k);
		}
		srotanimte.blurFragment(i-1, j);
		i++;
		//没有任何交换跳出
		if( ischange == false ){
			break;
		}
	}
	
	return array;
}
		

insert.js部分

/**
	插入排序
*/
function insertSort(array){

	var i = 1, j, step, key,
		len = array.length;
	
	for(; i < len; i++){
		step = j = i;
		key = array[j];
		while(--j > -1){
			if(array[j] > key){
				array[j+1] = array[j];
			}else{
				break;
			}
		}
		array[j+1] = key;
	}
	
	return array;
}

function insertSortAnimate(array, srotanimte){

	var i = 1, j, step, key,
		len = array.length;
	
	for(; i < len; i++){
		//srotanimte.activeOne(i);
		step = j = i;
		key = array[j];
		srotanimte.activeFragment(0, i);
		while(--j > -1){
			srotanimte.activeOne(j);
			if(array[j] > key){
				srotanimte.exchange(j+1, j);
				array[j+1] = array[j];
				srotanimte.blurOne(j);
			}else{
				srotanimte.blurOne(j);
				break;
			}
			srotanimte.blurOne(j);
		}
		//srotanimte.blurOne(i);
		array[j+1] = key;
		srotanimte.blurFragment(0, i);
	}
	
	return array;
}

oddEven.js部分:

/**
	奇偶 金锐提供
*/
function oddEvenSort(list){
	var len = list.length;
	var sorted = true;
	
	while(sorted){
		sorted = false;
		
		for(var i = 1; i < len - 1; i += 2)
		{
			if(list[i] > list[i + 1])
			{
				var t = list[i];
				list[i] = list[i + 1];
				list[i + 1] = t;
				sorted = true;
			}
		}	
		
		for(var i = 0; i < len; i += 2){
			
			if(list[i] > list[i + 1]){
				var tmp = list[i];
				list[i] = list[i + 1];
				list[i + 1] = tmp;
				sorted = true;
			}
		}
	}
	
	return list;
}


function oddEvenSortAnimate(list, srotanimte){
	var len = list.length;
	var sorted = true;
	
	while(sorted){
		sorted = false;
		
		for(var i = 1; i < len - 1; i += 2)
		{
			srotanimte.activeOne(i);
			srotanimte.activeOne(i+1);
			if(list[i] > list[i + 1])
			{
				var t = list[i];
				list[i] = list[i + 1];
				list[i + 1] = t;
				sorted = true;
				srotanimte.exchange(i, i+1);
			}
			srotanimte.blurOne(i);
			srotanimte.blurOne(i+1);
		}	
		
		for(var i = 0; i < len; i += 2){
		
			srotanimte.activeOne(i);
			srotanimte.activeOne(i+1);
			
			if(list[i] > list[i + 1]){
				var tmp = list[i];
				list[i] = list[i + 1];
				list[i + 1] = tmp;
				sorted = true;
				srotanimte.exchange(i, i+1);
			}
			srotanimte.blurOne(i);
			srotanimte.blurOne(i+1);
		}
	}
	
	return list;
}

quick.js部分:

/**
	快速排序
*/
function quickSort(array){
	if(array.length == 0){
		return array;
	}
	var i = 0;
	var j = array.length - 1;
	var Sort = function(i, j){
	
		// 结束条件
		if(i == j ){ 
			return; 
		};
		
		var key = array[i];
		var stepi = i; // 记录开始位置
		var stepj = j; // 记录结束位置
	
		while(j > i){
			// j <<-------------- 向前查找
			if(array[j] >= key){
				j--;
			}else{
				
				array[i] = array[j]
				//i++ ------------>>向后查找
				while(j > ++i){
					if(array[i] > key){
						array[j] = array[i];
						break;
					}
					
				}
			}

		}
		
		// 如果第一个取出的 key 是最小的数
		if(stepi == i){
			Sort(++i, stepj);
			return ;
		}
		
		// 最后一个空位留给 key
		array[i] = key;
		
		
		// 递归
		Sort(stepi, i);
		Sort(j, stepj);
	}
	
	Sort(i, j);
	
	return array;
}


function quickSortAnimate(array, srotanimte){
	if(array.length == 0){
		return array;
	}
	var i = 0;
	var j = array.length - 1;
	var Sort = function(i, j){
		
		
		// 结束条件
		if(i == j ){ 
			return; 
		};
		
		srotanimte.activeFragment(i, j)
		
		var key = array[i];
		var stepi = i; // 记录开始位置
		var stepj = j; // 记录结束位置
		//srotanimte.activeOne(i);
		while(j > i){
			srotanimte.activeOne(j);

			// j <<-------------- 向前查找
			if(array[j] >= key){
				srotanimte.blurOne(j);
				j--;
			}else{
				srotanimte.exchange(i, j);
				array[i] = array[j]
				//i++ ------------>>向后查找
				while(j > ++i){
					srotanimte.activeOne(i);
					if(array[i] > key){
						srotanimte.exchange(i, j);
						srotanimte.blurOne(i);
						array[j] = array[i];
						break;
					}
					srotanimte.blurOne(i);
				}
			}
			srotanimte.blurOne(j);
		}
		
		// 如果第一个取出的 key 是最小的数
		srotanimte.blurFragment()
		
		if(stepi == i){
			Sort(++i, stepj);
			return ;
		}
		
		// 最后一个空位留给 key
		array[i] = key;
		
		
		// 递归
		Sort(stepi, i);
		Sort(j, stepj);
	}
	
	Sort(i, j);
	
	return array;
}
		

selection.js部分:

/**
	选择排序
*/
function selectionSort(array){
	var len = array.length;
	var index = 0;
	var k;
	var item;
	var c;
	for(var i=0; i<len; i++){
		
		item = array[i];
		index = i;
		//寻找最小的数位置
		for(j=i+1; j<len;j++){
			if(array[j] < item){
				index = j;
				item = array[j];
			}
		}
		if(index != i){
			c = array[i];
			array[i] = array[index];
			array[index] = c;
		}
	}
	return array;
}

function selectionSortAnimate(array, srotanimte){
	var len = array.length;
	var index = 0;
	var k;
	var item;
	var c;
	for(var i=0; i<len; i++){
		
		item = array[i];
		index = i;
		
		srotanimte.activeOne(i);
		//寻找最小的数位置
		for(j=i+1; j<len;j++){
			srotanimte.activeOne(j);
			if(array[j] < item){
				srotanimte.blurOne(index);
				srotanimte.activeOne(j);
				index = j;
				item = array[j];
			}
			srotanimte.blurOne(j);
		}
		srotanimte.blurOne(i);
		if(index != i){
			srotanimte.blurOne(index);
			srotanimte.exchange(i, index);
			c = array[i];
			array[i] = array[index];
			array[index] = c;
		}
	}
	
	return array;
	
}
		

shell.js部分:

/*
	希尔
*/
function shellSort(array){

	var stepArr = [1031612713, 217378076, 45806244, 9651787, 2034035, 428481, 90358, 19001, 4025, 1750, 836, 701, 301, 132, 57, 23, 10, 4, 1]; // reverse() 在维基上看到这个最优的步长 较小数组
	var i = 0;
	var stepArrLength = stepArr.length;
	var len = array.length;
	var len2 =  parseInt(len/2);
	
	for(;i < stepArrLength; i++){
		if(stepArr[i] > len2){
			continue;
		}
		stepSort(stepArr[i]);
	}
	// 排序一个步长
	function stepSort(step){
		
		//console.log(step) 使用的步长统计
		
		var i = 0, j = 0, f, tem, key;
		
		
		for(;i < step; i++){// 依次循环列
			for(j=1; step * j + i < len; j++){//依次循环每列的每行
				tem = f = step * j + i;
				key = array[f];
				while((tem-=step) >= 0){// 依次向上查找
					if(array[tem] > key){
						array[tem+step] = array[tem];
					}else{
						break;
					}
				}
				array[tem + step ] = key;
			}
		}
		
	}
	
	return array;
	
}

function shellSortAnimate(array, srotanimte){

	var stepArr = [1031612713, 217378076, 45806244, 9651787, 2034035, 428481, 90358, 19001, 4025, 1750, 836, 701, 301, 132, 57, 23, 10, 4, 1]; // reverse() 在维基上看到这个最优的步长 较小数组
	var i = 0;
	var stepArrLength = stepArr.length;
	var len = array.length;
	var len2 =  parseInt(len/2);
	
	for(;i < stepArrLength; i++){
		if(stepArr[i] > len2){
			continue;
		}
		stepSort(stepArr[i]);
	}

	// 排序一个步长
	function stepSort(step){
		
		//console.log(step) 使用的步长统计
		
		var i = 0, j = 0, f, tem, key;
		
		
		for(;i < step; i++){// 依次循环列
			for(j=1; step * j + i < len; j++){//依次循环每列的每行
				tem = f = step * j + i;
				key = array[f];
				srotanimte.activeOne( f );
				while((tem-=step) >= 0){// 依次向上查找
					//console.log(tem);
					srotanimte.activeOne( tem );
					if(array[tem] > key){
						srotanimte.exchange(tem+step, tem);
						array[tem+step] = array[tem];
					}else{
						srotanimte.blurOne( tem );
						break;
					}
					srotanimte.blurOne( tem );
				}
				srotanimte.blurOne( f );
				array[tem + step ] = key;
			}
		}
		
	}
	
	return array;
	
}
				

SortAnimate.js部分:


/**
	记录数组排序过程的步骤
	
	options
	
*/



function SortAnimate( array, wrapper ){
	this.array = array.slice();
	this.max = Math.max.apply(Math, array);
	this.ui = {columns:[], wrapper:wrapper || document.body};
	this.index = -1;
	this.animte = [];
	this.html = "";
	this.init();
};
SortAnimate.prototype = {
	init:function(){
		var html = "";
		for(var i=0; i<this.array.length; i++){
			html += this.getColumn(this.array[i], this.array[i]);
		}
		this.html = html;
		
		this.ui.wrapper.innerHTML = this.html;
		this.ui.columns = this.ui.wrapper.getElementsByClassName("column");

	},
	getHeight:function( number ){
		return parseInt(number/this.max*100);
	},
	getColumn:function( number){
		return '
+this.getHeight(number)+'%" title="'+ number +'" >
'
; }, //交换两个数的位置 uiExchange:function( index1, index2 ){ var tmp = this.array[index1]; this.array[index1] = this.array[index2]; this.array[index2] = tmp; this.uiSetHeight(index1); this.uiSetHeight(index2); }, uiSetHeight:function( index ){ var number = this.array[index]; this.ui.columns[index].style.height = this.getHeight( number )+"%"; this.ui.columns[index].title = number; }, uiHighlightOne:function(index1, index2){ this.ui.columns[index1].className += " highlight"; this.ui.columns[index2].className += " highlight"; }, uiUnHighlightOne:function(index1, index2){ this.ui.columns[index1].className = this.ui.columns[index1].className.replace(" highlight", ""); this.ui.columns[index2].className = this.ui.columns[index2].className.replace(" highlight", ""); }, //激活第 index 个元素 uiActiveOne:function( index ){ this.ui.columns[index].className += " focus"; }, //取消激活 index 个元素 uiBlurOne:function( index ){ this.ui.columns[index].className = this.ui.columns[index].className.replace(" focus", ""); }, //激活数组中一个片段 uiActiveFragment:function(startIndex, endIndex){ //console.log("uiActiveFragment", startIndex, endIndex); this.startIndex = startIndex; this.endIndex = endIndex; for(var i= startIndex; i<endIndex; i++){ this.ui.columns[i].className += " active"; } }, //取消激活数组中一个片段 uiBlurFragment:function(startIndex, endIndex){ startIndex = startIndex || this.startIndex; endIndex = endIndex || this.endIndex; for(var i= startIndex; i<endIndex; i++){ this.ui.columns[i].className = this.ui.columns[i].className.replace(" active",""); } }, activeOne:function( index ){ this.animte.push({ type:"uiActiveOne", data:[index] }); }, blurOne:function( index ){ this.animte.push({ type:"uiBlurOne", data:[index] }); }, exchange:function(index1, index2){ this.highlight(index1, index2); this.unHighlight(index1, index2); this.animte.push({ type:"uiExchange", data:[index1, index2] }); }, activeFragment:function(startIndex, endIndex){ startIndex = startIndex < 0 ? 0 : startIndex; this.animte.push({ type:"uiActiveFragment", data:[startIndex, endIndex] }); }, blurFragment:function(startIndex, endIndex){ startIndex = startIndex < 0 ? 0 : startIndex; this.animte.push({ type:"uiBlurFragment", data:[startIndex, endIndex] }); }, highlight:function( index1, index2 ){ this.animte.push({ type:"uiHighlightOne", data:[index1, index2] }); }, unHighlight:function(index1, index2){ this.animte.push({ type:"uiUnHighlightOne", data:[index1, index2] }); }, changeSpeed:function( speed ){ this.speed = speed; this.play(); }, /** play 播放排序动画 id 容器id speed 速度单位毫秒 */ play:function( speed ){ clearInterval( SortAnimate.interid ); this.speed = speed || this.speed; var animate = this.animte; var len = animate.length; var self = this; SortAnimate.interid = setInterval(function(){ self.index++ if(self.index<len){ self[animate[self.index].type].apply( self, animate[self.index].data); }else{ clearInterval( SortAnimate.interid ); } }, this.speed); } };

排序算法动画演示-----(含完整代码)_第3张图片
建立如上图的文件,就OK啦,谢谢观看!

你可能感兴趣的:(前端)