分享缩略图

分享到:
链接已复制
首页> 新闻中心>

Uniapp小程序集成ECharts的K线图并实现动态无感加载

2025-06-24 12:24:34

来源:新华网

字体:

背景介绍

最近写了一个小程序项目,需要实现K线图的日线小时线功能,并且要支持无感加载,也就是在左右滑动k线图时,不出现停顿加载,可以无限一直滑下去,用户感知不到数据的加载。

经过我的一番摸索,最后把需求给实现了。所以打算给大家做个分享,聊一聊实现该功能我踩的一些坑,以及一些解决问题的思路。

这里补充一下,uniapp实现K线图的方式不只是要用ECharts,但是我的需求除了要实现普通的K线图以外,还有实现K线的标注,也就是在K线的某个柱子上,标记特定是数据。经过调研我发现只有ECharts的MarkPoint属性能实现我的需求,所以最终的选择了ECharts。

然后,本项目的demo源码,有需要的也会免费分享给大家,通过关注公众号【码农炼金术】,私信“k线图”获取。

482328d5d498402191fe81494d33a93f.gif

项目集成

一、uniapp集成ECharts。

这个建议去官网查看集成的教程,一般都能找到。比如本案例我们可以先去uniapp官网找集成ECharts的方式,找不到我们再去ECharts官网找集成到uniapp的集成方式。

但是不排除一些比较冷门的技术,官网找不到集成的教程,这样我们再去各大网站搜索技术博文。

最终我找到是的uniapp官网的插件市场找到了集成的组件。当然有很多不同的插件,我这里综合考虑选了这款。

e595f910a1d54ac8ab8ba2a802f9a679.png

插件地址:https://ext.dcloud.net.cn/plugin?id=1538

二、集成方法(插件主页有介绍)。

1、下载插件

下载地址https://ext.dcloud.net.cn/plugin?id=1538

2、复制uni-ec-canvas文件夹到项目组件目录

74e402e4b6f64f89b1ff640734aa45a3.png

3、项目中使用

view

js

style

// 这里一定要注意 设置该组件宽高 以及display:block来确保canvas初始化的时候是有宽高的

集成方式非常简单,接下来就可以根据自己的页面,去配置echarts的属性,配置方式直接参考eharts官方问答即可。

三、本案例集成代码

这里我就按照我的需求,给大家演示一遍。

1、按照插件提示,先指定组件容器尺寸。

2、因为使用图表过程中需要用到echarts实例,所以把echarts引入

import * as echarts from '@/components/uni-ec-canvas/echarts'

3、配置ECharts

这里参考官网的配置项

 

deaee685235f7ebf91e7ce8261d5d52b.png

地址:https://echarts.apache.org/examples/zh/index.html#chart-type-candlestickhttps://echarts.apache.org/examples/zh/index.html#chart-type-candlestick

在线调试配置项

 

c2acdd61452b21f5d0d6fd224816109d.png

地址:https://echarts.apache.org/examples/zh/editor.html?c=candlestick-sh

k线图数据配置,主要是由一个多根柱子数据组成,一根柱子由主要的四个数据组成,分别是开盘价、收盘价、最高价(上引线)、最低价(下引线)。

4、完整配置项

{   lazyLoad: true,  option: {     animation: false,    tooltip: {       trigger: 'axis',      showDelay: 0,      hideDelay: 0,      axisPointer: {         type: 'cross'      },      backgroundColor: '#FFF',      textStyle: {         color: '#616161'      },      formatter: (params) => {           const str = `${ params[0].name}\n开:${ params[0].data[1]}\n收:${ params[0].data[2]}\n高:${ params[0].data[4]}\n低:${ params[0].data[3]}`          return str      },    },    grid: {       left: '10%',      right: '5%',      bottom: '5%'    },    xAxis: {       type: 'category',      data: [],      boundaryGap: false,      axisLine: {  onZero: false, show: false },      axisTick: {         lineStyle: {           type: 'dashed'        }      },      splitLine: {  show: false },      min: 'dataMin',      max: 'dataMax',    },    yAxis: {       scale: true,      splitArea: { show: false},      axisLine: { show: false},      splitLine: {         lineStyle: {           type: 'dashed'        }      }    },    series: [{       id: 'candlestick',      name: '日K',      type: 'candlestick',      data: [],      itemStyle: {         color: '#DF1525',        color0: '#0BB329',        borderColor: '#DF1525',        borderColor0: '#0BB329'      }    }],    dataZoom: [{       id: 'candlestickZoom',      type: 'inside',      minValueSpan: 23,      maxValueSpan: 23,      startValue: 0,      endValue: 0,      zoomLock: true    }]  }}

