gpt4 book ai didi

javascript - D3.js 在每个鼠标滚轮事件之间空闲

转载 作者:行者123 更新时间:2023-11-30 00:25:39 25 4
gpt4 key购买 nike

当我尝试放大和缩小图表时,我遇到了 d3.js 问题。变焦非常缓慢且滞后。我正在尝试使用分析工具 (Opera/Chrome) 进行调试。我原以为我的缩放回调函数会成为限制因素,但事实证明每个鼠标滚轮滚动事件之间有很多空闲时间。 D3 is idling for 1s between each scrolls

操作策略:我开始分析,然后在鼠标滚轮上快速滚动(图表上为 5 秒)。该图滞后几秒钟(从图上的 5 秒到 8.5 秒),然后定期调用我的缩放回调(从图上的 8.5 到 14 秒)。我检查了堆栈调用,我所有的缩放回调都按顺序同步执行,这让我觉得它们是在空闲时间内完成的。我认为探查器不会记录一些系统/浏览器调用并将它们限定为空闲,因此我尝试使用中断(event.preventDefault() 等...)来确保在 zoomend 上没有执行任何操作。它提高了一点性能,但仍然有很多空闲时间:

It starts off nicely but slows down

谁能帮我弄清楚为什么有这么多空闲时间?

这是我的相关代码:

  • 不间断

     d3Zoom = d3.behavior.zoom()
    .x(element.self.xScale)
    .y(element.self.yScale)
    .scaleExtent([0.99, Infinity])
    .on("zoom", semanticZoom)
    .on("zoomend", updateSelection);
  • 有中断

     var delayTimer=0;
    d3Zoom = d3.behavior.zoom()
    .x(xScale)
    .y(yScale)
    .scaleExtent([0.99, Infinity])
    .on("zoom", semanticZoom)
    .on("zoomstart", function () {
    //prevent recalculating heavyCalculations too often
    window.clearTimeout(delayTimer);
    var evt = e ? e : window.event;
    return cancelDefaultAction(evt);
    })
    .on("zoomend", function () {
    // only start heavy calculations if user hasn't zoomed for 0.75sec
    delayTimer = window.setTimeout(updateSelection, 750);
    });

    function cancelDefaultAction(e) {
    var evt = e ? e : window.event;
    if (evt.preventDefault) evt.preventDefault();
    evt.returnValue = false;
    return false;
    }`

编辑:这是工作代码的示例。 semanticZoom 和更新选择在我的项目中比在这个例子中更复杂,但它们涉及自定义 AngularJS 指令、d3 画笔、扭曲的几何图形、聚合等......我裁剪了 semanticZoom 以仅执行基于四叉树(在这个例子中它可能表现得很有趣,但这只是为了展示我所做的操作)。 UpdateSelection 将可见数据更新为 Angular Directive(指令)以执行计算(各种统计信息等...)。我没有在这里填充它,但它实际上不是很密集。

var size = 100;

var dataset = d3.range(10).map(function(d, idx) {
return {
x: d3.random.normal(size / 2, size / 4)(),
y: d3.random.normal(size / 2, size / 4)(),
uuid: idx
};
});

//
// Init Scales
//

var xScale = d3.scale.linear()
.domain([0, size])
.range([0, 100]);

var yScale = d3.scale.linear()
.domain([0, size])
.range([0, 100]);

//
// Init Axes
//

var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(10)
.orient("bottom")
.tickSize(-size);

var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(10)
.orient("left")
.tickSize(-size);

//
// Init Zoom
//

var d3Zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([0.99, Infinity])
.on("zoom", semanticZoom)
.on("zoomend", updateSelection);

var quadtree = d3.geom.quadtree(dataset);


//------------------------ Callbacks --------------------------------

function semanticZoom() {

var s = 1;
var t = [0, 0];
if (d3.event) {
s = (d3.event.scale) ? d3.event.scale : 1;
t = (d3.event.translate) ? d3.event.translate : [0, 0];
}

// set zoom boundaries
// center of the zoom in svg coordinates
var center = [(size / 2 - t[0]) / s, (size / 2 - t[1]) / s];
// half size of the window in svg coordinates
var halfsize = size / (2 * s);
// top left corner in svg coordinates
var tl = [center[0] - halfsize, center[1] - halfsize];
// bottom right corner in svg coordinates
var br = [center[0] + halfsize, center[1] + halfsize];

/*

//
// Constrain zoom
//

if (!(tl[0] > -10 &&
tl[1] > -10 &&
br[0] < size + 10 &&
br[1] < size + 10)) {

// limit zoom-window corners
tl = [Math.max(0, tl[0]), Math.max(0, tl[1])];
br = [Math.min(size, br[0]), Math.min(size, br[1])];
// get restrained center
center = [(tl[0] + br[0]) / 2, (tl[1] + br[1]) / 2];
// scale center
t = [size / 2 - s * center[0], size / 2 - s * center[1]];

// update svg
svg.transition()
.duration(1)
.call( d3Zoom.translate(t).event );

}
*/

//
// Store zoom extent
//

d3Zoom.extent = [tl, br];
d3Zoom.scaleFactor = s;
d3Zoom.translation = t;

//
// Update some heavy duty stuff
// (create a quadtree, search that quadtree and update an attribute for the elements found)
//

// Prune non visible data
var displayedData = search(quadtree,
d3Zoom.extent[0][0], d3Zoom.extent[0][1],
d3Zoom.extent[1][0], d3Zoom.extent[1][1]);

redrawSubset(displayedData);

//
// Update axes
//

d3.select(".x.axis").call(xAxis);
d3.select(".y.axis").call(yAxis);

}

function redrawSubset(subset) {

//Attach new data

var elements = d3.select(".data_container")
.selectAll(".datum")
.data(subset, function(d) {
return d.uuid;
});

//enter

elements.enter()
.append("circle")
.attr("class", "datum")
.attr("r", 1)
.style("fill", "black");

//exit

elements.exit().remove();

//update

elements.attr("transform", ScaleData);

}


function updateSelection() {
// some not so heavy duty stuff

}

function ScaleData(d) {
return "translate(" + [xScale(d.x), yScale(d.y)] + ")";
}

//
// search quadtree
//

function search(qt, x0, y0, x3, y3) {
var pts = [];
qt.visit(function(node, x1, y1, x2, y2) {
var p = node.point;

if ((p) && (p.x >= x0) && (p.x <= x3) && (p.y >= y0) && (p.y <= y3)) {
pts.push(p);
}

return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;
});

return pts;
}



//------------------------- DOM Manipulation -------------------------

var svg = d3.select("body").append("svg")
.attr("width", size)
.attr("height", size)
.append("g")
.attr("class", "data_container")
.call(d3Zoom);



svg.append("rect")
.attr("class", "overlay")
.attr("width", size)
.attr("height", size)
.style("fill", "none")
.style("pointer-events", "all");



var circle = svg.selectAll("circle")
.data(dataset, function(d) {
return d.uuid;
}).enter()
.append("circle")
.attr("r", 1)
.attr("class", "datum")
.attr("transform", ScaleData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

SemanticZoom 和 UpdateSelection 都经过了单元测试,并且在大型数据集上的运行时间与上面的分析器图表(50-100 毫秒)相当。

最佳答案

如果您在圆圈计数中添加几个零并使 svg 足够大以供使用,则缩放速度会减慢到您所描述的程度。但这不足为奇,因为它有大量工作要做,以访问四叉树中的节点并写入 DOM 以管理 svg 组件。我不明白你为什么要变换单个圆圈而不是将它们分组并变换 g。如果你这样做,那么你可以让 svg 元素裁剪图像并避免所有 svg 开销,这将释放你预算的 75%。如果四叉树的唯一目的是找出哪些节点可见,那么它也将被淘汰。

我想一个关键的观察是这个配置文件与您发布的图片明显不同,从您的图片配置文件来看,它们似乎都是关于四叉树的,其余的都是空闲时间。在配置文件中看到您的 cpu 和 gpu 加载会很有趣。

enter image description here

您可以使用剪辑路径消除删除和重写节点的需要,这样唯一的开销就是重写变换属性。
您的搜索也有问题。有一种更简单的方法可以很好地工作,那就是使用比例尺的 #linear.invert(y) 方法。
这两个都在下面的示例代码中得到解决...

  var size = 500;

var margin = {top: 30, right: 40, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;

d3.select("#clipButton").on("click", (function() {
var clipped = false, clipAttr = [null, "url(#clip)"],
value = ["clip", "brush"];
return function() {
circles
.attr("clip-path", clipAttr[(clipped = !clipped, +clipped)]);
this.value = value[+clipped];
}
})());

var dataset = d3.range(1000).map(function(d, idx) {
return {
x: d3.random.normal(100 / 2, 100 / 4)(),
y: d3.random.normal(100 / 2, 100 / 4)(),
uuid: idx
};
});

//
// Init Scales
//

var xScale = d3.scale.linear()
.domain([0, 100])
.range([0, width])
.nice(10);

var yScale = d3.scale.linear()
.domain([0, 100])
.range([height, 0])
.nice(10);

//
// Init Axes
//

var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(10)
.orient("bottom")
.tickSize(-height);

var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(10)
.orient("left")
.tickSize(-width);

//
// Init Zoom
//

var d3Zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([0.99, Infinity])
.on("zoom", semanticZoom)
// .on("zoomend", updateSelection);

var Quadtree = d3.geom.quadtree()
.x(function(d){return d.x})
.y(function(d){return d.y});
quadtree = Quadtree(dataset);

//------------------------ Callbacks --------------------------------

function semanticZoom() {

var s = 1;
var t = [0, 0];
if (d3.event) {
s = (d3.event.scale) ? d3.event.scale : 1;
t = (d3.event.translate) ? d3.event.translate : [0, 0];
}

var tl = [xScale.invert(0), yScale.invert(height)];
var br = [xScale.invert(width), yScale.invert(0)];

//
// Store zoom extent
//

d3Zoom.extent = [tl, br];
d3Zoom.scaleFactor = s;
d3Zoom.translation = t;

//
// Update some heavy duty stuff
// (create a quadtree, search that quadtree and update an attribute for the elements found)
//

// Prune non visible data
var displayedData = search(quadtree, d3Zoom.extent);

markSubset(displayedData, circle);
updateSelection(circle);
//
// Update axes
//

d3.select(".x.axis").call(xAxis);
d3.select(".y.axis").call(yAxis);

};
function markSubset(data, nodes){
var marked = nodes.data(data, function(d){return d.uuid;});
marked.enter();
marked.classed("visible", true);
marked.exit().classed("visible", false);
}
function updateSelection(elements) {
// some not so heavy duty stuff
elements.attr("transform", ScaleData);

}

function ScaleData(d) {
return "translate(" + [xScale(d.x), yScale(d.y)] + ")";
}

//
// search quadtree
//

function search(qt, extent) {
var pts = [],
x0=extent[0][0], y0=extent[0][1],
x3=extent[1][0], y3=extent[1][1];
qt.visit(function(node, x1, y1, x2, y2) {
var p = node.point;

if ((p) && (p.x >= x0) && (p.x <= x3) && (p.y >= y0) && (p.y <= y3)) {
pts.push(p);
}

return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;
});

return pts;
}

//------------------------- DOM Manipulation -------------------------

var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "data_container")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3Zoom),

plotSurface = svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.style({"fill": "steelblue", opacity: 0.8})
.style("pointer-events", "all"),

gX = svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis),

gY = svg.append("g")
.attr("class", "y axis")
.call(yAxis),

clipRect = svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height),

circles = svg.append("g")/*
.attr("clip-path", "url(#clip)")*/,
circle = circles.selectAll("circle")
.data(dataset, function(d) {
return d.uuid;
});

circle.enter()
.append("circle")
.attr("r", 3)
.attr("class", "datum")
.attr("transform", ScaleData);

semanticZoom();
    svg {
outline: 1px solid red;
overflow: visible;
}
.axis path {
stroke: #000;
}

.axis line {
stroke: steelblue;
stroke-opacity: .5;
}
.axis path {
fill: none;
}
.axis text {
font-size: 8px;
}
.datum {
fill: #ccc;
}
.datum.visible {
fill: black;
}
#clipButton {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<input id="clipButton" type="button" value="clip">

关于javascript - D3.js 在每个鼠标滚轮事件之间空闲,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31546733/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com