编辑: 我编辑了我的代码。现在,您可以直接在stackoverflow中运行代码。
最后一天,我从D3开始,在修复动态轴方面遇到了一些问题。我是D3新手:)
我试图用D3绘制一些2d流数据。数据通过Web套接字进入。因此,随着数据的出现,我绘制了新点。我在这方面有所成功,但我的轴没有达到预期的效果。
随着新数据的出现,我重新调整了我的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>
答案 0 :(得分:1)
在我看来,解决方案是在更新轴时再次设置function GridViewModel() {
var self = this;
self.pageIndex = ko.observable(0);
...
self._refreshRequest = ko.observable(null).extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });
self._doRefresh = function() {
$.ajax(...)
.done(result) { // update rows, etc. }
.then(
function(r) { self._refreshPromise.resolve(r); },
function(r) { self._refreshPromise.reject(r); },
function(r) { self._refreshPromise.progress(r); }
)
.always(function() { self._refreshPromise = null; }
// here I used the obvious verbose redirecting
}
...
ko.computed(function() {
var pageIndex = self.pageIndex();
if (ko.computedContext.isInitial()) return;
this.refreshRequest("Paging");
});
ko.computed(function() {
var refreshRequest = self.refreshRequest();
if (ko.computedContext.isInitial() || !refreshRequest) return;
self._doRefresh(refreshRequest);
}
}
GridViewModel.prototype.Refresh = function(type) {
this._refreshPromise = this._refreshPromise || $.Deferred();
this._refreshRequest(type);
return this._refreshPromise;
}
。
现在,在您的代码中,域(对于x和y比例)正在发生变化,但轴将使用原始translate
和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;
}