<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>動態坐標軸</title> <link rel="stylesheet" type="text/css" href="../../css/styles.css"/> <script type="text/javascript" src="../../lib/d3.js"></script> </head> <body> <div class="control-group"> <button onclick="rescale()">重新生成坐標軸</button> </div> <script type="text/javascript"> let height = 500, width = 500, margin = 30, xAxis, yAxis, xAxisLength, yAxisLength; xAxisLength = width - 2*margin; yAxisLength = height - 2*margin; //創建一個svg標簽畫布 //后面還要創建一個g標簽,用於畫坐標系 let svg = d3.select("body").append("svg") .attr("class", "axis") .attr("width", width) .attr("height", height) .style("background-color","#cedb9c"); /** * 創建坐標軸
* 一個坐標軸包含:尺度、刻度、相對位置
* 尺度:一個映射關系,請業務數據映射到畫布的線上,所有要有兩組數據,一組為業務數據,另外一組則是畫布的一個線段
* 刻度:類似米尺上的刻度,比如一厘米一大格,中間又有一些代表毫米的小格
* 相對位置:米尺是個實物,而這里的刻度是畫在畫布上的,就是刻度在畫布上的位置 */ function renderXAxis() { //domain是值就是d3.svg.axis坐標系的x與y的值 //svg.axis就是業務數據坐標系,其數據是有業務含義的 //range的值是svg畫布像素的長度,意思就是要將業務數據domain畫在(映射到)svg畫布的指定長度范圍內 let scale = d3.scale.linear() .domain([0,100]) .range([0,xAxisLength]);
xAxis = d3.svg.axis() .scale(scale) .tickSubdivide(1) .orient("bottom");
//<sgv class="axis"><g class="x-axis" >...<g class="y-axis" ... svg.append("g") .attr("class","x-axis") .attr("transform",function () { return "translate("+margin+","+(xAxisLength+margin)+")"; }) .call(xAxis); } function renderYAxis() { let scale = d3.scale.linear() .domain([100,0]) .range([0,yAxisLength]) yAxis = d3.svg.axis() .scale(scale) .tickSubdivide(1) .orient("left");
//坐標軸是以svg標簽下的g標簽為畫板的 svg.append("g") .attr("class","y-axis") .attr("transform",function () { return "translate("+margin+","+margin+")"; }) .call(yAxis); } /** * X坐標軸對應的網格線對應的兩個點 * 一個是坐標系原點(0,0) * 一個是Y軸的終點(0,-yAxisLength) */ function renderXGridLines() { //通常坐標重構前都會刪除已有的圖形,盡管有時它不並存在 d3.selectAll("g.x-axis g.tick") .select("line.grid-line") .remove(); //然后重新選取新的圖形 let lines = d3.selectAll("g.x-axis g.tick") .append("line") .classed("grid-line",true); //圖形中涉及的坐標系是SVG坐標系,上負下正,右正 lines.attr("x1",0) .attr("y1",0) .attr("x2",0) .attr("y2",-yAxisLength); } /** * Y坐標軸對應的網格線對應的兩個點 * 一個是坐標系原點(0,0) * 一個是X軸的終點(xAxisLength,0) */ function renderYGridLines() { //通常坐標重構前都會刪除已有的圖形,盡管有時它不並存在 d3.selectAll("g.y-axis g.tick") .select("line.grid-line") .remove(); //然后重新選取新的圖形 let lines = d3.selectAll("g.y-axis g.tick") .append("line") .classed("grid-line",true); lines.attr("x1",0) .attr("y1",0) .attr("x2",xAxisLength) .attr("y2",0); } /** * 通過改變坐標軸的尺度來重構坐標系 */ function rescale() { let max = Math.round(Math.random()*100); let duration = 5000; xAxis.scale().domain([0,max]); //構建坐標軸會在g標簽中添加class為tick的g標簽,刪除這個就相當於刪除了坐標軸 //call方法中會自動刪除,所以這里不需要這一步了 // d3.selectAll("g.x-axis g.tick") // .remove(); d3.select("g.x-axis") .transition() .duration(duration) .call(xAxis); yAxis.scale().domain([max,0]); d3.select("g.y-axis") .transition() .duration(duration) .call(yAxis); renderXGridLines(); renderYGridLines(); } renderXAxis(); renderYAxis(); renderXGridLines(); renderYGridLines(); </script> </body> </html>
附css樣式 css/styles.css
body { font-family: "helvetica"; } button { margin: 0 7px 0 0; background-color: #f5f5f5; border: 1px solid #dedede; border-top: 1px solid #eee; border-left: 1px solid #eee; font-size: 12px; line-height: 130%; text-decoration: none; font-weight: bold; color: #565656; cursor: pointer; } .box { width: 200px; height: 200px; margin: 40px; float: left; text-align: center; border: #969696 solid thin; padding: 5px; } .red { background-color: #e9967a; color: #f0f8ff; } .blue { background-color: #add8e6; color: #f0f8ff; } .cell { min-width: 40px; min-height: 20px; margin: 5px; float: left; text-align: center; border: #969696 solid thin; padding: 5px; } .fixed-cell { min-width: 40px; min-height: 20px; margin: 5px; position: fixed; text-align: center; border: #969696 solid thin; padding: 5px; } .h-bar { min-height: 15px; min-width: 10px; background-color: steelblue; margin-bottom: 2px; font-size: 11px; color: #f0f8ff; text-align: right; padding-right: 2px; } .v-bar { min-height: 1px; min-width: 30px; background-color: #4682b4; margin-right: 2px; font-size: 10px; color: #f0f8ff; text-align: center; width: 10px; display: inline-block; } .baseline { height: 1px; background-color: black; } .clear { clear: both; } .selected { background-color: #f08080; } .control-group { padding-top: 10px; margin: 10px; } .table { width: 70%; } .table td, th { padding: 5px; } .table-header { background-color: #00AFEF; font-weight: bold; } .table-row-odd { background-color: #f0f8ff; } .table-row-odd { background-color: #d3d3d3; } .code { display: inline-block; font-style: italic; background-color: #d3d3d3; border: #969696 solid thin; padding: 10px; margin-top: 10px; margin-bottom: 10px; } .countdown{ width: 150px; height: 150px; font-size: 5em; font-weight: bold; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis text { font: 10px sans-serif; } .axis .grid-line{ stroke: black; shape-rendering: crispEdges; stroke-opacity: .2; } .line{ fill: none; stroke: steelblue; stroke-width: 2; } .dot { fill: #fff; stroke: steelblue; } .area { stroke: none; fill: steelblue; fill-opacity: .2; } .pie text{ fill: white; font-weight: bold; } .circle { stroke: none; fill: red; fill-opacity: .7; } .cross { stroke: none; fill: blue; fill-opacity: .7; } .diamond { stroke: none; fill: green; fill-opacity: .7; } .square{ stroke: none; fill: yellow; fill-opacity: .7; } .triangle-down{ stroke: none; fill: blueviolet; fill-opacity: .7; } .triangle-up{ stroke: none; fill: darkred; fill-opacity: .7; } .bubble{ fill-opacity: .3; } .bar{ stroke: none; fill: steelblue; }