Skip to main content

动画

LDataAnimationService是二维中基于数据源更新的动画服务,主要应用是格点填色图层在时间和高度上的数据动画播放。数据动画的实现原理是不断更新数据解析器中的当前高度层currentZIdx或者当前时次currentTIdx

为了更方便的实现动画逻辑,QE内置了动画服务,用户只需要负责将数据更新到provider一遍之后(如果数据本身取回来就是多维度的,则直接可以动画,无需边加载边动画),就可以实现流畅平滑的动画效果。

创建动画

创建一个动画步骤如下:

  • 使用格点数据解析器解析多时次或多高度层格点数据
  • 数据动画本质是更新数据源的动画,分时间数据动画和高度数据动画,所以,我们的数据源应该是一个多时次或者多高度层的数据,即provider构建的标准格点数据格式GridData[time][level]中time的length或者level的length应大于1。

  • 创建格点图层,并添加到地图上
  • 用于动画的图层,需要是实现了IAnimationableLayer接口的图层。QE内置的二维图层中LPixelLayer,LCanvasPixelLayer等图层可以。

  • 创建动画服务
  • 将之前的数据源和图层都传进动画服务,对动画参数进行设置。

  • 开启动画
  • 代码如下:

    getBinary("public/demos/data/year.ano.zip").then(buffers => {//获取数据
        const provider = new QEGridDataProvider(buffers[0]);     //构建provider
        const layer = new LPixelLayer().setDrawOptions({         //创建图层
            fillColor: "color-temp-ano#res",                     //格点颜色
            fillMode: GridDataGLFillMode.pixel2,                 //绘制方式
        }).setDataSource(provider);                              //设置数据源
        map.addLayer(layer);                                     //添加图层到地图上
        const aniService = new LDataAnimationService(provider, { //创建数据动画服务
          layer: layer,                                          //动画的图层
          map: map,                                              //所在的地图
          all: provider.allGrids().length                        //动画的最长数量
        });
        aniService.start();                                      //开启动画
    });
    

    这样一个最基础的动画就完成了(假设所有时次全部存放于一个数据中)!

    动画速度

    可以通过设置动画参数timerInterval每帧时间间隔来调整动画的快慢。默认的时间间隔是30ms

    const aniService = new LDataAnimationService(provider, { //创建数据动画服务
      layer: layer,                                          //动画的图层
      map: map,                                              //所在的地图
      all: provider.allGrids().length,                       //动画的最长数量
      timerInterval: 100                                     //单位毫秒
    });
    

    更多动画配置参数请查看IDataAnimationServiceOptions

    自动补帧

    LPixelLayer的构建参数interpFromPreSource若设置为true,即表示对格点数据进行自动补帧,即会记录上一次数据源,并根据当前数据源中对应的tz小数部分进行补帧,使动画效果更加平滑。也就是当每次动画的索引递增时,对于有小数的,会对小数部分进行补帧。动画配置参数索引递增量delta,默认是0.05,如currenTIdx=0时,下一次就是0.05,0.1,0.15...以此类推。如果没有开启图层的自动补帧功能,即使索引增量小于1,也不会进行补帧。

    getBinary("public/demos/data/year.ano.zip").then(buffers => {//获取数据
        const provider = new QEGridDataProvider(buffers[0]);     //构建provider
        const layer = new LPixelLayer({
            interpFromPreSource: true                            //开启自动补帧
        }).setDrawOptions({                                      //设置样式
          fillColor: "color-temp-ano#res",                       //格点颜色
          fillMode: GridDataGLFillMode.pixel2,                   //绘制方式
        }).setDataSource(provider);                              //设置数据源
        map.addLayer(layer);                                     //添加图层到地图上
        const aniService = new LDataAnimationService(provider, { //创建数据动画服务
          layer: layer,                                          //动画的图层
          map: map,                                              //所在的地图
          all: provider.allGrids().length                        //动画的最长数量
        });
        aniService.start();                                      //开启动画
    });
    
    

    叠加等值线

    将LPixelLayer样式设置中的显示等值线开启,数据动画中也能显示等值线的变化。

    getBinary("public/demos/data/year.ano.zip").then(buffers => {//获取数据
        const provider = new QEGridDataProvider(buffers[0]);  //构建provider
        const layer = new LPixelLayer({
            interpFromPreSource: true                         //开启自动补帧
        }).setDrawOptions({                                   //设置样式
            fillColor: "color-temp-ano#res",                  //格点颜色
            fillMode: GridDataGLFillMode.pixel2,              //绘制方式
            lineWidth: 2,                                     //等值线宽度
            lineColor: "white",                               //等值线颜色
            showLine: true                                    //是否显示等值线
        }).setDataSource(provider);                           //设置数据源
        map.addLayer(layer);                                  //添加图层到地图上
        const aniService = new LDataAnimationService(provider, { layer: layer, map: map, all: provider.allGrids().length });//创建动画服务,传参有数据源,图层,地图,时次数
        aniService.start();                                   //开启动画
    });
    
    该等值线为GPU渲染,暂不支持标签,且只对气温、气压等连续性变量有较好的效果,建议仅在动画时使用,单时次数据等值线可使用CPU追踪算法,请参考等值线色斑图小节。

    完整示例

    边载入边播放

    如果我们的数据是单时次数据,需要加载多个时次的数据来进行动画播放时,我们的provider也支持动态载入。让先加载的数据先进行显示,避免等所有时次都加载了再显示,会出现等待时间过长的情况。我们可以通过格点类数据解析器不断更新其中的GridData来实现,即调用provider的updateGrid方法。当数据没有载入完成时,会自动暂停播放直到数据源中出现该时次的数据后会继续播放。

    以下示例为气温预报色斑图动画,数据是单时次数据。

    let provider: QEGridDataProvider;
    let tIdx = 0;                                            //时次计数器
    return getBinaries([                                     //请求一些列二进制数据
        "public/demos/data/t1.china.zip",
        "public/demos/data/t2.china.zip",
        "public/demos/data/t3.china.zip"
    ], files => {                                            //每个请求完成时的回调
        if (!provider) {                                     //判断provider是否赋值过,如果没有
            provider = new QEGridDataProvider(files[0],      //先用第一个数据构建provider
                { meta: { dataOffset: -70 } as any }         //设置数据偏移量覆盖数据头中的原有的
            );
            provider.currentZIdx = 19;                       //设置当前高度维度值
            const style: IPixelLayerStyleOptions = {         //绘制样式
                fillColor: "color-temp#res",                 //格点颜色:
                fillMode: GridDataGLFillMode.shaded1,        //显示方式:双线性插值色斑图
            }
            const layer = new LPixelLayer({                  //创建LPixelLayer图层
                name: "温度预报",                             //图层name
                interpFromPreSource: true                    //进行自动补帧(使动画效果更平滑)
            })
                .setDrawOptions(style)                       //设置样式
                .setDataSource(provider);                    //设置数据源
            map.addLayer(layer);                             //图层添加到地图上
            const animationService = new LDataAnimationService(//构建数据动画服务
                provider,                                      //传入数据源
                {   
                  all: 3,                                      //动画的总共时次数量
                  delta: 0.05,                                 //每次动画的索引递增量
                  layer: layer,                                //动画的图层
                  map: map                                     //所在的地图
                }
            );
            animationService.start();                          //开始动画
        } else {                                               //如果provider已经赋值过
            const current = new QEGridDataProvider(files[0], { meta: { dataOffset: -70 } as any });//使用当前获取到的数据再构建一个provider
            provider.lock();                                                                       //数据源锁定,指框架内置的注册在provider上的事件将暂停触发,比如onTChanged,onZChanged等
            current.allGrids()[0].forEach((zGrid, idx) => {                                        //遍历刚构建的provider的每个高度层数据
                provider.updateGrid(zGrid, tIdx, idx, current.gridOptions.zValues[idx], false);    //向provider上增加该时次(tIdx)的该高度上(idx)的GridData,最后一个参数false表示setActive即是否设置为当前时次以及当前高度
            });
            provider.unlock();                                                                     //数据源解锁
        }
        tIdx++;                                                                                    //时次计数加1
    });
    

    叠加格点填值

    数据动画中叠加格点填值图层时,格点填值图层中的格点值应该是要跟随动画变化一起变化的,所以需要将LGridLabelLayer的构建参数trackDataSource设置为true,表示监听数据源消息,在数据源更新后会自动刷新图层。

    const labelLayer = new LGridLabelLayer({         //创建格点标签图层
        trackDataSource: true                        //监听数据源消息,在数据源更新后会自动刷新图层
    }).setDrawOptions({                              //设置样式
        text: {                                      //格点文本样式
            data: "#decimal?len=1"                   //取格点值并保留1位小数
        }
    }).setDataSource(provider);                      //设置数据源
    map.addLayer(labelLayer);                        //将格点填值图层添加到地图上
    

    播放动画时,显示每帧数据时间

    动画中传入的数据源,格点类provider中有onIntTChanged(监听时间整数部分发生变化) 和 onTChanged(监听时间发生变化) 这两种方法来监听数据源的变化,在里面传入回调函数,就可以在时次发生变化时被调用。

    更多示例

    关于动画的更多示例可以参考栅格填色图层中的描述。