在说 3D 图表以前,首先要明确两个概念,一个是数据的维度,一个是呈现数据载体的维度。对于数据的维度,一维的数据呈现,但是呈现的载体是二维的平面图,比如饼图:
已经能够很清晰地观察到数据的分布情况。数据如果增加一个维度,变成二维,呈现载体依然是二维的平面图:
数据表达依然是清晰的。但是,倘若再增加一维,这个时候就面临了两个问题:
- 数据的维度增加,复杂性也增大了;
- 计算机发展到现在,绝大多数情况下数据载体依然是二维的平面图,如何展示三维的数据呢?
这两个问题中,第一个问题从本质上说,无法解决。数据的维度越大,理解起来理所当然地,也越来越困难。
但是第二个问题,我们至少有两种解决办法。一种,在当前二维图表的基础上,通过颜色、图形、数值的不同等等,来表示第三个维度的数据。例如,利用颜色不同来表示第三个维度的热图:
在两个维度经度和维度的情况下,第三个维度温度通过颜色的不同来展示了。
另一种,就是绘制 3D 的图形,把第三个维度展示出来。需要注意的是,绘制 3D 的图形仅仅是技术上的一种呈现形式,并不意味着它的易懂性要好于上面一种方式。实际上,我们还是需要看看具体的问题是什么。
明确了这些概念以后,我再来介绍两则 JavaScript 的 3D 图表,它们都是为了呈现三维的数据,而不仅仅是看起来 3D 而已,大部分 JavaScript 的 3D 图表库都是基于 Canvas 的,如果你对 Canvas 不了解请移步参阅这篇文章;其中一些则是支持 WebGL 的。WebGL 是一种 3D 的绘图标准,有了它,JavaScript 就可以实现 OpenGL 标准能做的事情了,在 HTML5 Canvas 基础上,WebGL 允许硬件 3D 加速。
webgl-surface-plot
主页点此。特性列表:
- 纯 JavaScript 实现,不需要 Flash;
- 鼠标左键拖拽可以翻转图像;
- 按住 Shift 键可以缩放;
- Web GL 不可用的时候,可以直接使用 Canvas 绘制;
- 自定义坐标轴名称;
- 自定义颜色梯度和渐变;
- 包装为 Google Visualization API 的一部分。
在 IE 下,借助 excanvas 可以在 VML 下得到一样的效果。
对于这个例子,简单过一下重点代码,首先这部分是着色器的代码(片段着色器和顶点着色器),包括坐标轴和纹理:
代码语言:javascript复制 <script id="shader-fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
varying vec4 vColor;
varying vec3 vLightWeighting;
void main(void)
{
gl_FragColor = vec4(vColor.rgb * vLightWeighting, vColor.a);
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec4 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;
varying vec4 vColor;
uniform vec3 uAmbientColor;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
varying vec3 vLightWeighting;
void main(void)
{
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vec3 transformedNormal = uNMatrix * aVertexNormal;
float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);
vLightWeighting = uAmbientColor uDirectionalColor * directionalLightWeighting;
vColor = aVertexColor;
}
</script>
<script id="axes-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec4 vColor;
void main(void)
{
gl_FragColor = vColor;
}
</script>
<script id="axes-shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec4 vColor;
uniform vec3 uAxesColour;
void main(void)
{
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = vec4(uAxesColour, 1.0);
}
</script>
<script id="texture-shader-fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void)
{
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
</script>
<script id="texture-shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
varying vec2 vTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main(void)
{
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
</script>
这个方法用于保持两图步调一致:
代码语言:javascript复制 function coordinateCharts(){
// Link the two charts for rotation.
plot1 = surfacePlot.getChart();
plot2 = surfacePlot2.getChart();
if (!plot1 || !plot2)
return;
plot1.otherPlots = [plot2];
plot2.otherPlots = [plot1];
}
每发生变化需要重绘的时候,调用:
代码语言:javascript复制surfacePlot.draw(data, options, basicPlotOptions, glOptions);
surfacePlot2.draw(data2, options, basicPlotOptions2, glOptions2);
Demoparse 主要用来根据用户输入的公式 f(x,y) 计算 z 的值:
代码语言:javascript复制 function Demoparse(ID_result, ID_code, valueArray, toolTips){
var el, expr;
el = document.getElementById(ID_result)
expr = document.getElementById(ID_code).value;
expr = Parser.parse(expr);
var result;
var idx = 0;
var d = 360 / numRows;
for (var x = 0; x < numRows; x ) {
valueArray[x] = new Array();
for (var y = 0; y < numCols; y ) {
result = expr.simplify({
x: x * d,
y: y * d
});
result = result.evaluate();
valueArray[x][y] = result / 4.0 0.25;
toolTips[idx] = "x:" x ", y:" y " = " result;
idx ;
}
}
}
Canvas 3D Graph
相比前者,Canvas 3D Graph 真是太简单了,如果你需要这种风格的柱状图:
demo 的代码非常简单:
代码语言:javascript复制 //Initialise Graph
var g = new canvasGraph('graph');
//define some data
gData=new Array();
gData[0]={x:500,y:500,z:500};
gData[1]={x:500,y:400,z:600};
gData[2]={x:500,y:300,z:700};
gData[3]={x:500,y:200,z:800};
gData[4]={x:500,y:100,z:900};
// sort data - draw farest elements first
gData.sort(sortNumByZ);
//draw graph
g.drawGraph(gData);
PS:如果你遇到无法显示 WebGL 图形的问题——它不仅对浏览器,还对硬件有要求。如果你使用 Opera 浏览器,在地址栏输入 about:gpu,以查看你的显卡是否被支持。如果是 FireFox,地址栏输入 about:config,寻找 webgl.force-enabled,双击,将该值改为 true 即可。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》
×Scan to share with WeChat