草庐IT

【微信小程序】使用ucharts时遇到的一些坑及其解决方案(持续更新)

莫诺库诺 2023-04-09 原文

前言

新项目是一个微信小程序,由于未来有跨端的可能,且为了降低开发成本,于是选择了uni-app框架进行小程序的开发。
项目需求在小程序上显示各种图表,web端使用的是功能的强大的echarts,但由于官方并没有专门的移动端版本(有和微信团队一起开发的微信版,但无法跨端),虽然网上有各种个人适配版,但或多或少都有一些问题。
因此综合考虑,最后选型的结果是使用专为uni-app适配的移动端图表插件ucharts。
ucharts方面这里就不多介绍了,本文主要记录在使用过程中遇到的各种坑。
本文使用ucharts的方式为组件工具
另外,其实有一些问题官方文档是有提供相应的解决方案的,而且提供的在线配置工具也能很方便的看到调整后的效果,当然这是收费的,30块一年,100块终身。收费这个事我就不多评论了,仁者见仁智者见智,毕竟单纯只是用的话是不收费的,为了开发便利,我还是暂且先开了一年。

1. uchats开启canvas2d后,渲染位置出错的问题。

先说结论:模拟器和实机的环境不同,模拟器上出现位置问题不用理会,真机上是正常的。

canvas2d接口拥有更好的性能,并且支持同层渲染。ucharts中开启canvas2d的方法为在组件的props中加入:canvas2d="true"

<qiun-data-charts :type="chartsShowType" :chartData="chartsData" :opts="opts" :ontouch="true" :canvas2d="true" :canvasId="canvasId" />

需要注意的是,在开启canvas2d后,必须要传入canvasId,并且canvasId一般为随机字符串,不能是数字开头。这里的id可以用随机生成字符串的方法生成。(但其实ucharts官方在组件内部内置了随机生成id的方法,即使你不传canvasId,内部也会自动生成,这节省了一些麻烦,如果不是有特殊需求的话,直接不传也可以)

开启该接口后,在微信开发工具中的模拟器中,图表的定位发生了问题:

好家伙,这能看?于是尝试各种改样式,统统无效。
后来通过查阅资料得知,小程序的canvas在开启2d模式后,因为模拟器的环境和实机环境的差异,ucharts的图表在模拟器上会出现穿透问题,但真机是正常的。由于小程序最终还是要运行在真机上的,因此建议无视模拟器上的穿透问题即可。需要在模拟器上看调整的结果时,可以暂时将2d模式关闭,要真机看效果时再加上。

2. 用scroll-view包裹后,提示窗位置不对的问题。

如果有要在同一个垂直显示多个图表的需求的情况下,自然需要用到scroll-view组件来实现垂直滚动,但是在使用scroll-view包裹封装好的ucharts组件后,会发现点击图表时的判定点会出现问题,明明点击线条却点到下面的图例去了……
这里官方文档提供了解决方法,如果你用的是原生方式绘图的话,那么需要自己动手去校正坐标了:

如果遇到不显示tooltip或者显示位置不正确的情况,可能是canvas外层包裹了sroll-view或swiper或父元素采用fixed定位导致touch事件的event错乱。解决方法:将错误的event坐标处理回正确的点击坐标,需要在event中加上或减去部分的x,y坐标,具体数值可以通过去掉外层包裹和没有去掉外层包裹打印出来的event计算得出

但如果你用的是组件方式,那么官方已经把封装好的属性inScrollView提供给你了,直接使用即可解决问题,
非常省事:

<qiun-data-charts :type="chartsShowType" :chartData="chartsData" 
	:opts="opts":ontouch="true" :canvasId="canvasId" :inScrollView="true"/>

3. 折线图图例的线宽太长,想要改小点怎么办?

默认折线图的图例线宽是这样的:

图例有点太长了,现在有需求要改小点,但是官方文档里并有提供对应的接口,怎么办?还能怎么办,只能去改源码了。
找到并打开ucharts插件目录下的u-charts.js文件,全局搜索图例绘制方法function drawLegend,并跳转到switch (item.legendShape)这一片段:

      switch (item.legendShape) {
        case 'line':
          context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pix);
          context.fillRect(startX, startY + 0.5 * lineHeight - 4 * opts.pix + 1, 10 * opts.pix, 6 * opts.pix);
          break;
        case 'triangle':
          context.moveTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
          context.lineTo(startX + 2.5 * opts.pix, startY + 0.5 * lineHeight + 5 * opts.pix);
          context.lineTo(startX + 12.5 * opts.pix, startY + 0.5 * lineHeight + 5 * opts.pix);
          context.lineTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
          break;
        case 'diamond':
          context.moveTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
          context.lineTo(startX + 2.5 * opts.pix, startY + 0.5 * lineHeight);
          context.lineTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight + 5 * opts.pix);
          context.lineTo(startX + 12.5 * opts.pix, startY + 0.5 * lineHeight);
          context.lineTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
          break;
        case 'circle':
          context.moveTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight);
          context.arc(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight, 5 * opts.pix, 0, 2 * Math.PI);
          break;
        case 'rect':
          context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pix);
          context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pix, 15 * opts.pix, 10 * opts.pix);
          break;
        case 'square':
          context.moveTo(startX + 5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
          context.fillRect(startX + 5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix, 10 * opts.pix, 10 * opts.pix);
          break;
        case 'none':
          break;
        default:
          context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pix);
          context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pix, 15 * opts.pix, 10 * opts.pix);
      }

