基于d3.js重构canvas界面

1,142 阅读10分钟

起始:

最近由于公司项目要求,揭示板界面重构,要适配70寸、55寸等的安卓5.1、4.4版本等的1G内存电视正常显示。经过内存占用筛选,确定用d3.js去替换以前的canvas+fabric.min.js,减少内存占用;因为之前的界面定时刷新会导致内存占用越来越大,从而崩掉;替换后内存占用由原来的670M左右减低并稳定在340M左右,降低了一倍左右,并且稳定性更好,渲染界面更快。

内存占用(MB)测试:

(1)直接打开谷歌:433~296,297.1

(2)html+css+js 谷歌打开此网页:452->359 再次刷新此网页:415->370 多次刷新:383->349

(3)svg 谷歌打开此网页:369->237 再次刷新此网页:308->250 多次刷新:307->256

(4)d3.js 谷歌打开此网页:356->241 再次刷新此网页:314->241 多次刷新:312->250

(5)揭示板打开此网页(新-d3):506->336, 306.7 再次刷新此网页:430->316; 404->309 多次刷新:412->318; 407->313.6

(5-1)揭示板打开此网页(新-d3,加上定时刷新):506->386 再次刷新此网页:392->362 多次刷新:423->368,等一段时间444

(5-2)揭示板打开此网页(新-d3,完整的-加上定时刷新):464->335 再次刷新此网页:393->337 多次刷新:452->343,等一段时间335.1

(5-3)揭示板页面打开了40分钟(调试): 不去股道操作:XX分钟前294,XX分钟后158.6 股道操作,改变数据:340->296.9,等一段时间293.2, 273.7

(6)揭示板打开此网页(旧-canvas):594->506 再次刷新此网页:549->657 多次刷新:694->661

svg - polygon:

  • 左上角: 距离外部svg左边的距离 距离上边的距离
  • 左下角: 距离左边的距离 距离上边的距离
  • 右下角 距离左边的距离 距离上边的距离
  • 右上角 距离左边的距离 距离上边的距离

下面是操手之前在网上找到文档,花了一些时间了解和实践。

斯科特·默里(Scott Murray)D3教程

引用

<script type="text/javascript" src="d3/d3.v3.js"></script>

添加元素

d3.select("body").append("p").text("New paragraph!");

链接方式

d3.select("body")
    .append("p")
    .text("New paragraph!");

去无链

var body = d3.select("body");
var p = body.append("p");
p.text("New paragraph!");

绑定数据

1.数据

var dataset = [ 5, 10, 15, 20, 25 ];

2.DOM元素的选择

d3.select("body").selectAll("p")
    .data(dataset)
    .enter()
    .append("p")
    .text("New paragraph!");

.selectAll("p")—选择DOM中的所有段落。由于尚不存在,因此将返回一个空选择。将这个空的选择视为代表即将存在的段落。

.data(dataset)—计算并解析我们的数据值。我们的数据集中有五个值,因此,此点之后的所有操作都会执行五次,每个值一次。

.enter()—要创建新的数据绑定元素,必须使用enter()。此方法先查看DOM,然后再查看传递给它的数据。如果数据值多于相应的DOM元素,则enter() 创建一个新的占位符元素,您可以在上面处理魔术。然后,它将对这个新占位符的引用移交给链中的下一步。

.append("p")—接受由创建的占位符选择enter(),并将p元素插入DOM。万岁!然后,它将对刚创建的元素的引用交给链中的下一步。

.text("New paragraph!")—引用新创建的内容p并插入文本值。

数据在哪里?

console.log(d3.selectAll("p"))

打印: New paragraph! New paragraph! New paragraph! New paragraph! New paragraph!

使用数据

让我们将最后一行更改为:

.text(function(d) { return d; });

打印: 5 10 15 20 25

高功能

如果您不熟悉编写自己的函数(即方法),则函数定义的基本结构为:

function(input_value) {
    //Calculate something here
    return output_value;
}

我们上面使用的功能非常简单,没什么花哨的

function(d) {
    return d;
}

并封装在D3的text()函数中,因此我们函数返回的任何内容都将传递给text()。

.text(function(d) {
    return d;
});

