JavaScript 3D圖表

raychase發表於2013-08-26

  在說3D圖表以前,首先要明確兩個概念,一個是資料的維度,一個是呈現資料載體的維度。對於資料的維度,一維的資料呈現,但是呈現的載體是二維的平面圖,比如餅圖:

JavaScript 3D圖表

  已經能夠很清晰地觀察到資料的分佈情況。資料如果增加一個維度,變成二維,呈現載體依然是二維的平面圖:

JavaScript 3D圖表

  資料表達依然是清晰的。但是,倘若再增加一維,這個時候就面臨了兩個問題:

  1. 資料的維度增加,複雜性也增大了;
  2. 計算機發展到現在,絕大多數情況下資料載體依然是二維的平面圖,如何展示三維的資料呢?

  這兩個問題中,第一個問題從本質上說,無法解決。資料的維度越大,理解起來理所當然地,也越來越困難。

  但是第二個問題,我們至少有兩種解決辦法。一種,在當前二維圖表的基礎上,通過顏色、圖形、數值的不同等等,來表示第三個維度的資料。例如,利用顏色不同來表示第三個維度的熱圖:

JavaScript 3D圖表

  在兩個維度經度和維度的情況下,第三個維度溫度通過顏色的不同來展示了。

 另一種,就是繪製3D的圖形,把第三個維度展示出來。需要注意的是,繪製3D的圖形僅僅是技術上的一種呈現形式,並不意味著它的易懂性要好於上面一種方式。實際上,我們還是需要看看具體的問題是什麼。

  明確了這些概念以後,我再來介紹兩則JavaScript的3D圖表,它們都是為了呈現三維的資料,而不僅僅是看起來3D而已,大部分JavaScript的3D圖表庫都是基於Canvas的,如果你對Canvas不瞭解請移步參閱這篇文章;其中一些則是支援WebGL的。WebGL是一種3D的繪圖示準,有了它,JavaScript就可以實現OpenGL標準能做的事情了,在HTML5 Canvas基礎上,WebGL允許硬體3D加速。

  webgl-surface-plot

JavaScript 3D圖表

 

  主頁點此。特性列表:

  • 純JavaScript實現,不需要Flash;
  • 滑鼠左鍵拖拽可以翻轉影象;
  • 按住Shift鍵可以縮放;
  • Web GL不可用的時候,可以直接使用Canvas繪製;
  • 自定義座標軸名稱;
  • 自定義顏色梯度和漸變;
  • 包裝為Google Visualization API的一部分。

  在IE下,藉助excanvas可以在VML下得到一樣的效果。

  對於這個例子,簡單過一下重點程式碼,首先這部分是著色器的程式碼(片段著色器和頂點著色器),包括座標軸和紋理:

        <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>

  這個方法用於保持兩圖步調一致:

            function coordinateCharts(){
                // Link the two charts for rotation.
                
                plot1 = surfacePlot.getChart();
                plot2 = surfacePlot2.getChart();
                
                if (!plot1 || !plot2) 
                    return;
                
                plot1.otherPlots = [plot2];
                plot2.otherPlots = [plot1];
            }

  每發生變化需要重繪的時候,呼叫:

surfacePlot.draw(data, options, basicPlotOptions, glOptions);
surfacePlot2.draw(data2, options, basicPlotOptions2, glOptions2);

  Demoparse主要用來根據使用者輸入的公式f(x,y)計算z的值:

            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真是太簡單了,如果你需要這種風格的柱狀圖:

JavaScript 3D圖表

  demo的程式碼非常簡單:

        //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即可。

相關文章