可以看出来,这段代码就是根据图类型来绘制图例的,那么case 'line'就是折线图,我们再看moveTo()fillRect()方法,其实直接改矩形填充方法fillRect()的第三个参数width就可以了,但如果只改这里的话,图例的位置会有点歪,所以起始点也需要调整,调整前后的代码如下:
调整前

context.moveTo(startX, startY + 0.5 * lineHeight - 2 * opts.pix);
context.fillRect(startX, startY + 0.5 * lineHeight - 2 * opts.pix, 15 * opts.pix, 4 * opts.pix);

调整后

context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pix);
context.fillRect(startX, startY + 0.5 * lineHeight - 4 * opts.pix + 1, 10 * opts.pix, 6 * opts.pix);

具体调多少可以根据自己的需求慢慢去调。

4. 图例的位置放到图表上面后,X轴文字显示不全(开启了旋转)以及图例被数据文字覆盖的问题

问题现象如下图所示,这个很好改,直接通过设置修改边距即可,下面的代码设置仅供参考,具体数值可以自己去试。

opts: {
	padding: [15,10,5,15], // 画布填充边距[上,右,下,左],Array格式,修改前为[15,10,0,15]
    legend: {
    	margin: 10, // 图例外侧填充边距,默认为5
    }
}

修改之后:

另外在这里补充一下改变图例相对于图表位置的方法,下面以将图例放置于上方为例(其实官方文档里是有写的):

opts: {
	legend: {
		position: "top"
	}
}

5.ucharts加入动态切换图表类型

实在没有想到的是,ucharts居然不支持动态切换图表类型……
所以没办法又只能自己去改源码了。
由于opts和chartsData是可以动态更新的,通过官方文档中的指南可知,动态更新通过watch选项实现,因此打开qiun-data-charts.vue,跳转到watch选项的optsProps这一段:

optsProps: {
  handler(val, oldval) {
	if (typeof val === 'object') {
	  if (JSON.stringify(val) !== JSON.stringify(oldval) && this.echarts === false && this.optsWatch == true) {
		this.checkData(this.drawData);
	  }
	} else {
	  this.mixinDatacomLoading = false;
	  this._clearChart();
	  this.showchart = false;
	  this.mixinDatacomErrorMessage = '参数错误:opts数据类型错误';
	}
  },
  immediate: false,
  deep: true
},

其中的关键代码是this.checkData(this.drawData);,这是用来重绘图表的。想要动态改变类型,那么久需要监听改变类型的属性type,由于传入的值是字符串,因此判断一下前后的值是否一致就行了。代码如下:

type: {
	hanlder(val, oldval) {
		if (val !== oldval) {
			this.checkData(this.drawData);
		}
	}
}

此时便可实现动态切换图表类型了。如果你想加一些判断或是别的需求,那么以此为基础改就可以了。

P.S: 如果上面的代码无效,可以尝试下面的写法:

type(val, oldval) {
	if (val !== oldval) {
		this.checkData(this.drawData);
	}
}

两种写法其实是一样的,但不知道为何第一种写法会存在不生效的情况。

6. ucharts图表无法横向拖动

按照官方文档的介绍,开启横向拖动需要设置以下两点:

  • opts.enableScroll=true;
opts: {
	enableScroll: true
}
  • 在组件上将ontouch设为true
 <qiun-data-charts 
   type="column"
   :opts="opts"
   :chartData="chartData"
   :ontouch="true"
 />

设置完运行之后,发现虽然滚动条出现了,模拟器上也可以正常拖动,但是真机调试却无论如何都无法拖动。
如果你也出现这种情况,那么多半是因为在组件外部套上了scroll-view视图容器,如果是这样的话,可以尝试在scroll-view容器上将属性scroll-x设为true,应该能解决这个问题。

<scroll-view scroll-x="true"></scroll-view>

7.加入图表横屏展示的功能

由于手机屏幕宽度的限制,当数据量较大时,观看体验并不是很好,因此横屏展示的功能就显得必要了,还好ucharts官方提供了横屏展示的功能,可以免去自己手改的麻烦,不过在实现的时候还是需要注意一些地方。
详情参见:使用ucharts在小程序中加入图表横屏展示的功能

8. ucharts如何加入图例下拉显示/左右滚动的功能

在数据的项目比较多的时候,图例显示的也会很多,由于移动设备的屏幕比较小,且默认是竖屏观看,这样会导致下面的图表被压的很厉害,如下图所示(图片中间有个奇怪的箭头,暂且无视)

一个比较好的办法就是加入图例下拉显示/左右滚动的功能,由于这一块牵扯的东西比较多,相对而言难度也比较大,因此之后会单独写一篇博文详细的阐述解决思路和具体的代码,然后链接在此处。这里先做一个预告。

有关【微信小程序】使用ucharts时遇到的一些坑及其解决方案(持续更新)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