想要保留数据

不起作用

.text("I can count up to " + d);

如果不d使用匿名函数,d则没有任何价值。可以将其d视为一个孤独的小占位符值,它只需要一个温暖,包含亲切关怀函数的寄生虫的拥抱即可。

可以改为:

.text(function(d) {  // <-- Note tender embrace at left
    return "I can count up to " + d;
});

打印: I can count up to 5 I can count up to 10 I can count up to 15 I can count up to 20 I can count up to 25

超越文字

再增加一行

.style("color", "red");

打印:现在所有文字均为红色

.style("color", function(d) {
    if (d > 15) {   //Threshold of 15
        return "red";
    } else {
        return "black";
    }
});

打印:注意前三行是黑色的,但是一旦d超过任意阈值15,文本就会变成红色。

绘图div

开始继续使用我们的简单数据集:

var dataset = [ 5, 10, 15, 20, 25 ];

共享样式放入一个名为的类中bar:

div.bar {
    display: inline-block;
    width: 20px;
    height: 75px;   /* We'll override this later */
    background-color: teal;
}

或者

div class="bar"></div>

设定属性-attr()

attr()

<p class="caption">
<select id="country">
<img src="logo.png" width="100px" alt="Logo" />

总共包含五个属性(和相应的值),所有这些都可以使用设置attr(): class | caption id | country src | logo.png width | 100px alt | Logo

要给我们的divsa类bar,我们可以使用:

.attr("class", "bar")

课堂笔记-classed()

classed()该方法可用于快速应用元素或从元素中删除类。上面的代码行可以重写为:

.classed("bar", true)

回到bar

div.bar {
    display: inline-block;
    width: 20px;
    height: 75px;
    background-color: teal;
}
var dataset = [ 5, 10, 15, 20, 25 ];

d3.select("body").selectAll("div")
    .data(dataset)
    .enter()
    .append("div")
    .attr("class", "bar");

打印:您应该看到五个垂直条,为我们数据集中的每个点生成一个垂直条,尽管它们之间没有空格,但它们看起来像一个大矩形。

设定样式-style()

style()

<div style="height: 75px;"></div>

将其添加到D3代码的末尾:

.style("height", function(d) {
    return d + "px";
});

打印:当D3遍历每个数据点时,的值d将设置为相应数据点的值。因此,我们将height值设置为d(当前数据值)加上px(以指定单位为像素)。产生的高度将是5px,10px,15px,20px和25px。

把这些条子弄高一些:

.style("height", function(d) {
    var barHeight = d * 5;  //Scale up by factor of 5
    return barHeight + "px";
});

data()的力量

var dataset = [ 25, 7, 5, 26, 11, 8, 25, 14, 23, 19,
                14, 11, 22, 29, 11, 13, 12, 17, 18, 10,
                24, 18, 25, 9, 3 ];
d3.select("body").selectAll("div")
    .data(dataset)  // <-- The answer is here!
    .enter()
    .append("div")
    .attr("class", "bar")
    .style("height", function(d) {
        var barHeight = d * 5;
        return barHeight + "px";
    });

输入data()十个值,它将循环十次。给它一百万个值,它将循环一百万次。(耐心一点。)

这就是强大的力量data()–足够聪明,可以遍历您向其抛出的任何数据集的全长,在链中执行其下方的每个方法,同时更新每个方法所处的上下文,因此d始终引用当前数据在循环中的那一点。

随机数据

var dataset = [];                        //Initialize empty array
for (var i = 0; i < 25; i++) {           //Loop 25 times
    var newNumber = Math.random() * 30;  //New random number (0-30)
    dataset.push(newNumber);             //Add new number to array
}

01.创建一个名为的空数组dataset。 02.启动一个for循环,该循环执行25次。 03.每次,它都会生成一个新的随机数,其值在0到30之间。 04.该新数字将附加到dataset数组中。(push()是一个将新值附加到数组末尾的数组方法。)

