我们都知道,echarts 是百度开源的一个可以方便数据可视化呈现的一种图表,使用起来很方便,因此,对于一些定制程度不是很高的场景,我们直接用 echarts,可以快速成型我们的产品。

但是相对于 echarts 2d 图表来说,3d 的图表使用的人就少很多了,因此文档也不是很丰富,有时候碰到问题,也不是很容易找到解决方案。

因为本身就是一枚前端数据可视化工程师,所以在工作中,难免会用到各种 3d 框架,因此对于一些基本的 3d 概念,还是有所了解的。

因为 ECharts GL 提供了很好的 3d 图表,因此之前就直接在项目中拿来用了,但是最近客户反馈说,这个图的效果不太好,因为每次进去,位置都不太合适,需要手动拖动一下,才能勉强调整成这样:

image.png

可以看到的是,右边有很大一个空白的区域,这个图也就占用了整个视口一半的面积。

我心想,这改起来应该挺容易的吧,毕竟 3d 里面,物体的大小位置确定的情况下,调整下镜头参数就能从不同的角度看物体。

出于一个程序员的直觉,第一反应就是找官方文档,找找看有没有调节的方法。

确实是有,但是用起来好像并不是那么的方便,就是在下面这个页面里面:
https://www.echartsjs.com/option-gl.html#grid3D.viewControl

可以在配置项里,改变对应的属性,进行手动调整。但是又有个问题来了,这位置调整起来多不方便,我要调整到猴年马月才能调到合适的位置去啊。

于是我只得上 github 官方仓库的 issue 看看有没有人提到同样的问题,有没有热心的吃瓜群众给出好的解决方案。

我粗略的扫了一眼 issue,又搜了一下,或许是我关键字给的不太对,并没有找到跟我有一样烦恼的吃瓜群众,这就难办了。

但是我又不想继续手动调整,这难道只能研究源码了,从源码的层面去解决了么?但是这工作量有点大了,毕竟研究别人的代码并没有那么容易,何况还是个成熟的开源的框架。

其实我还是打算打开源码研究一下的,研究了半个小时未果,遂放弃了。

后来我灵机一动,echarts 里面不是根据 option 来生成图表的么?那如果我先在图上调整好位置,然后通过 charts.getOption() 方法获取 option,那么这个 option 里面的配置,是不是我调整好位置后的参数配置呢?

之所以有这么个想法,纯粹是出于惯性思维。

因为我们公司自己研发的 3d 引擎里面,就提供了现成的 api 可以获取到实时的镜头参数。但是在 echarts 里面没有的话,显然很不合理啊。

于是立刻开始尝试:

首先将 echarts 实力在控制台打出来,然后打开场景,填入数据,再拿到打印出的变量,调用 getOption api,获取 option。

可以看到的是,获取到的 grid3D 对象的属性是这样的:

