(D3.js) 如何更新动画中图形的每一步?
(D3.js) How to update every single step of the graph in animation?
首先,我想制作快速排序步骤的动画,以了解其行为。
快速排序算法很好(当然)来自参考书。
但我只能查看快速排序运动的开始和结束。
让我知道如何通过 d3.js 查看快速排序中的每一步。
(我是 JS 初学者。)
我的临时代码如下。
提前致谢。
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="utf-8" />
<title>Sort Viz 03</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="draw_functions.js"></script>
<script src="quickSort.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
font-size: 30px;
color: #333;
}
/* rect {
fill: orange;
} */
#counter{
width: 100px;
height:100px;
color: black;
font: 500 60px system-ui;
}
div {
font: 1000 30px system-ui;
}
</style>
</head>
<body>
<h1 id="title0">Quick Sort</h1>
<div id="counter0">0</div>
<div id="chart0"></div>
<script>
var myData = d3.range(10);
d3.shuffle(myData);
initDraw(myData, "chart", 0);
quickSort(myData, 0, myData.length, "chart", 0);
</script>
</body>
</html>
quickSort.js:
function quickSort(items, left, right, id, num) {
if (right - left <= 1) return;
var pivot_index = Math.floor((left + right) / 2);
var pivot = items[pivot_index];
swap(items, pivot_index, right - 1);
redraw(items, id, num);
var i = left;
for (j = left; j < right - 1; ++j) {
if (items[j] < pivot) {
swap(items, i++, j);
redraw(items, id, num);
}
}
swap(items, i, right - 1);
redraw(items, id, num);
quickSort(items, left, i, id, num);
quickSort(items, i + 1, right, id, num);
redraw(items, id, num);
// console.log("#####", items ,"#####", left, right, num);
}
function swap(items, i, j) {
// console.log("@@@@@", items, i, j, (i == j), "@@@@@")
var tmp = items[i];
items[i] = items[j];
items[j] = tmp;
console.log("@@@@@", items, i, j, (i == j), "@@@@@")
}
draw_functions.js:
const graphImageSizeX = 300;
const graphImageSizeY = 200;
const margin = {top: 20, right: 20, bottom: 20, left: 20};
const width = graphImageSizeX - margin.left - margin.right;
const height = graphImageSizeY - margin.top - margin.bottom;
var durationTime = 1000;
function initDraw(data, id, num) {
console.log("#####", data ,"#####", id, num);
var bandScale = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, width - margin.right])
.paddingInner(0.05);
d3.select("#" + id + num).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 0 + "," + 0 + ")");
d3.select("#" + id + num).select("svg").select("g")
.selectAll('rect')
.data(d3.range(data.length))
.join('rect')
.attr("id", function(d, i) { return( "rect" + num + "_" + i ); })
.attr('fill', "orange")
.attr('width', bandScale.bandwidth())
.attr('height', function(d, i) { return Math.floor(data[i] / data.length * height); })
.attr('x', function(d) { return bandScale(d); })
.attr("y", function(d, i) { return height - Math.floor(data[i] / data.length * height); })
}
async function redraw(data, id, num) {
console.log("#####", data ,"#####", id, num);
var bandScale = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, width - margin.right])
.paddingInner(0.05);
d3.select("#" + id + num).select("svg").select("g")
.selectAll('rect')
// .data(data)
// .enter()
.transition()
.duration(durationTime)
.attr('fill', "brown")
.attr('width', bandScale.bandwidth())
.attr('height', function(d, i) { return Math.floor(data[i] / data.length * height); })
.attr('x', function(d) { return bandScale(d); })
.attr("y", function(d, i) { return height - Math.floor(data[i] / data.length * height); })
}
此外:
我必须诚实地告诉你真相。这是我已经做过的。
此示例在排序中使用“记录”逐步进行。但是,如您所见,我的归并排序失败了。
所以,我想寻找另一种方法,不使用“录制和重放”,而是简单地“挂钩”排序过程——如果可能的话——。
否则,某些排序方法将更难(重新)播放其动画,这不是“交换”类比所描述的。 (记录的数据会比较复杂。)
抱歉不够诚实。但是遇到这种情况怎么办呢?
提前致谢。
问题是您在调用 redraw()
时没有等待转换完成(它需要 1 秒,即 durationTime
,而随后的 redraw
调用必须花费一些时间毫秒)。
解决方法是将所有步骤保存在一个数组中。然后在每次 redraw()
调用之间重新使用它重新绘制一个时间间隔。为此使用 setInterval
。
我创建了一个包含上述更改的工作 codepen。
- 用
steps.push
替换对 redraw()
的所有调用(参考:数组 push 方法)
- 排序完成后。调用重绘
steps.length
次,时间间隔大于 durationTime
(参考:setInterval)
- 完成重绘后,
clearInterval
清理 setInterval
(参考:clearInterval)
首先,我想制作快速排序步骤的动画,以了解其行为。
快速排序算法很好(当然)来自参考书。
但我只能查看快速排序运动的开始和结束。 让我知道如何通过 d3.js 查看快速排序中的每一步。 (我是 JS 初学者。)
我的临时代码如下。 提前致谢。
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="utf-8" />
<title>Sort Viz 03</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="draw_functions.js"></script>
<script src="quickSort.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
font-size: 30px;
color: #333;
}
/* rect {
fill: orange;
} */
#counter{
width: 100px;
height:100px;
color: black;
font: 500 60px system-ui;
}
div {
font: 1000 30px system-ui;
}
</style>
</head>
<body>
<h1 id="title0">Quick Sort</h1>
<div id="counter0">0</div>
<div id="chart0"></div>
<script>
var myData = d3.range(10);
d3.shuffle(myData);
initDraw(myData, "chart", 0);
quickSort(myData, 0, myData.length, "chart", 0);
</script>
</body>
</html>
quickSort.js:
function quickSort(items, left, right, id, num) {
if (right - left <= 1) return;
var pivot_index = Math.floor((left + right) / 2);
var pivot = items[pivot_index];
swap(items, pivot_index, right - 1);
redraw(items, id, num);
var i = left;
for (j = left; j < right - 1; ++j) {
if (items[j] < pivot) {
swap(items, i++, j);
redraw(items, id, num);
}
}
swap(items, i, right - 1);
redraw(items, id, num);
quickSort(items, left, i, id, num);
quickSort(items, i + 1, right, id, num);
redraw(items, id, num);
// console.log("#####", items ,"#####", left, right, num);
}
function swap(items, i, j) {
// console.log("@@@@@", items, i, j, (i == j), "@@@@@")
var tmp = items[i];
items[i] = items[j];
items[j] = tmp;
console.log("@@@@@", items, i, j, (i == j), "@@@@@")
}
draw_functions.js:
const graphImageSizeX = 300;
const graphImageSizeY = 200;
const margin = {top: 20, right: 20, bottom: 20, left: 20};
const width = graphImageSizeX - margin.left - margin.right;
const height = graphImageSizeY - margin.top - margin.bottom;
var durationTime = 1000;
function initDraw(data, id, num) {
console.log("#####", data ,"#####", id, num);
var bandScale = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, width - margin.right])
.paddingInner(0.05);
d3.select("#" + id + num).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 0 + "," + 0 + ")");
d3.select("#" + id + num).select("svg").select("g")
.selectAll('rect')
.data(d3.range(data.length))
.join('rect')
.attr("id", function(d, i) { return( "rect" + num + "_" + i ); })
.attr('fill', "orange")
.attr('width', bandScale.bandwidth())
.attr('height', function(d, i) { return Math.floor(data[i] / data.length * height); })
.attr('x', function(d) { return bandScale(d); })
.attr("y", function(d, i) { return height - Math.floor(data[i] / data.length * height); })
}
async function redraw(data, id, num) {
console.log("#####", data ,"#####", id, num);
var bandScale = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, width - margin.right])
.paddingInner(0.05);
d3.select("#" + id + num).select("svg").select("g")
.selectAll('rect')
// .data(data)
// .enter()
.transition()
.duration(durationTime)
.attr('fill', "brown")
.attr('width', bandScale.bandwidth())
.attr('height', function(d, i) { return Math.floor(data[i] / data.length * height); })
.attr('x', function(d) { return bandScale(d); })
.attr("y", function(d, i) { return height - Math.floor(data[i] / data.length * height); })
}
此外:
我必须诚实地告诉你真相。这是我已经做过的。
此示例在排序中使用“记录”逐步进行。但是,如您所见,我的归并排序失败了。
所以,我想寻找另一种方法,不使用“录制和重放”,而是简单地“挂钩”排序过程——如果可能的话——。
否则,某些排序方法将更难(重新)播放其动画,这不是“交换”类比所描述的。 (记录的数据会比较复杂。)
抱歉不够诚实。但是遇到这种情况怎么办呢?
提前致谢。
问题是您在调用 redraw()
时没有等待转换完成(它需要 1 秒,即 durationTime
,而随后的 redraw
调用必须花费一些时间毫秒)。
解决方法是将所有步骤保存在一个数组中。然后在每次 redraw()
调用之间重新使用它重新绘制一个时间间隔。为此使用 setInterval
。
我创建了一个包含上述更改的工作 codepen。
- 用
steps.push
替换对redraw()
的所有调用(参考:数组 push 方法) - 排序完成后。调用重绘
steps.length
次,时间间隔大于durationTime
(参考:setInterval) - 完成重绘后,
clearInterval
清理setInterval
(参考:clearInterval)