具有动态轴缩放的固定中心
Fixed center with dynamic axis scaling
编辑:
我已经编辑了我的代码。现在可以直接运行Whosebug里面的代码了
昨天,我开始使用 D3,在修复动态轴时遇到了一些问题。我是 D3 的新手:)
我正在尝试构建以下内容。
我正在尝试使用 D3 绘制一些二维流数据。数据通过网络套接字传入。因此,随着数据的到来,我绘制了新的点。我在这方面取得了一些成功,但我的轴没有按预期运行。
随着新数据的到来,我重新调整了我的 x 轴和 y 轴。例如,如果 (20,100) 出现在下图中,那么我将缩放我的 y 轴以适应 100。但是如果你仔细观察 x 轴和 y 轴相交的点不在 0 上。最初它在 (0,0) 但重新调整后,它就在下方。如何在动态重新缩放轴的同时固定中心 a (0,0)?
你能帮帮我吗?这是我的代码。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
div.bar {
display: inline-block;
width: 20px;
height: 75px; /* We'll override this later */
background-color: teal;
}
/* Format X and Y Axis */
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<svg width="960" height="500"></svg>
<br/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var dataset = []; // Initialize empty array
var numDataPoints = 15; // Number of dummy data points
for(var i=0; i<numDataPoints; i++) {
dataset.push([getRandomInt(-80,80), getRandomInt(-60,60)]); // Add new number to array
}
var width = 1020;
var height = 840;
var xScale = d3.scale.linear().domain([d3.min(dataset, function(d) { return d[0]; } ), d3.max(dataset, function(d) { return d[0]; })])
.range([20, width - 20 * 2])
.nice();
var yScale = d3.scale.linear().domain([d3.min(dataset, function(d) { return d[1]; } ), d3.max(dataset, function(d) { return d[1]; })])
.range([height - 20, 20])
.nice();
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left");
var svg = d3.select("svg")
.attr("height",height)
.attr("width", width)
.append("g")
.attr("transform","translate(20,20)")
.attr("width", width- 40)
.attr("height", height- 40);
// x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
//.attr("transform", "translate(0," + (height - 20) + ")")
//.style("position", "fixed")
.call(xAxis);
//y-axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
//.attr("transform", "translate(" + 20 + ",0)")
//.style("position", "fixed")
.call(yAxis);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle") // Add circle svg
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) { // Circle's Y
return yScale(d[1]);
})
.attr("r", 2)
.call(updatePlotWithSocketData);
function updatePlotWithSocketData() {
console.log(dataset.length);
for(var i=0; i<numDataPoints; i++) {
dataset.push([getRandomInt(-200,200), getRandomInt(-100,100)]); // Add new number to array
}
// Update scale domains
xScale.domain([d3.min(dataset, function(d) { return d[0]; } ), d3.max(dataset, function(d) { return d[0]; })]);
yScale.domain([d3.min(dataset, function(d) { return d[1]; } ), d3.max(dataset, function(d) { return d[1]; })]);
xScale.range([20, width - 20 * 2]).nice();
yScale.range([height - 20, 20]).nice();
// Update old points to the new scale
svg.selectAll("circle")
.transition()
.duration(1000)
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
});
// Update circles
svg.selectAll("circle")
.data(dataset) // Update with new data
.enter()
.append("circle")
.transition() // Transition from old to new
.duration(1000) // Length of animation
.each("start", function() { // Start animation
d3.select(this) // 'this' means the current element
.attr("fill", "red") // Change color
.attr("r", 5); // Change size
})
.delay(function(d, i) {
return i / dataset.length * 500; // Dynamic delay (i.e. each item delays a little longer)
})
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
})
.each("end", function() { // End animation
d3.select(this) // 'this' means the current element
.transition()
//.duration(500)
.attr("fill", "black") // Change color
.attr("r", 2); // Change radius
});
/* force.on("tick", function() {
nodes[0].x = width / 2;
nodes[1].y = height / 2;
}); */
/* var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left"); */
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
// Update Y Axis
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
setTimeout(updatePlotWithSocketData, 3000);
}
// Socker code
/*
var socket;
var registered = false;
function startDataReceptionFromSocket() {
console.log("opening socket");
//on http server use document.domain instead od "localhost"
//Start the websocket client
socket = new WebSocket("ws://localhost:8887/");
//When the connection is opened, login.
socket.onopen = function() {
console.log("Opened socket.");
//register the user
};
socket.onmessage = function(a) {
//process the message here
console.log("received message socker => : " + a.data);
var message = JSON.parse(a.data);
console.log("message =>" + message.message);
var numbers = message.message.split(" ")
dataset.push([numbers[0],numbers[1]]);
updatePlotWithSocketData();
}
socket.onclose = function() { console.log("Closed socket."); };
socket.onerror = function() { console.log("Error during transfer."); };
}
// On document load, open the socket connection
(function() {
startDataReceptionFromSocket();
})();*/
</script>
在我看来,解决方案是在更新轴时再次设置 translate
。
现在,在您的代码中,域(对于 x 和 y 尺度)正在改变,但轴是使用原始 xScale(0)
和 yScale(0)
位置转换的。这个演示重现了这个问题:
var margin = 10;
var width = 250, height = 250;
var svg = d3.select("#mydiv")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scale.linear()
.range([margin, width - margin])
.domain([-10, 10]);
var yScale = d3.scale.linear()
.range([margin, height - margin])
.domain([-10, 10]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
d3.select("#btn").on("click", randomize);
function randomize(){
xScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
yScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
svg.selectAll(".x.axis")
.transition()
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.call(yAxis);
}
.axis path, .axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="btn">Randomize</button>
<div id="mydiv"></div>
现在是解决方案。下一个演示具有相同的代码,但更新位置如下:
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
查看演示:
var margin = 10;
var width = 250, height = 250;
var svg = d3.select("#mydiv")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scale.linear()
.range([margin, width - margin])
.domain([-10, 10]);
var yScale = d3.scale.linear()
.range([margin, height - margin])
.domain([-10, 10]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
d3.select("#btn").on("click", randomize);
function randomize(){
xScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
yScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
}
.axis path, .axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="btn">Randomize</button>
<div id="mydiv"></div>
编辑: 我已经编辑了我的代码。现在可以直接运行Whosebug里面的代码了
昨天,我开始使用 D3,在修复动态轴时遇到了一些问题。我是 D3 的新手:)
我正在尝试构建以下内容。
我正在尝试使用 D3 绘制一些二维流数据。数据通过网络套接字传入。因此,随着数据的到来,我绘制了新的点。我在这方面取得了一些成功,但我的轴没有按预期运行。
随着新数据的到来,我重新调整了我的 x 轴和 y 轴。例如,如果 (20,100) 出现在下图中,那么我将缩放我的 y 轴以适应 100。但是如果你仔细观察 x 轴和 y 轴相交的点不在 0 上。最初它在 (0,0) 但重新调整后,它就在下方。如何在动态重新缩放轴的同时固定中心 a (0,0)?
你能帮帮我吗?这是我的代码。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
div.bar {
display: inline-block;
width: 20px;
height: 75px; /* We'll override this later */
background-color: teal;
}
/* Format X and Y Axis */
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<svg width="960" height="500"></svg>
<br/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var dataset = []; // Initialize empty array
var numDataPoints = 15; // Number of dummy data points
for(var i=0; i<numDataPoints; i++) {
dataset.push([getRandomInt(-80,80), getRandomInt(-60,60)]); // Add new number to array
}
var width = 1020;
var height = 840;
var xScale = d3.scale.linear().domain([d3.min(dataset, function(d) { return d[0]; } ), d3.max(dataset, function(d) { return d[0]; })])
.range([20, width - 20 * 2])
.nice();
var yScale = d3.scale.linear().domain([d3.min(dataset, function(d) { return d[1]; } ), d3.max(dataset, function(d) { return d[1]; })])
.range([height - 20, 20])
.nice();
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left");
var svg = d3.select("svg")
.attr("height",height)
.attr("width", width)
.append("g")
.attr("transform","translate(20,20)")
.attr("width", width- 40)
.attr("height", height- 40);
// x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
//.attr("transform", "translate(0," + (height - 20) + ")")
//.style("position", "fixed")
.call(xAxis);
//y-axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
//.attr("transform", "translate(" + 20 + ",0)")
//.style("position", "fixed")
.call(yAxis);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle") // Add circle svg
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) { // Circle's Y
return yScale(d[1]);
})
.attr("r", 2)
.call(updatePlotWithSocketData);
function updatePlotWithSocketData() {
console.log(dataset.length);
for(var i=0; i<numDataPoints; i++) {
dataset.push([getRandomInt(-200,200), getRandomInt(-100,100)]); // Add new number to array
}
// Update scale domains
xScale.domain([d3.min(dataset, function(d) { return d[0]; } ), d3.max(dataset, function(d) { return d[0]; })]);
yScale.domain([d3.min(dataset, function(d) { return d[1]; } ), d3.max(dataset, function(d) { return d[1]; })]);
xScale.range([20, width - 20 * 2]).nice();
yScale.range([height - 20, 20]).nice();
// Update old points to the new scale
svg.selectAll("circle")
.transition()
.duration(1000)
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
});
// Update circles
svg.selectAll("circle")
.data(dataset) // Update with new data
.enter()
.append("circle")
.transition() // Transition from old to new
.duration(1000) // Length of animation
.each("start", function() { // Start animation
d3.select(this) // 'this' means the current element
.attr("fill", "red") // Change color
.attr("r", 5); // Change size
})
.delay(function(d, i) {
return i / dataset.length * 500; // Dynamic delay (i.e. each item delays a little longer)
})
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
})
.each("end", function() { // End animation
d3.select(this) // 'this' means the current element
.transition()
//.duration(500)
.attr("fill", "black") // Change color
.attr("r", 2); // Change radius
});
/* force.on("tick", function() {
nodes[0].x = width / 2;
nodes[1].y = height / 2;
}); */
/* var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left"); */
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
// Update Y Axis
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
setTimeout(updatePlotWithSocketData, 3000);
}
// Socker code
/*
var socket;
var registered = false;
function startDataReceptionFromSocket() {
console.log("opening socket");
//on http server use document.domain instead od "localhost"
//Start the websocket client
socket = new WebSocket("ws://localhost:8887/");
//When the connection is opened, login.
socket.onopen = function() {
console.log("Opened socket.");
//register the user
};
socket.onmessage = function(a) {
//process the message here
console.log("received message socker => : " + a.data);
var message = JSON.parse(a.data);
console.log("message =>" + message.message);
var numbers = message.message.split(" ")
dataset.push([numbers[0],numbers[1]]);
updatePlotWithSocketData();
}
socket.onclose = function() { console.log("Closed socket."); };
socket.onerror = function() { console.log("Error during transfer."); };
}
// On document load, open the socket connection
(function() {
startDataReceptionFromSocket();
})();*/
</script>
在我看来,解决方案是在更新轴时再次设置 translate
。
现在,在您的代码中,域(对于 x 和 y 尺度)正在改变,但轴是使用原始 xScale(0)
和 yScale(0)
位置转换的。这个演示重现了这个问题:
var margin = 10;
var width = 250, height = 250;
var svg = d3.select("#mydiv")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scale.linear()
.range([margin, width - margin])
.domain([-10, 10]);
var yScale = d3.scale.linear()
.range([margin, height - margin])
.domain([-10, 10]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
d3.select("#btn").on("click", randomize);
function randomize(){
xScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
yScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
svg.selectAll(".x.axis")
.transition()
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.call(yAxis);
}
.axis path, .axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="btn">Randomize</button>
<div id="mydiv"></div>
现在是解决方案。下一个演示具有相同的代码,但更新位置如下:
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
查看演示:
var margin = 10;
var width = 250, height = 250;
var svg = d3.select("#mydiv")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scale.linear()
.range([margin, width - margin])
.domain([-10, 10]);
var yScale = d3.scale.linear()
.range([margin, height - margin])
.domain([-10, 10]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
d3.select("#btn").on("click", randomize);
function randomize(){
xScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
yScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
}
.axis path, .axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="btn">Randomize</button>
<div id="mydiv"></div>