如何解决HTML/JS/D3.js:鼠标悬停在画布的某些部分滞后?
我最近开始尝试使用 D3.js 创建基于 HTML/JS 的交互式图形。在我最近的实验中,我想创建一个图形,根据鼠标当前悬停的位置沿 x 轴移动函数。
我目前的草案在原则上很好,但结果图的行为很奇怪。如果鼠标悬停在主画布(图形内的区域)上,更新速度会减慢到几乎无功能的程度。但是,当您将鼠标水平移动到 x 轴下方(跨 x 刻度标签)时,代码会按预期顺利运行。
我不知道可能导致这种滞后差异的原因。您对可能导致这种情况的原因有什么建议吗?
要重现我在此处报告的内容,请运行下面的代码片段,然后将鼠标移到结果图上。该代码几乎不会在 x 和 y 轴跨越的矩形内响应,但在 x 轴下方移动鼠标时会平滑响应。
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/mathjs/lib/browser/math.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hsv.v0.1.min.js"></script>
<script src="https://d3js.org/d3-contour.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<style>
</style>
</head>
<!-- Create a div where the graph will take place -->
<div id="my_datavisualization">
<svg id="click" xmlns="http://www.w3.org/2000/svg">
<defs>
<g id="pointer" transform="scale(0.5)">
<circle cx="0" cy="0" r="20" id="dragcircle" />
</g>
</defs>
</svg>
</div>
<body style='overflow:hidden'>
<script>
// Get the viewport height and width
const vw = Math.max(document.documentElement.clientWidth || 0,window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0,window.innerHeight || 0)
// Fit to viewport
var height = math.min([vw,vh])*0.7;
var width = math.min([vw,vh]);
// Create the canvas. We will use only part of it for the main plot
var svg = d3.select("#click") // This selects the div
.attr("width",width) // This defines the canvas' width
.attr("height",height) // This defines the canvas' height
// Prepare the contour grid
var conc = linspace(0,60,101);
var obs = 24;
var obs_sd = 2;
//print(conc_short)
// Get the initial likelihood
var likeli = likelihood(
y = conc,x = obs,std = obs_sd)
var x_limits = [0,60];
var y_limits = [0,d3.max(likeli)*1.2];
// Get scaling functions for the x scale and the y_scale
const xScale = d3.scaleLinear()
.domain([x_limits[0],x_limits[1]])
.range([width*0.05,width*0.95])
const xScale_inverse = d3.scaleLinear()
.domain([width*0.05,width*0.95])
.range([x_limits[0],x_limits[1]])
const yScale = d3.scaleLinear()
.domain([y_limits[0],y_limits[1]])
.range([height*0.92,height*0.08])
// Draw the x axis
svg
.append("g")
.attr("transform","translate(0,"+(height*0.92).toString()+")")
.call(d3.axisBottom(xScale))
.style("font-size",(16*width/600).toString()+"px")
svg.append("text")
.attr("transform","translate("+(width*0.625).toString()+","+(height*0.055).toString()+")")
.style("text-anchor","middle")
.text("value of random variable x")
.style("font-family","arial")
.style("font-size",(16*width/600).toString()+"px")
// Draw the y axis
svg
.append("g")
.attr("transform","translate("+(width*0.05).toString()+",0)")
.attr("id","mainxaxis")
.call(d3.axisLeft(yScale))
.style("font-size",(16*width/600).toString()+"px");
//.call(d3.axisLeft(yScale).tickFormat(""));
svg.append("text")
.attr("transform","translate("+(width*0.95).toString()+","+(height*0.5).toString()+") rotate(270)")
.style("text-anchor","middle")
.text("value of random variable y")
.style("font-family",(16*width/600).toString()+"px")
// Get the data for the path
var data = [];
for (i = 0; i < conc.length; i++) {
data.push({
x: conc[i],y: likeli[i]})
}
// Add a realization of the marker
svg.append("use")
.attr("href","#pointer")
.attr("x",xScale(24))
.attr("y",yScale(0.2))
.attr("fill","blue") // "#c3e7f9"
.attr("stroke-width",5)
.attr("id","point1");
// Add the x coordinate tracker
svg
.append('line')
.attr('x1',xScale(obs))
.attr('y1',yScale(y_limits[0]))
.attr('x2',xScale(obs))
.attr('y2',yScale(y_limits[1]))
.attr("stroke-width",2*height/600)
.attr("stroke","#666666")
.attr("id","vline");
var valueline = d3.svg.line()
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); });
svg.append("path")
.attr("class","line")
.attr("d",valueline(data))
.attr("fill","none")
.attr("stroke-width",3*height/600)
.attr("stroke","#4794c1")
.attr("id","thatline")
// Shift the marker around on mouSEOver; restrict it to the contour
var movex
var movey
svg
.on("mouSEOver",function () {
// Get the current mouSEOver coordinates
movex = d3.event.x;
movey = d3.event.y;
if (movex < 0.05*width) {
movex = 0.05*width;
} else if (movex > width*0.95) {
movex = width*0.95;
}
if (movey > 0.95*height) {
movey = 0.95*height;
} else if (movey < height*0.05) {
movey = height*0.05;
}
// Shift the point to this position
d3.select("#point1")
.attr("x",movex)
.attr("y",movey);
d3.select("#vline")
.attr("x1",movex)
.attr("x2",movex)
likeli = likelihood(
y = conc,x = xScale_inverse(movex),std = obs_sd)
for (i = 0; i < data.length; i++) {
data[i].y = likeli[i]
}
// Select the section we want to apply our changes to
// Make the changes
d3.select("#thatline") // change the line
.attr("d",valueline(data));
});
// ============================================================
// Helper function: Printing function for troubleshooting
// ============================================================
function print(value) {
var precision = 14;
document.write(math.format(value,precision) + '<br>');
}
// ============================================================
// Helper function: Linspace
// ============================================================
function linspace(start,end,resolution) {
var spacing = [];
// Go through a for-loop
var i;
for (i = 0; i < resolution; i++) {
spacing.push(start + (end-start)*i/(resolution-1))
}
return spacing; // The function returns the linspace between p1 and p2
}
// ============================================================
// Helper function: likelihood
// ============================================================
function likelihood(y,x,std) {
mean = x;
std = std;
// Create a dummy variable
var result = [];
normalize = math.dotDivide(
1,math.sqrt(
math.dotMultiply(
math.dotMultiply(
2,math.PI),std)));
var i;
for (i = 0; i < y.length; i ++) {
// Evaluate the first element of the Gaussian mixture
expon = math.exp(
-math.dotDivide(
math.pow(
y[i]-mean,2),math.dotMultiply(
2,math.pow(
std,2))))
temp = math.dotMultiply(
normalize,expon)
result.push(temp)
}
return result
}
</script>
</body>
</html>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。