问题现象
这个问题的现象说起来很简单.
小程序页面中有一篇很长的文章, 内部有一个 Echarts 图表, 手指上下滑动观看内容.
但是手指滑动区域在 Echarts 图表上时, 页面却不能滑动了.
如下图:
追踪问题原因
因为在小程序上渲染图表用到的是这个组件, 而这个组件确实不支持一些 Echarts 功能.
所以最开始我怀疑是这个组件的问题, 认为它把我的滑动事件给吞了.
为了确认这个问题, 我直接在这个组件 ec-canvas 旁加了个兄弟节点 view, 然后用绝对定位将其覆盖在 ec-canvas, 这样滑动的时候就会滑动到我的 view 上而不是 ec-canvas.
但是结果在 iOS 下, 还是不能滑动.
于是我给这个 view 的加了个背景色, 在 iOS 下的真机调试时发现, ec-canvas 组件还是在 view 上面.
不论是加大 view 上的 z-index 值, 还是将 absolute 改为 fixed, 反正 ec-canvas 组件所渲染的图表就是在 view 上面, 而没有被 view 遮挡.
这个 ec-canvas 组件是如此出众, 无论什么都遮盖不了它的风采.
而导致它如此出众的原因就是: 图表是一个 canvas 组件, 而小程序中 canvas 是一个原生组件.
接下来就让我们看看小程序中使用原生组件的限制.
小程序的原生组件使用限制
这里先附上链接: 小程序原生组件使用限制.
让我们看看关键的地方:
也就是说 canvas 这类原生组件就是比 view 这种非原生的组件层级高.
用 cover-view 来解决?
为了解决原生组件层级最高的限制. 小程序专门提供了 cover-view 和 cover-image 组件, 可以覆盖在部分原生组件上面. 这两个组件也是原生组件.
我将原来的兄弟 view 组件替换为了 cover-view 组件, 然后希望达到可以滑动的效果.
虽然此时 cover-view 组件已经可以覆盖在 canvas 上了, 但是依然不能滑动.
关于这个问题, 我们可以认为小程序的所有组件都是放在 webview 中, 而原生组件在 webview 中用的是占位符.
在滚动时, 获取原生组件占位符的位置, 再改变原生组件的位置.(如果仔细观察, 会发现这些原生组件有时会产生一些奇怪的抖动, 这一点可以佐证这个论点.)
所以 iOS 下, 我们手指在 canvas 和 cover-view 这类原生组件上滑动时, 事件是不会传导到 webview 上的, 页面也就不会滑动.
最终解决方案
对于这个问题, 因为我这边和 echarts 的交互比较少, 所以我的解决方案就是在 echarts 渲染完毕后将它替换为一张图片.
如果我更新了数据, 那么就重新放出 echarts, 等它渲染完毕后, 再次替换为一张图片.
由于公司代码不适合放出, 所以我搞了个简易版的代码放在这里.
wxml 文件关键代码:
- <view class="echart-container">
- <image wx:if="{{echartImgSrc!==''}}"src="{{echartImgSrc}}"class='echart-img'></image>
- <ec-canvas wx:if="{{echartImgSrc===''}}"id="mychart-dom-pie"canvas-id="mychart-pie"ec="{{ ec }}"bind:init="echartInit"></ec-canvas>
- </view>
JS 文件关键代码:
- Page({
- data: {
- ec: {
- },
- echartImgSrc: ''
- },
- initChart(canvas, width, height) {
- const chart = echarts.init(canvas, null, {
- width: width,
- height: height
- });
- canvas.setChart(chart);
- var option = {
- // ...
- };
- chart.on('finished', () => {
- this.selectComponent('#mychart-dom-pie').canvasToTempFilePath({
- success: res => {
- this.setData({
- echartImgSrc: res.tempFilePath
- })
- },
- fail: res => console.log('转换图片失败', res)
- });
- })
- chart.setOption(option);
- return chart;
- },
- echartInit(e) {
- this.initChart(e.detail.canvas, e.detail.width, e.detail.height);
- }
- });
总结
总的来说, 解决起来还算简单.
但是对于和 Echarts 有很多交互的场景, 这个方案就未必那么好实现了.
从这个问题入手, 我对微信小程序原生组件的玩法有了更多的认识.
更深入一点的认识就是, 微信小程序当下对原生组件的这种处理更像是在一件普通的布衣上贴上貂皮补丁.
虽然考虑到了原生组件所带来的性能优势, 但是同样也会引发大量的问题, 对于这件衣服的整体表现而言这些貂皮补丁恐怕并不见得是件好事.
希望以后小程序能从根本上解决这种问题吧.
来源: https://www.cnblogs.com/vvjiang/p/11161103.html