[
    {
        "boxWidth": 50,
        "boxDepth": 500,
        "viewControl": {
            "distance": 400,
            "alpha": 3.069606211544383,
            "beta": 45.74964692390633,
            "minDistance": 40,
            "maxDistance": 400,
            "rotateSensitivity": [
                1,
                0
            ],
            "zoomSensitivity": 1,
            "panSensitivity": 1,
            "panMouseButton": "middle",
            "rotateMouseButton": "left",
            "orthographicSize": 150,
            "maxOrthographicSize": 400,
            "minOrthographicSize": 20,
            "center": [
                -113.52360803563104,
                -11.605600586260213,
                18.02255622141436
            ],
            "minAlpha": -90,
            "maxAlpha": 90,
            "autoRotate": false,
            "projection": "perspective",
            "autoRotateDirection": "cw",
            "autoRotateSpeed": 10,
            "autoRotateAfterStill": 3,
            "damping": 0.8
        },
        "light": {
            "main": {
                "intensity": 1.2,
                "shadow": true,
                "alpha": 30,
                "beta": 40,
                "shadowQuality": "high",
                "color": "#fff"
            },
            "ambient": {
                "intensity": 0.75,
                "color": "#fff"
            },
            "ambientCubemap": {
                "texture": null,
                "exposure": 1,
                "diffuseIntensity": 0.5,
                "specularIntensity": 0.5
            }
        },
        "splitLine": {
            "show": true,
            "lineStyle": {
                "opacity": 0.5,
                "color": [
                    "#ccc"
                ],
                "width": 1,
                "type": "solid"
            }
        },
        "show": true,
        "zlevel": -10,
        "left": 0,
        "top": 0,
        "width": "100%",
        "height": "100%",
        "environment": "auto",
        "boxHeight": 100,
        "axisPointer": {
            "show": true,
            "lineStyle": {
                "color": "rgba(0, 0, 0, 0.8)",
                "width": 1
            },
            "label": {
                "show": true,
                "formatter": null,
                "margin": 8,
                "textStyle": {
                    "fontSize": 14,
                    "color": "#fff",
                    "backgroundColor": "rgba(0,0,0,0.5)",
                    "padding": 3,
                    "borderRadius": 3
                }
            }
        },
        "axisLine": {
            "show": true,
            "lineStyle": {
                "color": "#333",
                "width": 2,
                "type": "solid"
            }
        },
        "axisTick": {
            "show": true,
            "inside": false,
            "length": 3,
            "lineStyle": {
                "width": 1
            }
        },
        "axisLabel": {
            "show": true,
            "inside": false,
            "rotate": 0,
            "margin": 8,
            "textStyle": null,
            "fontSize": 12
        },
        "splitArea": {
            "show": false,
            "areaStyle": {
                "color": [
                    "rgba(250,250,250,0.3)",
                    "rgba(200,200,200,0.3)"
                ]
            }
        },
        "postEffect": {
            "enable": false,
            "bloom": {
                "enable": true,
                "intensity": 0.1
            },
            "depthOfField": {
                "enable": false,
                "focalRange": 20,
                "focalDistance": 50,
                "blurRadius": 10,
                "fstop": 2.8,
                "quality": "medium"
            },
            "screenSpaceAmbientOcclusion": {
                "enable": false,
                "radius": 2,
                "quality": "medium",
                "intensity": 1
            },
            "screenSpaceReflection": {
                "enable": false,
                "quality": "medium",
                "maxRoughness": 0.8
            },
            "colorCorrection": {
                "enable": true,
                "exposure": 0,
                "brightness": 0,
                "contrast": 1,
                "saturation": 1,
                "lookupTexture": ""
            },
            "edge": {
                "enable": false
            },
            "FXAA": {
                "enable": false
            }
        },
        "temporalSuperSampling": {
            "enable": "auto"
        }
    }
]

找到 viewControl 对象:

"viewControl": {
    "distance": 400,
    "alpha": 3.069606211544383,
    "beta": 45.74964692390633,
    "minDistance": 40,
    "maxDistance": 400,
    "rotateSensitivity": [
        1,
        0
    ],
    "zoomSensitivity": 1,
    "panSensitivity": 1,
    "panMouseButton": "middle",
    "rotateMouseButton": "left",
    "orthographicSize": 150,
    "maxOrthographicSize": 400,
    "minOrthographicSize": 20,
    "center": [
        -113.52360803563104,
        -11.605600586260213,
        18.02255622141436
    ],
    "minAlpha": -90,
    "maxAlpha": 90,
    "autoRotate": false,
    "projection": "perspective",
    "autoRotateDirection": "cw",
    "autoRotateSpeed": 10,
    "autoRotateAfterStill": 3,
    "damping": 0.8
},

可以看到,center 就是当前镜头看向的目标点,alpha 就是垂直转角,beta 就是水平转角。

这里可以看大,其实他于一般的 3d 框架里面还是有点区别的,这里好像并没有镜头的概念,镜头都是固定死的,然后通过设置 center,alpha,beta 等相关参数去控制。

但是究竟是不是我们想的这样子的呢?试过了才知道,才有发言权嘛。

于是将这段配置拷到我们代码的配置里面去,怀着忐忑的心情刷新页面,映入眼帘的果然就是我们之前调整好的效果:

image.png

可以说这种方式就很赞了,但是还缺乏点自动化的功能,如果每次都这么手动的去调整,接下来再手动的获取相关参数,那岂不是累死了。

于是我们稍微搭配上一个工具 —— dat.gui,来辅助我们调整。

 const viewControl = this._option.grid3D.viewControl;

const gui = new dat.GUI({ name: 'My GUI' });

gui.add(viewControl, 'alpha', -90, 90)
  .onChange((val) => {
    chart.setOption(this._option);
  })
  .name('alpha');
gui.add(viewControl, 'beta', -180, 180)
  .onChange((val) => {
    chart.setOption(this._option);
  })
  .name('beta');

下面是小工具的样式:
image.png

调整起来果然很方便,也能实时的知道目前 alpha 和 beta 值是多少了。

如果你没用过 dat.gui,可以参考一下 https://github.com/dataarts/dat.gui,有源码,有示例,有文档。

刚才看了眼 echarts 的配置文档,里面有对 alpha 和 beta 的说明,可能比我解释的要详细些,现在直接贴到这里来了:

image.png

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