console.log(dataset) 。您应该看到25个随机数据值的完整数组。 打印: [29.008557153312662, 27.358102895412994, 1.8531420247113806, 16.458962264131355, 26.23005996178159, 21.423858033433216, 23.02974585678495, 14.492293735898214, 1.4009596341184483, 4.870038095128395, 29.705936448111455, 29.545566415279104, 23.387205995731414, 18.928408390390054, 23.580675188245568, 0.8581797024793425, 22.09853928963049, 8.800467041052556, 9.877315663211355, 2.9875160127928657, 11.943283651942558, 27.60897370588235, 29.809121268489193, 9.074583100012381, 5.488617241879057]

请注意,它们都是十进制或浮点值(14.793717765714973),而不是像我们最初使用的整数或整数(14)。如果您需要整数,则可以使用JavaScript的Math.round()方法。

var newNumber = Math.round(Math.random() * 30);

确实已四舍五入为整数

SVG底漆

使用SVG更可靠,视觉上一致且速度更快。可以使用Illustrator之类的矢量绘图软件来生成SVG文件,但是我们需要学习如何使用代码生成它们。

SVG元素

必须先创建SVG元素,然后才能绘制任何内容。可以将SVG元素视为在其上呈现视觉效果的画布。(在这方面,SVG在概念上类似于HTML的canvas元素。)至少,最好指定width和height值。如果未指定这些内容,则SVG将在其封闭元素中占用尽可能多的空间。

<svg width="500" height="50">
</svg>

另请注意,浏览器假定像素为默认度量单位。我们指定的尺寸500和50,不500px和50px。我们可以指定px明确的,或任意数量的其它支持单位,其中包括em,pt,in,cm,和mm。

简单形状

其中包括rect,circle,ellipse,line,text,和path。 增大的x值向右移动,而增大的y值向下移动。

rect绘制一个矩形。使用x与y指定左上角的坐标,width并height指定尺寸。这个矩形填充了我们SVG的整个空间:

<rect x="0" y="0" width="500" height="50"/>

circle画一个圆。使用cx和cy指定中心的坐标,并r指定半径。该圆位于我们500像素宽的SVG的cx中心,因为其(“ center-x”)值为250。

<circle cx="250" cy="25" r="25"/>

ellipse相似,但需要为每个轴指定单独的半径值。而是r使用rx和ry。

<ellipse cx="250" cy="25" rx="100" ry="25"/>

line画一条线。使用x1与y1指定线的一端的坐标,x2并y2指定另一端的坐标。一个stroke颜色必须为行中指定可见。

<line x1="0" y1="0" x2="500" y2="50" stroke="black"/>

text呈现文本。使用x指定的左边缘的位置,并y指定类型的垂直位置的基线。

<text x="250" y="25">Easy-peasy</text>

text除非另有说明,否则它将继承其父元素的CSS指定的字体样式。 我们可以重写该格式,如下所示:

<text x="250" y="25" font-family="sans-serif" font-size="25" fill="gray">Easy-peasy</text>

设置SVG元素的样式

默认样式是不带笔触的黑色填充。 SVG的常见属性是:

  • fill—颜色值。与CSS一样,颜色可以指定为
    • 命名的颜色- orange
    • 十六进制值-#3388aa或#38a
    • RGB值- rgb(10, 150, 20)
    • 具有Alpha透明度的RGB — rgba(10, 150, 20, 0.5)
  • stroke —颜色值。
  • stroke-width —数值测量(通常以像素为单位)。
  • opacity —一个介于0.0(完全透明)和1.0(完全不透明)之间的数值。

text:

  • font-family
  • font-size

与CSS平行的另一种方式是,将样式应用于SVG元素有两种方法:直接(内联)作为元素的属性,或使用CSS样式规则;为CSS样式目标的新类。

分层和绘图顺序

SVG中没有“层”,也没有真正的深度概念。SVG不支持CSS的z-index属性,因此形状只能在二维x / y平面内排列。

透明度

应用透明度的方法有两种:使用带alpha的RGB颜色或设置一个opacity值。

请注意,使用时rgba(),透明度将分别应用于fill和stroke颜色。 要将透明度应用于整个元素,请设置一个opacity属性。

绘制SVG

SVG元素的所有属性都指定为attribute。

<element property="value"/>

创建SVG

d3.select("body").append("svg");

建议:

var svg = d3.select("body").append("svg");

