扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
javascript 自带有个对象(构造函数),Date().下面是代码:
创新互联专业为企业提供从江网站建设、从江做网站、从江网站设计、从江网站制作等企业网站建设、网页设计与制作、从江企业网站模板建站服务,十余年从江做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
var myDate = new Date(); //实例一个时间对象;
myDate.getFullYear(); //获取系统的年;
myDate.getMonth()+1; //获取系统月份,由于月份是从0开始计算,所以要加1
myDate.getDate(); // 获取系统日,
myDate.getHours(); //获取系统时,
myDate.getMinutes(); //分
myDate.getSeconds(); //秒
在jquery里可以直接用isNaN(),因为他是JavaScript的类库。是由JavaScript语言写的。
例如:
script type="text/javascript"
$(document).ready(function(){
alert(isNaN($('#id').val()));
});
/script可以直接使用
Highcharts Y轴的刻度保留2位小数是通过highcharts对象的yAxis属性中有一个tickPositions属性,用于手动设置轴上的坐标点的值,当此属性存在时,会覆盖tickInterval属性和tickPixelInterval属性(这两个属性并不会对渲染产生影响,只是根据手动赋值而自行调整这两个属性的默认值,横轴的tickPixelInterval属性默认为100,竖轴的tickPixelInterval属性默认为72,横轴竖轴的tickInterval属性均为空)。
Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,并且免费提供给个人学习、个人网站和非商业用途使用。HighCharts支持的图表类型有曲线图、区域图、柱状图、饼状图、散状点图和综合图表。
随着现在自定义可视化的需求日益增长,Highcharts、echarts等高度封装的可视化框架已经无法满足用户各种强定制性的可视化需求了,这个时候D3的无限定制的能力就脱颖而出。
如果想要通过D3完成可视化,除了对于D3本身API的学习, 关于web标准的HTML, SVG, CSS, Javascript 和 数据可视化的概念以及标准都是需要学习的。这无疑带来了较高的学习门槛,但这也是值得的,因为掌握 D3 后,我们几乎可以实现任何 2d 的可视化需求。
本文通过对D3核心模块分析以及进行具体案例实践的方式,来帮助初学者学习了解D3的绘图思路。
D3的全称是 Data-Driven Documents(数据驱动文档),是基于数据来操作文档的 JavaScript 库,其核心在于使用绘图指令对数据进行转换,在源数据的基础上创建新的可绘制数据, 生成SVG路径以及通过数据和方法在DOM中创建数据可视化元素(如轴)。
相对于Echats等开箱即用的可视化框架来说,D3更接近底层,它可以直接控制原生的SVG元素,并且不直接提供任何一种现成的可视化图表,所有的图表都需我们在它的库里挑选合适的方法构建而成,这也大大提高了它的可视化定制能力。而且D3 没有引入新的图形元素,它遵循了web标准(HTML, CSS, SVG 以及 Canvas )来展示数据 ,所以它可以不需要依赖其他框架独立运行在现代浏览器中。
在V4版本后,D3的 API 现在已经被拆分成一个个模块,我们可以根据自己的可视化需求进行按需加载。根据泛义可以将D3 API模块分为以下的几大类: DOM操作、数据处理,数据分析转换、地理路径,行为等 。
这里我们主要对 D3-selection 和 D3-scale 模块进行解析:
D3-selection (选择集) 是 D3js的核心模块,主要是用来进行选择元素,设置属性、数据绑定,事件绑定等操作。
选择元素: D3-selection 提供了两种方法来获取目标元素,d3.select():返回目标元素的第一个节点,d3.selectAll():返回目标元素的集合,乍一看有点类似原生API 的 querySelector 和 querySelectorAll,但是 d3.select 返回的是一个 selection 对象,querySelector 返回的是一个 NodeList 数组。通过控制台打印的信息,可以看到 selection 下的 groups 存放了所有选择的元素集合,parents 存放了所有选中元素的父节点。
设置属性或者绑定事件: 我们不需要关心 groups 的结构是怎么样的。当调用 selection.attr 或者 selection.style 的时候, selection 中的所有 group 的所有子元素都会被调用,group 存在的唯一影响是: 当我们传参是一个function 的时候,例如 selection.attr('attrName', function(data, i)) 或 selection.on('click', function(data, i)) 时, 传递的 function(data, i) 中, 第二个参数 i 是元素在 group 中的索引而不是在整个 selection 中的索引。
数据绑定: 实际上是给选择的DOM元素的 __data__ 属性赋值,这里提供了3种方式进行数据绑定:
(1)给每一个单独的 DOM 元素调用 selection.datum:d3.select('body').datum(20) 等价于 document.body.__data__ = 20
(2)从父节点中继承来数据, 比如: append , insert , select,子节点会主动继承父节点的数据:
(3) 调用 selection.data() 方法,支持传入装有基础数据类型的数据,也支持传入一个function(parentNode, groupIndex)根据节点索引与数据做映射,data()方法引入了 d3 中非常重要的 join 思想:
绑定 data 到 DOM 元素, 在D3中是通过比较 data 和 DOM 的 key 值来找到对应关系的。 如果我们没有单独设置 key 值,那么默认根据 data 的下标索引来设定,但是当数据顺序发生改变,这个默认下标 key 值 就变得不可靠了,这时我们可以使用 selection.data(data, keyFunction) 中的第二个参数 keyFunction,根据当前的数据返回一个对应的 key 值。通过下面的图例可以看出,不管是有一个还是多个 group(每个group 都是独立的),只要我们保证在任意一个 group 中的 key 值是唯一的,数据一旦发生变化都会反映给对应的 DOM 元素( update 的过程):
上面提到的都是data数据和DOM元素数量相同的情况下的数据绑定,那如果data数据和DOM元素数量不相同时,我们来看看 D3 又是如何进行数据绑定的:现在终于可以来介绍 D3-selecion 模块的核心 Join 思想了,这个思想简单来说就是 “不应该告诉D3去怎么创建元素, 而是告诉D3,.selectAll() 得到的 selecion 集合应该和 .data(data) 绑定的数据要怎么一一对应”。
从上图可以看出,在进行 d3.data(data) 数据绑定的时候,会产生三种状态的选择集:
用 Join 的方式来理解意味着,我们要做的事情仅仅是声明 DOM集合和数据集合之间的关系, 并且通过处理三个不同状态的集合 enter、update 、 exit 来描述这种关系。这种方式可以大大简化我们对DOM元素的操作,我们不需要再用 if 和 for 循环的方式来进行复杂的逻辑判断,来得到我们需要得到的元素集合。并且在处理动态数据的时候,可以通过处理这三种状态,轻松的展示实时数据和添加平滑的动态交互效果。
D3-scale (比列尺) 提供多种不同类型的比例尺。经常和 D3-axis 坐标轴模块一起使用。
D3-scale 提供了多种连续性和非连续性的比例尺,总体可以将他们分为三大类:
常用的一些比例尺:
(1)d3-scaleLinear 线性比例尺(连续性输入和连续性输出)
可以看出,调用d3.scaleLinear()可以生成线性比例尺,domain()是输入域,range()是输出域,相当于将domain中的数据集映射到range的数据集中。
使用示例:
映射关系:
(2)d3-scaleTime 时间比例尺(连续性输入和连续性输出)
时间比例尺与线性比例尺类似,只不过输入域变成了一个时间轴。正常我们使用比例尺都是个正序的过程,但是D3也提供了invert()以及invertExtent()方法,我们可以通过输出域中的具体值得出对应输入域的值。
使用示例:
(3)d3.scaleQuantize 量化比例尺(连续性输入和离散性输出)
量化比例尺是将连续的输入域根据输出域被分割为均匀的片段,所以它的输出域是离散的。
使用示例:
映射关系:
(4)d3. scaleThreshold 阈值比例尺(连续性输入和离散性输出)
阈值比例尺可以为一组连续数据指定分割阈值,阈值比例尺默认的 domain:[0.5] 以及默认的 range:[0, 1] ,因此默认的 d3.scaleThreshold() 等价于 Math.round 函数。 阈值比例尺输入域为 N 的话,输出域必须为 N + 1,否则比例尺对某些值可能会返回 undefined,或者输出域多余的值会被忽略。
使用示例:
存在三种映射关系:
a. 当domain和range的数据是 N : N+1
b. 当domain和range的数据是 N : N + 大于1
c. 当domain和range的数据是 N + 大于0 : N
(5)d3.scaleOrdinal 序数比例尺(离散性输入和离散性输出)
与scaleLinear等连续性比例尺不同,序数比例尺的输出域和输入域都是离散的。
使用示例:
存在三种映射关系:
a.当domain和range的数据是一一对应
b.当domain少于range的数据
c.当domain多于range的数据
通过以上的学习,应该对d3是如何操作DOM以及坐标轴的数据映射为相应的可视化表现有了一定的了解,下面我们来实际运用这两个模块,来实现我们常见的可视化图表:柱状图。
(1)首先添加一个SVG元素。
(2)根据我们上面说到 d3.scale 模块以及 d3.axis 模块绘制坐标轴,d3.scaleBand() 叫做序数分段比例尺,类似我们说的 d3.scaleOrdinal() 序数比例尺,但是它支持连续的数值类型的输出域,离散的输入域可以将连续的范围划分为均匀的分段。这里再讲一个细节,在绘制网格的时候,我们并没有额外添加 line 元素来实现,而是通过 d3.axis 坐标轴模块的 axis.ticks() 方法对坐标轴刻度进行了设置,通过 tickSIze() 设置了刻度线长度,来模拟和图表宽度相等的网格线,并且还可以通过 tickFormat() 对Y轴刻度值进行格式化转换。
(3)坐标轴绘制好了后,我们通过数据绑定来绘制与之对应的矩形(rect)元素了。
(4)这个时候柱状图已经基本绘制好了,我们再丰富内容展示,添加标签、标题等提示信息。
(5)最后我们通过给柱子绑定监听事件,实现tooltips的信息浮层交互。
通过对 d3.selection 、d3.scale 以及 d3.axis等模块的学习,我们已经可以绘制出常用的柱状图等图表,我们也可以通过d3提供的其他模块绘制出更加复杂的可视化效果,例如通过 d3-hierarchy(层级模块) 实现层级树图可视化,d3-geo(地理投影) 实现地图数据可视化等,本文讲解的内容还只是D3库的冰山一角。所以等我们掌握了D3后,限制我们实现可视化的不再是技术而是想象力。
div id="d"/div
canvas id="clock" width="400" height="400"/canvas
script type="text/javascript"
var time = new Date();
var h = time.getHours(); //时
var m = time.getMinutes(); //分
var s = time.getSeconds(); //秒
h=h12?(h-12)*5+parseInt(m/12):h*5+parseInt(m/12); //时针 初始位置
//=====================================
var x=200,y=200,sAngle=0; //x y 原点 秒针角度变量
function draw(){
var c=document.getElementById("clock");
var ctx=c.getContext("2d"); //获取绘图对象
ctx.clearRect(0,0,c.width,c.height); //清除上次绘制的图形
s++;//秒针
ctx.fillStyle = '#fff' //填充白色背景色
ctx.fillRect(0,0,c.width,c.height); //设置画布区域
//填充圆点,在画布中心(200,200)绘制一个半径10px的圆形
ctx.beginPath();
ctx.arc(x,y,10,0,Math.PI*2,true);
ctx.fill();
ctx.closePath();
//填充版权文字
ctx.fillStyle="#ccc";
ctx.font = "12pt Arial";
ctx.fillText(" beyond",150,250);
//调用日期并填充到画布中
ctx.fillStyle="#666";
ctx.font = "14pt Verdana";
ctx.fillText(time.getMonth()+1+"-"+time.getDate(),183,170);
ctx.save(); //保存当前绘图状态
// 时间刻度
for(var i=0;i12;i++){
var angle=(Math.PI*2)/12;
ctx.beginPath();//开始绘制
ctx.font="12px Arial";
if(i==0||i==3||i==6||i==9){
ctx.fillStyle="red";
radius=4;
}else{
ctx.fillStyle="blue";
radius=3;
}
ctx.arc(x,y-100,radius,0,Math.PI*2,true); //画圆
ctx.fill(); //填充路径
trans(ctx,x,y,angle); //刻度分布
}
ctx.restore(); //恢复上次保存的绘图状态
sAngle=(Math.PI*2)/60*s; //秒度
//时针转动
ctx.save();
ctx.strokeStyle="red";
ctx.lineWidth=3;
trans(ctx,x,y,(Math.PI*2)/60*h);
pointer(ctx,x,y,y-40);
ctx.restore();
//分针转动
ctx.save();
ctx.strokeStyle="blue";
ctx.lineWidth=2;
trans(ctx,x,y,(Math.PI*2)/60*m);
pointer(ctx,x,y,y-68);
ctx.restore();
//秒针转动
ctx.save();
ctx.strokeStyle="#000";
trans(ctx,x,y,sAngle);
pointer(ctx,x,y,y-80);
ctx.restore();
//数据整理
if(s%60==0){
sAngle=0,s=0,m++;
if(m%12==0){ //每十二分 时针旋转一次
if(m!=0)h++;
if(m%60==0)m=0;
}
if(h%60==0)h=0;
}
}
//绘制指针
function pointer(ctx,x,y,z){
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(x,z);
ctx.stroke();
ctx.fill();
}
//据坐标旋转
function trans(ctx,x,y,angle){
ctx.transform(Math.cos(angle), Math.sin(angle),
-Math.sin(angle), Math.cos(angle),
x*(1-Math.cos(angle)) + x*Math.sin(angle),
y*(1-Math.cos(angle)) - y*Math.sin(angle))
}
setInterval("draw()",1000);
/script
/div
时间数据格式最常与折线图搭配,可以用来看数据的走向及趋势,Flot的时间数据格式是以javascript timestamps为单位,也就是毫秒,从"1970-01-01 00:00"算起.在这章我们会介绍如何自定时间数据的输出格式.
黄金价格(时间格式数据)范例
绘图用到的档案
所有需要的档案清单如下.jquery.flot.axislabels.js为轴标签的插件,jquery.flot.time.js为使用时间数据格式的插件.另外为了让IE8以下版本能显示Flot图表,所以需要excanvas.min.js插件.
script type="text/javascript" src="/js/jquery-1.8.3.min.js"/script
!--[if lte IE 8]script language="javascript" type="text/javascript" src="/js/flot/excanvas.min.js"/script![endif]--
script type="text/javascript" src="/js/flot/jquery.flot.js"/script
script type="text/javascript" src="/js/flot/jquery.flot.time.js"/script
script type="text/javascript" src="/js/flot/jquery.flot.axislabels.js"/script
建立数据
在范例里我们绘制了2条折线图,所以我们就建立2个数组变量,并在数组里插入数组格式如[x ,y],分别代表x轴及y轴资料,在x轴我们用了一个自定义函式gd(),是为了要抓取javascript timestamps资料,在这里我们准备了一个月的黄金价格数据,程序代码如下.
//sell out
var data1 = [
[gd(2013, 1, 2), 1690.25], [gd(2013, 1, 3), 1696.3], [gd(2013, 1, 4), 1659.65], [gd(2013, 1, 7), 1668.15], [gd(2013, 1, 8), 1656.1], [gd(2013, 1, 9), 1668.65], [gd(2013, 1, 10), 1668.15], [gd(2013, 1, 11), 1680.2], [gd(2013, 1, 14), 1676.7], [gd(2013, 1, 15), 1680.7], [gd(2013, 1, 16), 1689.75], [gd(2013, 1, 17), 1687.25], [gd(2013, 1, 18), 1698.3], [gd(2013, 1, 21), 1696.8], [gd(2013, 1, 22), 1701.3], [gd(2013, 1, 23), 1700.8], [gd(2013, 1, 24), 1686.75], [gd(2013, 1, 25), 1680], [gd(2013, 1, 28), 1668.65], [gd(2013, 1, 29), 1671.2], [gd(2013, 1, 30), 1675.7], [gd(2013, 1, 31), 1684.25]
];
//buy in
var data2 = [
[gd(2013, 1, 2), 1674.15], [gd(2013, 1, 3), 1680.15], [gd(2013, 1, 4), 1643.8], [gd(2013, 1, 7), 1652.25], [gd(2013, 1, 8), 1640.3], [gd(2013, 1, 9), 1652.75], [gd(2013, 1, 10), 1652.25], [gd(2013, 1, 11), 1664.2], [gd(2013, 1, 14), 1660.7], [gd(2013, 1, 15), 1664.7], [gd(2013, 1, 16), 1673.65], [gd(2013, 1, 17), 1671.15], [gd(2013, 1, 18), 1682.1], [gd(2013, 1, 21), 1680.65], [gd(2013, 1, 22), 1685.1], [gd(2013, 1, 23), 1684.6], [gd(2013, 1, 24), 1670.65], [gd(2013, 1, 25), 1664], [gd(2013, 1, 28), 1652.75], [gd(2013, 1, 29), 1655.25], [gd(2013, 1, 30), 1659.7], [gd(2013, 1, 31), 1668.2]
];
function gd(year, month, day) {
return new Date(year, month - 1, day).getTime();
}
接着我们建立一个叫dataset的数组变量,把上面建立好的数据放到数组里的对象中,设定到data属性里,在这里我们还设定了label属性,这将会用做图例的显示名称,而color属性决定了折线图的颜色,points.fillColor属性则是数据点的填满颜色,另外也要将points.show设定为true,数据点符号才会显示,最后因为我们是画折线图,所以要设定lines.show为true,折线图才会出现.
var dataset = [
{
label: "Sell out",
data: data1,
color: "#FF0000",
points: { fillColor: "#FF0000", show: true },
lines: { show: true }
},
{
label: "Buy in",
data: data2,
xaxis:2,
color: "#0062E3",
points: { fillColor: "#0062E3", show: true },
lines: { show: true }
}
];
建立图表选项
在这里我们为了让图表看起来美观一点,我们设定了series.shadowSize,这会让折线图绘出阴影,让图表看起来更立体.
series: {
shadowSize: 5
}
在y轴的选项里,yaxis.color表示y轴刻度卷标的文字颜色,这里设定成黑色的.而yaxis.tickDecimals是用来设定y轴刻度标签的小数点位数,因为黄金价格是有小数的,所以我们设定成显示2位小数,输出看起来就会是像1650.00,而所有axisLabel开头的属性都是属于轴标签的,也就是我们在一开始时加入的jquery.flot.axislabels.js插件的功能.
yaxis: {
color: "black",
tickDecimals: 2,
axisLabel: "Gold Price in USD/oz",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 5
}
因为x轴的部份是显示时间数据格式的,所以在xaxis.mode里需要设定成"time",这样Flot才会以时间格式编译x轴的刻度标签.另外我们用了xaxis.tickFormatter自行定义刻度标签要输出的格式,在这里我们输出成星期几,若是不用自行定义的方式,你也可以设定timeformat:"%a"让卷标显示星期几.xaxis.position表示刻度标签要出现在那里,我们设定成"top"表示出现在图表的上方.
在x的第二个轴的部份,我们设定了tickformat:"%m/%d",表示刻度标签会输入出:01/05的日期格式,若要输出年含年度的日期可以写成"%Y/%m/%d",输出会变成:2012/01/05,Flot还支持其它的日期格式设定.另外我们还设定了tickSize:[3, "day"],这表示刻度标签会以每3天的间隔显示,如果设定成tickSize:[2, "month"]则表示刻度标签会以每2个月的间隔显示
var dayOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"];
xaxes: [{
mode: "time",
tickFormatter: function (val, axis) {
return dayOfWeek[new Date(val).getDay()];
},
color: "black",
position: "top",
axisLabel: "Weekday",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 5
},
{
mode: "time",
timeformat: "%m/%d",
tickSize: [3, "day"],
color: "black",
axisLabel: "Date",
axisLabelUseCanvas: true,
axisLabelFontSizePixels: 12,
axisLabelFontFamily: 'Verdana, Arial',
axisLabelPadding: 10
}]
完成绘图
最后我们再呼叫$.plot把上面建立的dataset以及options带入,再呼叫UseTootip()后,整个绘图即完成.
$(document).ready(function () {
$.plot($("#flot-placeholder"), dataset, options);
$("#flot-placeholder").UseTooltip();
});
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流