在我的D3图表中,添加此功能后,我实现了“放大”和“缩小”功能。我在使用工具提示时遇到了一些问题,它没有按预期显示该点/圆的悬停数据。 (在添加放大后感觉newX和newY没有正确更新)
没有放大缩小的工具提示的工作代码here
添加了放大缩小代码here
.on("mousemove", function() {
var mouse = d3.mouse(this);
var mouseDate = xScale.invert(mouse[0]);
var i = bisectDate(data, mouseDate); // returns the index to the current data item
var d0 = data[i - 1];
var d1 = data[i];
let d;
// work out which date value is closest to the mouse
if (typeof d1 !== "undefined") {
d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
} else {
d = d0;
}
div
.html(
`<span>${parseDate(d.startTime)}</span>
<span>Magnitude: ${d.magnitude} </span>`
)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
var x = xScale(d.startTime);
var y = yScale(d.magnitude);
focus
.select("#focusCircle")
.attr("cx", x)
.attr("cy", y);
focus
.select("#focusLineX")
.attr("x1", x)
.attr("y1", yScale(yDomain[0]))
.attr("x2", x)
.attr("y2", yScale(yDomain[1]));
focus
.select("#focusLineY")
.attr("x1", xScale(xDomain[0]))
.attr("y1", y)
.attr("x2", xScale(xDomain[1]))
.attr("y2", y);
});
我无法区分出问题的原因。请帮我。
答案 0 :(得分:1)
我很好奇您的代码,但我从未见过这样的tip函数构建,错了,但是可以正常工作
查看没有缩放版本的
如果您更改
g.selectAll("dot")
.data(data)
.enter()
.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {....
到
svg
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {....
它将产生相同的问题
您制作了一个浪费的元素,看到没有缩放的元素
g.selectAll("dot")
.data(data)
.enter()
.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {....
您不应该这样做,为什么selectAll ("dot")
和append ('rect')
,请阅读更多的d3.js
文档,以及为什么要这样做,所以您要为所有矩形元素构建每个要覆盖的数据并保持隐藏?为了什么?您甚至都没有使用附加到其上的数据,为什么不将其设为一个rect元素
g.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {....
.on("mousemove", function(d) {....
现在,当您的鼠标指向该svg时,您的指针将指向哪个元素?到svg层的底部还是第一个?指针未指向该元素,并且您放置了诸如mousemove之类的事件,那么您的鼠标指针将永远不会触摸该元素,它将无法正常工作,我相信您知道原因,这就是为什么此代码将鼠标事件和zoom函数放置到svg上,但是它会产生一些您现在面临的问题,只需对这两个代码进行一点调整即可完成
var data = [
{
startTime: "1567765320049",
magnitude: 0,
startupMagnitude: 0,
startupRunningStatus: "IN_SYNC"
},
{
startTime: "1567851720049",
magnitude: 0,
startupMagnitude: 0,
startupRunningStatus: "IN_SYNC"
},
{
startTime: "1568024520049",
magnitude: 10,
startupMagnitude: 10,
startupRunningStatus: "IN_SYNC"
},
{
startTime: "1568283720049",
magnitude: 10,
startupMagnitude: 0,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1568629320049",
magnitude: 0,
startupMagnitude: 10,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1569061320049",
magnitude: 0,
startupMagnitude: 0,
startupRunningStatus: "IN_SYNC"
},
{
startTime: "1569579720049",
magnitude: -20,
startupMagnitude: 0,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1570184520049",
magnitude: -20,
startupMagnitude: -10,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1570875720049",
magnitude: 0,
startupMagnitude: 0,
startupRunningStatus: "IN_SYNC"
},
{
startTime: "1571653320049",
magnitude: 10,
startupMagnitude: -0,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1572517320049",
magnitude: 0,
startupMagnitude: -10,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1573467720049",
magnitude: 0,
startupMagnitude: -10,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1574504520049",
magnitude: 10,
startupMagnitude: -10,
startupRunningStatus: "OUT_OF_SYNC"
},
{
startTime: "1575627720049",
magnitude: 10,
startupMagnitude: -10,
startupRunningStatus: "OUT_OF_SYNC"
}
];
var drawLineGraph = function(containerHeight, containerWidth, data, yLabel, warnLine) {
// A function that updates the chart when the user zoom and thus new boundaries are available
var newX = ''
const updateChart = () => {
d3.select("#focusCircle").style('display', 'none')
// recover the new scale
newX = d3.event.transform.rescaleX(xScale);
var newY = yScale;
// update axes with these new boundaries
d3.select("#axisX").call(d3.axisBottom(newX));
//d3.select('#axisY').call(d3.axisLeft(newY));
// update circle position
scatter
.selectAll("circle")
.attr("cx", function(d) {
if (d) {
return newX(d.startTime);
}
})
.attr("cy", function(d) {
if (d) {
return newY(d.magnitude);
}
});
var line2 = d3
.line()
.x(function(d) {
return newX(d.startTime);
})
.y(function(d) {
return newY(d.startupMagnitude);
});
var area = d3
.area()
.x(function(d) {
return newX(d.startTime);
})
.y0(function(d) {
return yScale(d.startupMagnitude);
})
.y1(height);
var line = d3
.line()
.x(function(d) {
return newX(d.startTime);
})
.y(function(d) {
return newY(d.magnitude);
});
g.select('#firstLayer').on("mousemove", function() {
d3.select("#focusCircle").style('display', 'block')
focus.style('display', 'none')
var mouse = d3.mouse(this);
var mouseDate = newX.invert(mouse[0]);
var i = bisectDate(data, mouseDate); // returns the index to the current data item
var d0 = data[i - 1];
var d1 = data[i];
let d;
// work out which date value is closest to the mouse
if ( typeof d1 !== "undefined" && typeof d0 !== "undefined"){
focus.style('display', 'block')
div.style('display', 'block')
if (typeof d1 !== "undefined" ) {
d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
} else {
d = d0;
}
div
.html(
`<span>${parseDate(d.startTime)}</span>
<span>Magnitude: ${d.magnitude} </span>`
)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
var x = newX(d.startTime);
var y = yScale(d.magnitude);
focus
.select("#focusCircle")
.attr("cx", x)
.attr("cy", y);
focus
.select("#focusLineX")
.attr("x1", x)
.attr("y1", yScale(yDomain[0]))
.attr("x2", x)
.attr("y2", yScale(yDomain[1]));
focus
.select("#focusLineY")
.attr("x1", xScale(xDomain[0]))
.attr("y1", y)
.attr("x2", xScale(xDomain[1]))
.attr("y2", y);
} else {
d3.select("#focusCircle").style('display', 'none')
focus.style('display', 'none')
div.style('display', 'none')
}
});
scatter.select("#line2").attr("d", line2);
scatter.select(".line").attr("d", line);
scatter.select("#area").attr("d", area);
};
var zoom = d3
.zoom()
.scaleExtent([0.5, 20]) // This control how much you can unzoom (x0.5) and zoom (x20)
.extent([[0, 0], [containerWidth, containerHeight]])
.on("zoom", updateChart);
var svg = d3
.select('#chart')
.append("svg")
.attr("width", containerWidth)
.attr("height", containerHeight);
var clip = svg
.append("defs")
.append("SVG:clipPath")
.attr("id", "clip")
.append("SVG:rect")
.attr("width", containerWidth)
.attr("height", containerHeight)
.attr("x", 50)
.attr("y", 0);
// Create the scatter variable: where both the circles and the brush take place
var scatter = svg.append("g").attr("clip-path", "url(#clip)");
var margin = { top: 50, left: 50, right: 50, bottom: 80 };
var height = containerHeight - margin.top - margin.bottom;
var width = containerWidth - margin.left - margin.right;
var xDomain = d3.extent(data, function(d) {
return d.startTime;
});
var yDomain = d3.extent(data, function(d) {
return d.magnitude;
});
var xScale = d3
.scaleTime()
.range([0, width])
.domain(xDomain);
var yScale = d3
.scaleLinear()
.range([height, 0])
.domain(yDomain);
var xAxis = d3.axisBottom(xScale);
var yAxis = d3.axisLeft(yScale);
var line = d3
.line()
.x(function(d) {
return xScale(d.startTime);
})
.y(function(d) {
return yScale(d.magnitude);
});
var line2 = d3
.line()
.x(function(d) {
return xScale(d.startTime);
})
.y(function(d) {
return yScale(d.startupMagnitude);
});
var area = d3
.area()
.x(function(d) {
return xScale(d.startTime);
})
// .x0(function(d) {
// return xScale(d.startTime);
// })
// .x1(function(d) {
// return xScale(d.magnitude);
// })
.y0(function(d) {
return yScale(d.startupMagnitude);
})
.y1(height);
// .y0(height)
// .y1(function(d) { return yScale(d.magnitude); });
// var area = d3
// .area()
// .x0(function(d) {
// return xScale(d.startTime);
// })
// .x1(function(d) {
// return xScale(d.startTime);
// })
// .y0(function(d) {
// return yScale(d.magnitude);
// })
// .y1(function(d) {
// return yScale(0);
// });
// var area = d3
// .area()
// .x(function(d) {
// return xScale(d.startTime);
// })
// .y0(function(d) {
// return yScale(d.magnitude);
// })
// .y1(yScale(0));
// Define the div for the tooltip
var div = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = scatter
.append("g")
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
var g2 = svg
.append("g")
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
g.append("path")
.datum(data)
.attr("class", "area")
.attr("id", "area")
.attr("d", area);
g.append("g")
.attr("class", "x axis")
.attr("id", "axisX")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
g2.append("g")
.attr("class", "y axis")
.attr("id", "axisY")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.attr("text-anchor", "end")
.text(yLabel);
g.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
g.append("path")
.datum(data)
.attr("class", "line2")
.attr("id", "line2")
.attr("d", line2);
g.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.startTime);
})
.attr("cy", function(d) {
return yScale(d.magnitude);
})
.attr("r", function(d) {
if (d.startupRunningStatus === "OUT_OF_SYNC") {
return 5;
}
})
.attr("class", "circle");
//legend code
svg
.append("circle")
.attr("cx", 40)
.attr("cy", 380)
.attr("r", 6)
.style("fill", "#1391d8");
svg
.append("circle")
.attr("cx", 40)
.attr("cy", 400)
.attr("r", 6)
.style("fill", "red");
svg
.append("text")
.attr("x", 60)
.attr("y", 380)
.text("Startup Config")
.style("font-size", "15px")
.attr("alignment-baseline", "middle");
svg
.append("text")
.attr("x", 60)
.attr("y", 400)
.text("Running Config")
.style("font-size", "15px")
.attr("alignment-baseline", "middle");
// focus tracking
var focus = g.append("g").style("display", "none");
focus
.append("line")
.attr("id", "focusLineX")
.attr("class", "focusLine");
focus
.append("line")
.attr("id", "focusLineY")
.attr("class", "focusLine");
focus
.append("circle")
.attr("id", "focusCircle")
.attr("r", 5)
.attr("class", "circle focusCircle");
//grid line
const make_x_axis = () => {
return d3.axisBottom(xScale);
};
scatter
.append("g")
.attr("class", "grid")
.attr("transform", "translate(50," + (height + 50) + ")")
.call(
make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
);
var bisectDate = d3.bisector(function(d) {
return d.startTime;
}).left;
var parseDate = d3.timeFormat("%Y-%m-%d %H:%M:%S");
g
.append("rect")
.attr("class", "overlay")
.attr("id","firstLayer")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {
console.log('ok')
focus.style("display", null);
div
.transition()
.duration(200)
.style("opacity", 0.9);
})
.on("mouseout", function() {
focus.style("display", "none");
div
.transition()
.duration(300)
.style("opacity", 0);
})
.on("mousemove", function() {
var mouse = d3.mouse(this);
var mouseDate = xScale.invert(mouse[0]);
var i = bisectDate(data, mouseDate); // returns the index to the current data item
var d0 = data[i - 1];
var d1 = data[i];
let d;
// work out which date value is closest to the mouse
if (typeof d1 !== "undefined") {
d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
} else {
d = d0;
}
div
.html(
`<span>${parseDate(d.startTime)}</span>
<span>Magnitude: ${d.magnitude} </span>`
)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
var x = xScale(d.startTime);
var y = yScale(d.magnitude);
focus
.select("#focusCircle")
.attr("cx", x)
.attr("cy", y);
focus
.select("#focusLineX")
.attr("x1", x)
.attr("y1", yScale(yDomain[0]))
.attr("x2", x)
.attr("y2", yScale(yDomain[1]));
focus
.select("#focusLineY")
.attr("x1", xScale(xDomain[0]))
.attr("y1", y)
.attr("x2", xScale(xDomain[1]))
.attr("y2", y);
})
.call(zoom);
// warn line
// if (
// warnLine &&
// yDomain[0] < warnLine.lineValue &&
// yDomain[1] > warnLine.lineValue
// ) {
// g.append("line")
// .attr("x1", xScale(xDomain[0]))
// .attr("y1", yScale(warnLine.lineValue))
// .attr("x2", xScale(xDomain[1]))
// .attr("y2", yScale(warnLine.lineValue))
// .attr("class", "zeroline");
// g.append("text")
// .attr("x", xScale(xDomain[1]))
// .attr("y", yScale(warnLine.lineValue))
// .attr("dy", "1em")
// .attr("text-anchor", "end")
// .text(warnLine.label)
// .attr("class", "zerolinetext");
// }
}
drawLineGraph(410, 700, data, "Magnitude", {
lineValue: 0,
label: "Startup Config!"
});
.axis path,
.axis line {
fill: none;
stroke: #e0e0e0;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: red;
stroke-width: 2px;
}
.line2 {
fill: none;
stroke: #2e59cf;
stroke-width: 0.3px;
}
.circle {
/* fill: white;
stroke: steelblue;
stroke-width: 2px; */
fill: steelblue;
stroke: steelblue;
/* stroke-width: 2px; */
}
.area {
fill: #2e59cf;
stroke: none;
opacity: 0.1;
}
.zeroline {
fill: none;
stroke: #1391d8;
stroke-width: 1px;
stroke-dasharray: 8 8;
}
.zerolinetext {
fill: #1391d8;
}
.overlay {
fill: none;
stroke: none;
pointer-events: all;
}
.focusLine {
fill: none;
stroke: steelblue;
stroke-width: 0.5px;
}
.focusCircle {
fill: red;
}
div.tooltip {
position: absolute;
text-align: center;
width: 150px;
height: 38px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.grid .tick {
stroke: lightgrey;
stroke-width: 0.7px;
stroke-dasharray: 8 8;
opacity: 0.3;
}
.grid path {
stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div className="App">
<h1>Chart</h1>
</div>
<div id="chart"></div>