配置完的效果

 

6ade637afdf3645565d526cbb44e1bfc.png

 

5、这里要重点提一下k线图中dataZoom配置的几个属性:

dataZoom-inside. startValue

  • 数据窗口范围的起始数值。如果设置了 dataZoom-inside.start 则 startValue 失效。

  • dataZoom-inside.startValue 和 dataZoom-inside.endValue 共同用 绝对数值 的形式定义了数据窗口范围。

  • 注意,如果轴的类型为 category,则 startValue 既可以设置为 axis.data 数组的 index,也可以设置为数组值本身。但是如果设置为数组值本身,会在内部自动转化为数组的 index。

  • 关于坐标轴范围(axis extent)和 dataZoom-inside.startValue 的关系的更多信息,请参见:dataZoom-inside.rangeMode。

  •  

dataZoom-inside. endValue

  • 数据窗口范围的结束数值。如果设置了 dataZoom-inside.end 则 endValue 失效。

  • dataZoom-inside.startValue 和 dataZoom-inside.endValue 共同用 绝对数值 的形式定义了数据窗口范围。

  • 注意,如果轴的类型为 category,则 endValue 即可以设置为 axis.data 数组的 index,也可以设置为数组值本身。但是如果设置为数组值本身,会在内部自动转化为数组的 index。

  • 关于坐标轴范围(axis extent)和 dataZoom-inside.endValue 的关系的更多信息,请参见:dataZoom-inside.rangeMode。

     

dataZoom-inside. minValueSpan

  • 用于限制窗口大小的最小值(实际数值)。

  • 如在时间轴上可以设置为:3600 * 24 * 1000 * 5 表示 5 天。在类目轴上可以设置为 5 表示 5 个类目。

     

dataZoom-inside. maxValueSpan

  • 用于限制窗口大小的最大值(实际数值)。

  • 如在时间轴上可以设置为:3600 * 24 * 1000 * 5 表示 5 天。在类目轴上可以设置为 5 表示 5 个类目。

     

dataZoom-inside. zoomLock

  • 是否锁定选择区域(或叫做数据窗口)的大小。

  • 如果设置为 true 则锁定选择区域的大小,也就是说,只能平移,不能缩放。

配置项我们这里选的是startValue和endValue模式,为什么不选start和end,这个后面我们会解释。

我们的案例将startValue和endValue初始值都设置为0,是因为我们在配置的时候,startValue和endValue是不确定的,要根据接口第一次返回的数值进行计算。比如我们接口一次返回的数据是4天的k线图,也就是4x24根柱子的数据,那么我们画布视图首屏的窗口,展示的数值范围应该是startValue=72,endValue=95,值从0开始计算。这个计算过程会在我们初始化echarts的时候去执行。

minValueSpan、maxValueSpan以及zoomLock,都是为了固定画布窗口仅展示一天的数据,也就是24根柱子。所以我们minValueSpan=23,maxValueSpan=23,zoomLock=true。

k线图配置项基本已经调试好。接下来是咱们的要说的重点,无感加载。

 

无感加载

先说一下思路,echarts配置接收的是一个数组,也就是多根k线柱子的数据。因为第一次加载数据,默认我们是加载当前最新的,这样我们只要考虑画布往左滑动,加载历史的k线数据即可。正常解法是,我们只要在滑动画布的时候,动态往数组里面添加数据就可以,这是我们最直观的思路。

 

557a4d9c5e52409d9b57b5a0bb0d9d4c.gif

但是实现起来并不是这么简单。

第一个面临的问题是,我们要知道是在什么地方触发加载。要实现无感加载,那么我们就不能等到画布滑动到边缘的时候再触发加载数据,这样会造成滑动停顿中断,对用户体验不好。