通过创建一个新的变量svg,我们可以捕获交回的参考文献append()。

为了简化您的工作,我建议将width和height值放入代码顶部的变量中

//Width and height
var w = 500;
var h = 50;

//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h);

数据驱动的形状

var dataset = [ 5, 10, 15, 20, 25 ];

svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle");

data()遍历每个数据点,circle为每个数据点创建一个. 请记住,selectAll()将返回对所有circles的空引用(尚不存在),data()将我们的数据绑定到我们将要创建的元素,enter()返回对新元素的占位符引用,append()最后circle向DOM中添加a 。

//完整的代码
//Width and height
var w = 500;
var h = 50;

//Data
var dataset = [ 5, 10, 15, 20, 25 ];

//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", 500)
    .attr("height", 50);

var circles = svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle");

circles.attr("cx", function(d, i) {
        return (i * 50) + 25;
    })
    .attr("cy", h/2)
    .attr("r", function(d) {
    return d;
});

生成:

<svg width="500" height="50">
    <circle cx="25" cy="25" r="5"></circle>
    <circle cx="75" cy="25" r="10"></circle>
    <circle cx="125" cy="25" r="15"></circle>
    <circle cx="175" cy="25" r="20"></circle>
    <circle cx="225" cy="25" r="25"></circle>
</svg>

逐步看一下代码:

circles.attr("cx", function(d, i) {
	return (i * 50) + 25;
})

(0 * 50) + 25 returns 25 (1 * 50) + 25 returns 75 (2 * 50) + 25 returns 125 (3 * 50) + 25 returns 175 (4 * 50) + 25 returns 225

漂亮的颜色,哦!

.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {
    return d/2;
});

数据类型

变数

变量是数据,是最小的数据构建块。变量是所有其他数据结构的基础,这些数据结构只是变量的不同配置。 请知道它是一种松散类型的语言,这意味着您不必预先指定将在变量中存储哪种类型的信息。但是,JavaScript会根据您为其分配的信息类型自动键入变量。

数组

var numbers = [ 5, 10, 15, 20, 25 ];
 ID | Value
------------
 0  |  5
 1  |  10
 2  |  15
 3  |  20
 4  |  25

做什么数组()

没有数组和强大的for()循环,基于代码的数据可视化将是不可能的。

for (var i = 0; i < numbers.length; i++) {
    console.log(numbers[i]);  //Print value to console
}

对象

数组非常适合简单的值列表,但是对于更复杂的数据集,您将需要将数据放入对象中。

var fruit = {
    kind: "grape",
    color: "red",
    quantity: 12,
    tasty: true
};

对象+数组

var fruits = [
    {
        kind: "grape",
        color: "red",
        quantity: 12,
        tasty: true
    },
    {
        kind: "kiwi",
        color: "brown",
        quantity: 98,
        tasty: true
    },
    {
        kind: "banana",
        color: "yellow",
        quantity: 0,
        tasty: true
    }
];

JSON格式

var jsonFruit = {
    "kind": "grape",
    "color": "red",
    "quantity": 12,
    "tasty": true
};

GeoJSON

正如JSON只是现有JavaScript对象语法的形式化一样,GeoJSON是JSON对象的形式化语法,已针对存储地理数据进行了优化。

var geodata = {
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [ 150.1282427, -24.471803 ]
            },
            "properties": {
                "type": "town"
            }
        }
    ]
};

制作条形图

首先回顾我们之前使用div元素制作的条形图。然后,我们将修改该代码以使用SVG绘制条形图,从而使我们在视觉呈现方面更具灵活性。

新图表

var w = 500;
var h = 100;
var barPadding = 1;  // <-- New!

var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h);

svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x", function(d, i) {
   		return i * (w / dataset.length);
    })
    .attr("y", function(d) {
    	return h - (d * 4);
    })
    .attr("width", w / dataset.length - barPadding)
    .attr("height", function(d) {
    	return d * 4;
    });

颜色

.attr("fill", "teal");

使用数据驱动颜色

.attr("fill", function(d) {
    return "rgb(0, 0, " + (d * 10) + ")";
});

标签

需要在可视化文件中以文本形式显示实际数据值。

