D3.js实现排序算法动画触发浏览器崩溃问题求助
D3.js冒泡排序动画浏览器崩溃问题修复
问题描述
尝试用D3.js实现冒泡排序动画,点击开始按钮后浏览器直接崩溃,修改.data(data)为.data([data[j], data[j+1]])后仍存在性能问题。
问题根源
同步嵌套for循环会瞬间触发大量D3过渡动画,JS单线程特性导致所有过渡任务被一次性推入事件队列,浏览器资源被耗尽最终崩溃。循环不会等待上一次动画完成就执行下一次交换,导致过渡任务堆积。
修复方案
改用异步流程控制,让每一步排序交换完成后再执行下一次动画,避免任务堆积。以下是修正后的完整代码:
import * as d3 from "d3"; const drawVisualization = (data: number[]) => { const margin = { top: 10, right: 30, bottom: 30, left: 40 }; const width = 800 - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; // 清除旧的可视化内容 d3.select(".visualization").selectAll("*").remove(); const svg = d3 .select(".visualization") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`); // 为每个数据绑定唯一标识,避免D3数据匹配错误 const rects = svg.selectAll("rect") .data(data, (d, i) => i) // 用索引作为key,确保数据和元素正确对应 .enter() .append("rect") .attr("x", (_d, i) => i * (width / data.length)) .attr("y", (d) => height - d) .attr("width", width / data.length - 1) .attr("height", (d) => d) .on("mouseover", function (_d, i) { svg.append("text") .attr("class", `label-${i}`) .text(data[i]) .attr("x", i * (width / data.length) + (width / data.length) / 2) .attr("y", height - data[i] - 20) .attr("font-size", "14px") .attr("fill", "blue") .attr("text-anchor", "middle"); d3.select(this).style("opacity", 0.85); }) .on("mouseleave", function (_d, i) { svg.select(`.label-${i}`).remove(); d3.select(this).style("opacity", 1); }); const startButton = document.querySelector(".run-sorting-btn") as HTMLButtonElement; // 异步冒泡排序函数 const bubbleSort = async () => { const arr = [...data]; // 复制原数组,避免修改原始数据 const rectElements = svg.selectAll("rect"); for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j + 1]) { // 交换数组元素 [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // 等待动画完成 await new Promise<void>((resolve) => { // 只更新被交换的两个矩形的位置 rectElements.filter((_d, idx) => idx === j || idx === j + 1) .data([arr[j], arr[j + 1]], (d, idx) => idx) .transition() .duration(500) .attr("x", (_d, idx) => (j + idx % 2) * (width / arr.length)) .on("end", resolve); }); } } } // 排序完成后更新所有矩形的最终位置(可选,确保对齐) rectElements.data(arr, (d, i) => i) .transition() .duration(200) .attr("x", (_d, i) => i * (width / arr.length)); }; startButton.addEventListener("click", () => { bubbleSort(); }); }; export default drawVisualization;
关键修改点
- 异步流程控制:用
async/await配合Promise,确保每一步动画完成后再执行下一次排序交换,避免任务堆积。 - 数据绑定key:绑定数据时使用索引作为唯一标识(
.data(data, (d,i)=>i)),避免D3在数据交换后匹配错误元素。 - 精准更新元素:只对交换的两个矩形执行过渡动画,而不是所有矩形,减少性能开销。
- 复制原数组:排序时操作数组副本,避免修改原始输入数据,保证可视化的可重复性。
内容的提问来源于stack exchange,提问作者IvonaK
相关产品推荐
相关产品推荐

