固定中心,带动态轴缩放

时间:2016-11-11 11:14:16

标签: javascript d3.js plot scatter-plot

编辑: 我编辑了我的代码。现在,您可以直接在stackoverflow中运行代码。

最后一天,我从D3开始,在修复动态轴方面遇到了一些问题。我是D3新手:)

我正在尝试构建以下内容。 enter image description here

我试图用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>

1 个答案:

答案 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比例)正在发生变化,但轴将使用原始translatexScale(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;
}