svg.selectAll("text")
   .data(dataset)
   .enter()
   .append("text")

将text使用text()方法将代码扩展为在每个元素内包含一个数据值

svg.selectAll("text")
    .data(dataset)
    .enter()
    .append("text")
    .text(function(d) {
    	return d;
    })
    .attr("text-anchor", "middle")
    .attr("x", function(d, i) {
    	return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
    })
    .attr("y", function(d) {
    	return h - (d * 4) + 14;
    })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "white");

制作散点图

我们仅绘制了带有简单数据的条形图-只是一维数字集。 但是,当您有两组要相互绘制的值时,则需要第二个维度。散点图是一种常见的可视化类型,在两个不同的轴上代表两组对应的值:水平和垂直,x和y。

数据

var dataset = [
                [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
              ];

可以看到这10行中的每行将对应于我们可视化中的一个点。[5, 20]例如,对于row ,我们将其5用作x值,并将其20用于y。

散点图

//Width and height
var w = 500;
var h = 100;

var dataset = [
    [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
    [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];

//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h);

svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle")
    .attr("cx", function(d) {
    	return d[0];
    })
    .attr("cy", function(d) {
    	return d[1];
    })
    .attr("r", 5);

尺寸

.attr("r", function(d) {
    return Math.sqrt(h - d[1]);
});

标签

svg.selectAll("text")
    .data(dataset)
    .enter()
    .append("text")

    .text(function(d) {
    	return d[0] + "," + d[1];
    })
    
    .attr("x", function(d) {
    	return d[0];
    })
    .attr("y", function(d) {
    	return d[1];
    })
    
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "red");

“比例尺是从输入域映射到输出范围的函数。” D3比例尺是您定义其参数的功能。创建它们后,您可以调用scale函数,将其传递给数据值,然后它很好地返回缩放后的输出值。您可以定义和使用任意多个比例尺。

苹果和像素

var dataset = [ 100, 200, 300, 400, 500 ];

我们一直将数据值直接用作显示值,而忽略了单位差异。因此,如果售出了500个苹果,则相应的条形图将为500像素高。

那可能行得通,但是下个月(卖出600个苹果)又如何呢?一年后,卖出了1800个苹果?您的观众将不得不购买更大的显示器,以便能够看到那些非常高的苹果条的全高!(嗯,苹果棒!)

域和范围

  • 输入!领域!
  • 输出!范围!

创建一个标度,输入域为100,500,输出范围为10,350。如果您给该刻度定值100,它将返回10。如果你给了它500,它会吐回来的350。

正常化

归一化是根据可能的最小值和最大值将数值映射到介于0和1之间的新值的过程。例如,一年中有365天,则天数310对应于全年的约0.85或85%。

创建比例

通过访问D3的比例尺生成器,d3.scale然后输入所需的比例尺类型。

var scale = d3.scale.linear();

我们可以100,500通过将这些值domain()作为数组传递给方法来将刻度的输入域设置为:

scale.domain([100, 500]);

使用类似的方式设置输出范围range():

scale.range([10, 350]);

这些步骤可以如上所述单独进行,也可以链接成一行代码:

var scale = d3.scale.linear()
    .domain([100, 500])
    .range([10, 350]);

无论哪种方式,我们的秤都可以使用!

scale(100);  //Returns 10
scale(300);  //Returns 180
scale(500);  //Returns 350

缩放散点图

例如min()和max()快速分析我们的数据集。例如,这循环遍历数组中的每个x值并返回最大的一个值:

var xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[0]; })])
    .range([0, w]);
var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[1]; })])
    .range([0, h]);
    
// 返回缩放值(而不是原始值):
.attr("cx", function(d) {
    return xScale(d[0]);
})
.attr("cy", function(d) {
    return yScale(d[1]);
})
.attr("x", function(d) {
    return xScale(d[0]);
})
.attr("y", function(d) {
    return yScale(d[1]);
})

完整代码:

//Width and height
var w = 500;
var h = 100;

var dataset = [
    [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
    [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];

//Create scale functions
var xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[0]; })])
    .range([0, w]);

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[1]; })])
    .range([0, h]);

//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h);

svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle")
    .attr("cx", function(d) {
    	return xScale(d[0]);
    })
    .attr("cy", function(d) {
    	return yScale(d[1]);
    })
    .attr("r", function(d) {
    	return Math.sqrt(h - d[1]);
    });

svg.selectAll("text")
    .data(dataset)
    .enter()
    .append("text")
    .text(function(d) {
    	return d[0] + "," + d[1];
    })
    .attr("x", function(d) {
    	return xScale(d[0]);
    })
    .attr("y", function(d) {
    	return yScale(d[1]);
    })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "red");

完善情节

var padding = 20;


//Width and height
var w = 500;
var h = 300;
var padding = 20;

var dataset = [
    [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
    [410, 12], [475, 44], [25, 67], [85, 21], [220, 88],
    [600, 150]
];

//Create scale functions
var xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[0]; })])
    .range([padding, w - padding * 2]);

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[1]; })])
    .range([h - padding, padding]);

var rScale = d3.scale.linear()
    .domain([0, d3.max(dataset, function(d) { return d[1]; })])
    .range([2, 5]);

//Create SVG element
var svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h);

svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle")
    .attr("cx", function(d) {
    	return xScale(d[0]);
    })
    .attr("cy", function(d) {
    	return yScale(d[1]);
    })
    .attr("r", function(d) {
    	return rScale(d[1]);
    });

svg.selectAll("text")
    .data(dataset)
    .enter()
    .append("text")
    .text(function(d) {
    	return d[0] + "," + d[1];
    })
    .attr("x", function(d) {
    	return xScale(d[0]);
    })
    .attr("y", function(d) {
    	return yScale(d[1]);
    })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "red");

其他方法

d3.scale.linear() 还有其他几种方便的方法,在这里值得一提:

  • nice()—这告诉比例尺采用您给定的任何输入域,range()并将两端扩展到最接近的舍入值。“例如,对于[0.20147987687960267,0.996679553296417]的域,好的域是[0.2,1]。”
  • rangeRound()—rangeRound()代替使用,range()并且刻度尺输出的所有值都将四舍五入到最接近的整数。如果您希望形状具有精确的像素值,这将很有用,以避免因抗锯齿而产生的模糊边缘。
  • clamp()—默认情况下,线性刻度可以返回超出指定范围的值。例如,如果给定值超出其预期的输入域,则小数位将返回也在输出范围之外的数字。.clamp(true)但是,调用刻度会强制所有输出值都在指定范围内。意思是,过多的值将四舍五入到范围的低值或高值(以最接近的那个为准)。

其他秤

除了linear比例尺(上面已讨论过)外,D3还内置了其他几种比例尺方法:

  • identity — 1:1比例,主要用于像素值
  • sqrt —平方根刻度
  • pow —功率秤(对健身房有益)
  • log —对数刻度
  • quantize —一种线性标度,其输​​出范围具有离散值,适用于要将数据分类到“存储桶”中的情况
  • quantile —与上述类似,但其输入域具有离散值(当您已有“存储桶”时)
  • ordinal—序数标度使用非定量值(如类别名称)进行输出;比较苹果和橙子的完美选择

轴数

斧头介绍

与缩放功能很像,D3的轴实际上是您定义其参数的功能。与刻度不同,调用轴函数时,它不返回值,而是生成轴的可视元素,包括线条,标签和刻度线。

请注意,轴功能是特定于SVG的,因为它们生成SVG元素。此外,轴还打算用于定量刻度(与顺序刻度相反)。

设置轴

使用d3.svg.axis()创建一个通用的轴功能:

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom");
    
//D3的call()功能将选择作为输入,并将该选择移交给任何功能。call()将该选择移交给该xAxis函数,因此我们的轴在new中生成g。
svg.append("g")
    .call(d3.svg.axis()
                .scale(xScale)
                .orient("bottom"));

清理它

svg.append("g")
    .attr("class", "axis")  //Assign "axis" class
    .call(xAxis);
.axis path,
.axis line {
    fill: none;
    stroke: black;
    shape-rendering: crispEdges;
}

.axis text {
    font-family: sans-serif;
    font-size: 11px;
}

我们可以transform将整个轴组推到最下面:

svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + (h - padding) + ")")
    .call(xAxis);