所以我们要在用户滑动到边缘前,提前加载下一页的数据。echarts中配置我们通过监听datazoom的变化去判断什么时候触发下一页数据加载。代码如下

chart.on('datazoom', async (event) => {   this.calcStartEndValue() // 画布滑动,重新计算startValue和endValue位置  if(this.startValue <= 24){  // 当startValue 小于等于24,触发数据加载,也就是一个画布窗口的宽度    this.loadChartDatas(true) // 加载下一页数据  }});

我们通过startValue的变化来判断提前多少来加载下一页数据,这里我们设置的是startValue值变化到小于等于24时进行下一页数据加载,也就是滑动到还有一个画布窗口大小的位置时,进行触发加载下一页。

通过上面的监听配置代码,我们看到有一个calcStartEndValue的方法,是因为画布滑动的时候,我们配置的值startValue和endValue并不会自动变化,监听函数只返回了start和end的值,这两个配置是指当前画布窗口,所在整个k线图位置的百分比,所以,每次滑动画布之后,我们要手动计算当前百分比做在的startValue和endValue值是多少,这样才能保正我们在动态加载下一页数据时,计算出准确的startValue和endValue位置的值,保证加载数据时,画布不会跳转到错误的窗口位置。

所以,到这里大家应该已经能知道我们为什么不直接使用start和end的值作为配置项。是因为这两个配置项是百分比,当我们动态加载数据时,总数据量变多了,我们百分比不变的情况下,表示是数据范围就变大了,这样会导致我们minValueSpan和maxValueSpan值失效,画布视图窗口数据总览也会变多导致滑动交互出现错误。

可能有小伙伴想说,通过动态计算start和end的百分比,是不是也可以实现。这个我起初尝试过,发现每次重新复制start和end时,画布定位并不准确,k线数据会出现跳动,这个应该是echarts的问题,大家可以去尝试一下。

综上所述,所以我们最终是使用startValue和endValue配置,数据加载时通过重新计算startValue和endValue再赋值,可以保持画布位置不固定不乱跳动。

calcStartEndValue计算方法

calcStartEndValue(first){   if(first){     this.startValue = this.chartDatas.categoryData.length - 24;    this.endValue = this.chartDatas.categoryData.length - 1;  }else {     let start = chart.getOption().dataZoom[0].start;    this.startValue = parseInt(start / 100 * this.chartDatas.categoryData.length);    this.endValue = this.startValue + 23;  }}

loadChartDatas方法

async loadChartDatas(add){   console.log('加载数据...');  const chartDatas = await Api.getChartDatas();  if(add){     this.startValue = this.startValue + chartDatas.categoryData.length;    this.endValue = this.startValue + 23;    this.chartDatas.categoryData = [...chartDatas.categoryData, ...this.chartDatas.categoryData];    this.chartDatas.values = [...chartDatas.values, ...this.chartDatas.values];                    // console.log('new_start', this.startValue);    chart.setOption({         xAxis: {           data: this.chartDatas.categoryData,        },        series: [{           id: 'candlestick',          data: this.chartDatas.values        }],        dataZoom: [{           id: 'candlestickZoom',          startValue: this.startValue,          endValue: this.endValue        }],    });  }else {     this.chartDatas = chartDatas;    this.calcStartEndValue(true);  }}

最后补充一点,uni-ec-canvas中的echarts.js包,是可以从echarts官网自定义构建来替换的,按需构建可以减少包的文件大小。

但是需要注意echarts的版本不要超过v4.9.0。因为uni-ec-canvas最新的版本是1.0.3,在2020-08-17更新的。也就意味着,echarts的版本不要超过2020-08-17这个时间点,对应的这个时间之前最新的版本就是v4.9.0。

 

f5d3ddb7bdcba48564b38b33c7bbef1f.png

(uni-ec-canvas插件)

 

64873cf56e4f9a508d03c2f86f06710f.png

(echarts官网)

 

源码分享

关注【码农炼金术】公众号后,发送"k线图"获取完整案例源码

edc5a6c09d5a4fc9a91cc88ed71dbede.png

 

【责任编辑:新华网】
返回顶部