在我的D3图表中,“放大”和“缩小”功能在D3图表中无法正常工作。
它将缩放整个图形,而不仅仅是缩放的线条和区域。预期行为应仅放大和缩小图表,而不是x轴和y轴。
当我使用以下代码使用d3.zoom()
时-
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform)
}));
但是上面的代码也更新了轴。因此,我尝试添加剪切路径并尝试使用它,但是它不起作用。
// A function that updates the chart when the user zoom and thus new boundaries are available
const updateChart = () => {
// recover the new scale
var newX = d3.event.transform.rescaleX(xScale);
var newY = d3.event.transform.rescaleY(yScale);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX));
yAxis.call(d3.axisLeft(newY));
// update circle position
scatter
.selectAll("circle")
.attr("cx", function(d) {
return newX(d.Sepal_Length);
})
.attr("cy", function(d) {
return newY(d.Petal_Length);
});
};
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(this.refs.chart)
.append("svg")
.attr("width", containerWidth)
.attr("height", containerHeight)
.call(zoom);
// Create the scatter variable: where both the circles and the brush take place
var scatter = svg.append("g").attr("clip-path", "url(#clip)");
此散点图应移动圆圈以放大和缩小。我的工作代码沙箱here-
谢谢。
答案 0 :(得分:1)
我尝试在反应之外重做此操作,您必须遵循d3 zoom hierarki
绘制defs
绘制剪辑,
要在剪辑中移动的绘图项目,
缩放后,请记住比例会发生变化,请使用新的比例来更新所有元素,如果不这样做,它将随缩放而下降。
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);
});
svg.on("mousemove", function() {
d3.select("#focusCircle").style('display', 'block')
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") {
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);
});
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.selectAll("dot")
.data(data)
.enter()
.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
svg.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);
});
svg
.append("rect")
.attr("width", containerWidth)
.attr("height", containerHeight)
.style("fill", "none")
.style("pointer-events", "all")
.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>