- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
当我尝试放大和缩小图表时,我遇到了 d3.js 问题。变焦非常缓慢且滞后。我正在尝试使用分析工具 (Opera/Chrome) 进行调试。我原以为我的缩放回调函数会成为限制因素,但事实证明每个鼠标滚轮滚动事件之间有很多空闲时间。
操作策略:我开始分析,然后在鼠标滚轮上快速滚动(图表上为 5 秒)。该图滞后几秒钟(从图上的 5 秒到 8.5 秒),然后定期调用我的缩放回调(从图上的 8.5 到 14 秒)。我检查了堆栈调用,我所有的缩放回调都按顺序同步执行,这让我觉得它们是在空闲时间内完成的。我认为探查器不会记录一些系统/浏览器调用并将它们限定为空闲,因此我尝试使用中断(event.preventDefault() 等...)来确保在 zoomend 上没有执行任何操作。它提高了一点性能,但仍然有很多空闲时间:
谁能帮我弄清楚为什么有这么多空闲时间?
这是我的相关代码:
不间断
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 加载会很有趣。
您可以使用剪辑路径消除删除和重写节点的需要,这样唯一的开销就是重写变换属性。
您的搜索也有问题。有一种更简单的方法可以很好地工作,那就是使用比例尺的 #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/
我正在尝试为我的网站创建一个功能,允许用户使用 mousemove 和 touchmove 事件水平滚动 div 内容(类似于 Apple AppStore any app Screenshots s
我有固定的侧边栏导航栏,它在悬停时工作,但我想通过单击折叠按钮打开第一个菜单。类似于悬停在菜单 1 上的工作方式。我已经尝试了以下方法。 jsfiddle Demo $(document).on('c
Mouse.Synchronize() 在 .Net 中有什么作用? MSDN 说它“强制鼠标重新同步” 最佳答案 只是我的假设: Stylus 中存在类似的方法类别:Stylus.Synchroni
有没有什么办法可以同时使用鼠标, pygame.mouse.set_visible(False) 已激活。当前鼠标仅在尝试使用时返回右下坐标。需要在隐藏鼠标时能够获得正确的坐标。 在他们的 docum
我有一个缺少数据的数据库。我需要估算数据(我使用的是鼠标),然后根据原始列创建新列(使用估算数据)。我需要使用这些新列进行统计分析。 具体来说,我的参与者使用李克特 7 分量表填写了几份问卷。有些人没
我正在编写一个与电脑交互的机器人。简而言之,我所做的是: -截取屏幕截图- 在此屏幕截图上识别对象(使用 cv2 matchTemplate) -使用找到的位置进行一些鼠标操作(例如:将鼠标指针移动到
我的程序是一个文本游戏,它使用 WindowsForm 上的文本框模拟控制台输出。我试图实现的一个功能是通过单击一个按钮,它将以一定的速度输出到 TextBox,这是通过这种方法实现的 atm: pu
我遇到了一个问题。如果有任何帮助,我将不胜感激。 我正在尝试从玩家位置射击到鼠标点击位置。代码没有给我任何错误,根据我的逻辑,它应该可以工作,但它没有 它创建了项目符号对象,仅此而已。 //Bulle
给定一个带蓝牙的 Windows Mobile 6.1 智能手机,我想将它注册为鼠标。 基本上我现在做的: 使用 Guid {00001124-0000-1000-8000-00805f9b34fb}
我有一个关于在 JavaFX 中实现鼠标拖动事件的正确方法的问题。 我的 playGame() 方法当前使用 onMouseClicked,但这只是一个占位符 理想情况下,我希望“飞盘”沿着鼠标拖动的
已关闭。此问题旨在寻求有关书籍、工具、软件库等的建议。不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以
我目前正在使用 Windows 的 RawInput API 来访问键盘和鼠标输入。我有点困惑的一件事是,当我将鼠标注册为 RawInputDevice 时,我无法移动我的 Win32 窗口或使用那里
我想在我的网站浏览器窗口中 move 鼠标,如下所示:www.lmsify.com。我怎样才能做到这一点?(javascript、flash、activex) 问候,丽莎M 最佳答案 他们并没有真正
我想要一个动画。我是后端开发人员,但我必须使用 jquery 创建动画。 动画、背景和元素位置随鼠标移动而变化。 类似于http://www.kennedyandoswald.com/#!/premi
如何将鼠标“锁定”到某个 OpenGL 窗口。有点像在 Minecraft 中是如何完成的。GameDev 是一个更好的询问地点吗? 最佳答案 正如 Robert 在评论中所说,OpenGL 实际上并
我正在尝试实现一个颜色选择器,它从屏幕上各处的像素中获取颜色。为此,我计划使用全局鼠标 Hook 来监听 WM_MOUSEMOVE,以便在鼠标四处移动时更新颜色,并监听鼠标点击以确认 (WM_LBUT
如何使用 Java 和 JNA(Java native 访问)与 Windows API 交互?。我试图通过在鼠标输入流上排队鼠标事件来让鼠标做某事,并且代码有效,因为 SendInput(...)
我想用 C++ 脚本 move 鼠标光标。我在 Parallels 中的 Windows 7 中使用 Visual C++ 2010 Express,并创建了一个控制台应用程序。 我知道 SetCur
我有一些关于 WH_MOUSE 的问题。根据我的阅读,通过将钩子(Hook)放入 DLL 中,它会注入(inject)进程。这是否意味着捕获鼠标也适用于我的桌面、菜单启动等?那么应用程序的标题栏呢?我
如何为多只鼠标显示另一个光标? 我有两个 TMemos,两个可以输入各自 TMemo 的键盘,2 个鼠标,我需要 2 个光标。 如果假设的话,我已经可以检测出哪只鼠标是哪只了。我怎样才能让我自己的光标
我是一名优秀的程序员,十分优秀!