基于d3的冒泡排序可视化
来源
https://github.com/xiaozfang/arithmetic-visualization/blob/master/web/view/bubbleSort.html
总结
想要实现动画效果,需要配合使用定时器和动画。
因为冒泡排序是双层循环排序,想要实现每次比较都可以动画化,则定时器和动画须在内层循环。因为循环的速度很快,但是想要看到比较效果,就不能太快,所以需要延迟循环。这里使用定时器“延迟循环”。
如果外层继续使用循环,过快的话会导致内层循环中的定时器执行混乱(内层循环的定时器所用的时间是固定的,时间到就会执行内部函数),所以使用interval定时器代替外层循环,不影响内层的循环且可以达到定时循环(延迟外层循环)的效果。
源码
<!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" />
<title>Document</title>
</head>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const data = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48],
svgWidth = 500,
svgHeight = 300,
rectWidth = 25,
rectGap = 5, // 矩形间的间距
textBottom = 10, // 文字距下方的距离
// 向body中添加svg节点
svg = d3
.select('body')
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight);
init();
bubbleSort();
// 初始化图形结构
function init() {
// 在本例中,使用比例尺,可以由d3推算传入的值,在svg中应该渲染的高度
const scale = d3
// 线性比例尺
.scaleLinear()
// 定义域(使用scale时,传入的值在domain范围内,则d3推算输出range范围内的数据)
.domain([0, d3.max(data)])
// 值域(最大渲染到svg的高度)
.range([d3.min(data), svgHeight]);
svg
.selectAll('rect')
.data(data)
.enter()
.append('g')
.append('rect')
.attr('width', rectWidth)
// d是数据中的每个元素
.attr('height', (d) => scale(d))
.attr('id', (d, i) => `rect${i}`)
// i表示数据索引
.attr('x', (d, i) => i * (rectWidth + rectGap))
// 怎么让矩形从底部向上开始? 让纵轴方向向下偏移到最大纵轴的位置,但是减去自己的高度
.attr('y', (d) => svgHeight - scale(d))
// 矩形填充色
.style('fill', 'skyblue');
}
// 排序主方法
function bubbleSort() {
let i = 0,
j = 0;
// 外层循环使用定时器代替
const timer = d3.interval(() => {
if (i < data.length - 1) {
if (j < data.length - 1 - i) {
compare(j, j + 1);
j++;
} else {
complete(j);
i++;
j = 0;
}
} else {
timer.stop();
}
}, 1000);
}
function compare(key1, key2) {
let min = key1;
// 初始化颜色
d3.timeout(() => {
svg
.select(`#rect${key1}`)
.transition()
.duration(100)
.style('fill', 'green');
svg
.select(`#rect${key2}`)
.transition()
.duration(100)
.style('fill', 'green');
}, 10);
// 比较值的大小,交换矩形位置
d3.timeout(() => {
if (data[key1] > data[key2]) {
// 交换值
[data[key1], data[key2]] = [data[key2], data[key1]];
move_left(key2);
move_right(key1);
min = key2;
}
}, 300);
d3.timeout(() => {
// 小值恢复颜色
svg.select(`#rect${min}`).style('fill', 'skyblue');
}, 600);
}
function move_right(key) {
svg
.select(`#rect${key}`)
.transition()
.duration(600)
.attr('x', (key + 1) * (rectWidth + rectGap))
.attr('id', `rect${key + 1}`);
}
function move_left(key) {
svg
.select(`#rect${key}`)
.transition()
.duration(600)
.attr('x', (key - 1) * (rectWidth + rectGap))
.style('fill', 'skyblue')
.attr('id', `rect${key - 1}`);
}
// 循环一轮,设置最大值的颜色
function complete(key) {
svg.select(`#rect${key}`).style('fill', 'orange');
}
</script>
</body>
</html>

浙公网安备 33010602011771号