注意使用(h - padding),因此组的顶部边缘设置为h,整个图像的高度减去padding我们之前创建的值。

检查壁虱

您可以使用以下命令自定义轴的各个方面,从大致的滴答声开始ticks():

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(5);  //Set rough # of ticks

为什么不呢?

是时候标记垂直轴了!将其添加到代码顶部附近

//Define Y axis
var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .ticks(5);
    
// 靠近底部的位置:
//Create Y axis
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(" + padding + ",0)")
    .call(yAxis);
// 请注意,标签将被定向left,并且yAxis组将按g数量转换到右侧padding。

最后的润色

为了向您证明我们的新轴是动态且可扩展的,我想从使用静态数据集切换为使用随机数:

//Dynamic, random dataset
var dataset = [];
var numDataPoints = 50;
var xRange = Math.random() * 1000;
var yRange = Math.random() * 1000;
for (var i = 0; i < numDataPoints; i++) {
    var newNumber1 = Math.round(Math.random() * xRange);
    var newNumber2 = Math.round(Math.random() * yRange);
    dataset.push([newNumber1, newNumber2]);
}
// 该代码初始化一个空数组,然后循环50次,每次选择两个随机数,然后将该对值加(“推”)到dataset数组中。
//完整代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>D3 Demo: Axes</title>
		<script type="text/javascript" src="../d3/d3.v3.min.js"></script>
		<style type="text/css">
			
			.axis path,
			.axis line {
				fill: none;
				stroke: black;
				shape-rendering: crispEdges;
			}
			
			.axis text {
				font-family: sans-serif;
				font-size: 11px;
			}

		</style>
	</head>
	<body>
		<script type="text/javascript">

			//Width and height
			var w = 500;
			var h = 300;
			var padding = 20;
			
			var dataset = [
							[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
							[410, 12], [475, 44], [25, 67], [85, 21], [220, 88],
							[600, 150]
						  ];

			//Create scale functions
			var xScale = d3.scale.linear()
								 .domain([0, d3.max(dataset, function(d) { return d[0]; })])
								 .range([padding, w - padding * 2]);

			var yScale = d3.scale.linear()
								 .domain([0, d3.max(dataset, function(d) { return d[1]; })])
								 .range([h - padding, padding]);

			var rScale = d3.scale.linear()
								 .domain([0, d3.max(dataset, function(d) { return d[1]; })])
								 .range([2, 5]);

			//Define X axis
			var xAxis = d3.svg.axis()
							  .scale(xScale)
							  .orient("bottom");

			//Create SVG element
			var svg = d3.select("body")
						.append("svg")
						.attr("width", w)
						.attr("height", h);

			//Create circles
			svg.selectAll("circle")
			   .data(dataset)
			   .enter()
			   .append("circle")
			   .attr("cx", function(d) {
			   		return xScale(d[0]);
			   })
			   .attr("cy", function(d) {
			   		return yScale(d[1]);
			   })
			   .attr("r", function(d) {
			   		return rScale(d[1]);
			   });

			//Create labels
			svg.selectAll("text")
			   .data(dataset)
			   .enter()
			   .append("text")
			   .text(function(d) {
			   		return d[0] + "," + d[1];
			   })
			   .attr("x", function(d) {
			   		return xScale(d[0]);
			   })
			   .attr("y", function(d) {
			   		return yScale(d[1]);
			   })
			   .attr("font-family", "sans-serif")
			   .attr("font-size", "11px")
			   .attr("fill", "red");
			
			
			//Create X axis
			svg.append("g")
				.attr("class", "axis")
				.attr("transform", "translate(0," + (h - padding) + ")")
				.call(xAxis);

		</script>
	</body>
</html>

格式化刻度标签

我们一直在使用整数-整数-既好又简单。但是数据通常比较混乱,在这种情况下,您可能希望对轴标签的格式进行更多控制。输入tickFormat(),使您可以指定数字的格式。

var formatAsPercentage = d3.format(".1%");

然后,告诉您的轴对其刻度使用该格式化功能,例如:

xAxis.tickFormat(formatAsPercentage);

查看我关于D3过渡的Eyeo Festival演讲