微信小程序echarts图表层级过高解决方案

5,965 阅读2分钟

环境

框架:uniapp打包微信小程序

echarts组件:echarts-for-wx

由于自己用得饼图多个地方引用,为了复用,封装成了组件

问题

页面上有固定定位的按钮选择区域,界面内容有滚动条,但是由于canvas组件层级过高,滚动时canvas覆盖在了fixed定位的按钮选择区域

踩坑

  1. canvas属于小程序源生组件,层级最高,其他组件的z-index对它失效
  2. canvas绘制图片必须给宽高,不然会显示白板,图片绘制也是白板
  3. canvas导出图片必须得占用高度,但opacity和visiblity在真机都失效,display也不能使用

方案

在echarts绘制完成后,调用canvas转图片方法transformImage,将图片显示出来,canvas向左平移,完美解决

1、最外层调用饼图组件,只需要传参,没有什么特殊处理

...
<pie-chart
           :canvasData="inspectorTasksStatistics.itemData"
           canvasId="inspect_chart"
           :nowRatio="inspectorTasksStatistics.completion_rate"
           ref="piechart"
           nowType="worker"
           ></pie-chart>
...
​
...
//根据ref调用peichart组件方法
  this.getInspectorTasksStatistics(data).then((res) => {
        try {
          this.$refs.piechart.chartInit();
        } catch (e) {
          console.log(e);
        }
      });
...
​
​

2、饼图引入后,监听切换时候,先重新绘制echarts,在echarts绘制finished后调用最底层uni-ec-canvas.vue的transformImage方法,重绘图片,为了防止echarts图没有高度,通过监听值变化动态平移canvas,值变化时候先绘制canvas,有图片后,再将canvas平移,展示图片,具体操作见下chart_pie组件:

<!--echarts chart_pie饼图封装-->
<template>
  <view class="canvas-wrap">
    <view>
            <!-- 绘制echarts区域 -->
      <uni-ec-canvas
        class="uni-ec-canvas"
        :class="{ translateCanvas: isShowImage }"
        :ref="canvasId"
        canvas-id="uni-ec-canvas"
        :ec="ec"
        force-use-old-canvas="true"
        @chartImgUrl="chartImgUrlParent"
      >
      </uni-ec-canvas>
    </view>
        <!-- 转化图片占位区域 -->
    <view class="img-wrap">
      <image
        class="img-chart"
        :src="canvasImgUrl"
        v-if="isShowImage"
        mode="widthFix"
      ></image>
    </view>
  </view>
</template><script>
import uniEcCanvas from "../../components/uni-ec-canvas/uni-ec-canvas.vue";
import * as echarts from "../../components/uni-ec-canvas/echarts";
​
export default {
  props: {
    canvasData: { type: Array },
    canvasId: { type: String },
    nowRatio: { tyoe: Number },
    nowType: { type: String },
  },
  components: {
    uniEcCanvas,
  },
  data() {
    return {
      ec: {
        lazyLoad: true, // 延迟加载
      },
      canvasImgUrl: "",
      isShowImage: false,
    };
  },
  created() {},
  mounted() {
    try {
      this.$refs[this.canvasId].init(this.initChart);
    } catch (error) {
      console.log(err);
    }
  },
  methods: {
    chartInit() {
      this.$refs[this.canvasId].init(this.initChart);
    },
​
    initChart(canvas, width, height, canvasDpr) {
      this.isShowImage = false;
      let chart = null;
      let that = this;
      chart = echarts.init(canvas, null, {
        width: width,
        height: height,
        devicePixelRatio: canvasDpr,
      });
      canvas.setChart(chart);
      chart.setOption(this.pieGetOption());
            //echarts绘制完成
      chart.on("finished", function() {
        that.$refs[that.canvasId].transformImage();
        this.isShowImage = true;
      });
​
      return chart;
    },
        //饼图
    pieGetOption() {
      let that = this;
      let data = that.canvasData;
      let colorArr = [];
      if (this.nowType === "admin") {
        colorArr = ["#15ADEF", "#FFBF34", "#F0230D"];
      } else if (this.nowType === "worker") {
        colorArr = ["#15ADEF", "#F0230D", "#FFBF34"];
      }
      var option = {
        title: {
          text: "巡检任务完成率:" + (this.nowRatio * 100).toFixed(2) + "%",
          right: 10,
          top: 15,
          textStyle: {
            fontSize: 13,
            fontWeight: 400,
          },
        },
        tooltip: {
          show: false,
        },
        legend: {
          orient: "vertical", //垂直显示
          type: "scroll",
          pageIconColor: "#fff", // 可以点击的翻页按钮颜色
          pageIconInactiveColor: "#ccc", // 禁用的按钮颜色
          y: "center", //延Y轴居中
          right: 10,
          top: 40,
          selectedMode: false,
          textStyle: {
            rich: {
              a: {
                fontSize: 12,
                padding: [0, 10, 0, 10],
                width: 35,
              },
              b: {
                fontSize: 12,
                padding: [0, 10, 0, 10],
                width: 35,
              },
            },
          },
          formatter: function(name) {
            var target;
            var total = 0;
            for (var i = 0, l = data.length; i < l; i++) {
              total += data[i].value;
              if (data[i].name == name) {
                target = data[i].value;
              }
            }
            var arr = ["{a|" + name + "}", "{b|" + target + "}"];
            return arr.join("");
          },
        },
        color: colorArr,
        series: [
          {
            type: "pie",
            radius: "90%",
            center: ["25%", "50%"],
            data: that.canvasData,
            label: {
              //饼图图形上的文本标签
              normal: {
                show: true,
                position: "inner", //标签的位置
                textStyle: {
                  fontWeight: 300,
                  fontSize: 8, //文字的字体大小
                  color: "#fff",
                },
                formatter: "{b}:{c}",
              },
            },
          },
        ],
      };
      return option;
    },
    chartImgUrlParent(src) {
      this.canvasImgUrl = src;
    },
  },
  watch: {
        //根据值得变化,判断转化完图片的显示隐藏
    canvasImgUrl(val, oldVal) {
      if (val != oldVal) {
        this.isShowImage = true;
      } else {
        this.isShowImage = false;
      }
    },
  },
};
</script><style scoped>
.canvas-wrap,
.uni-ec-canvas,
.img-wrap {
  width: 680rpx;
  height: 260rpx;
}
​
.canvas-wrap {
  position: relative;
}
​
.uni-ec-canvas {
  position: absolute;
  left: 0;
  top: 0;
}
​
.img-wrap {
  position: absolute;
  left: 0;
  top: 0;
  background: #fff;
}
​
.img-chart {
  width: 100%;
  height: 100%;
}
​
.translateCanvas {
  left: -1000rpx;
}
</style>

3、下载的扩展包uni-ec-canvas/uni-ec-canvas.vue添加将canvas转成转成图片的代码

methods:{
...
//将canvas转化成图片,向父组件传递生成的图片
    transformImage() {
      let that = this;
      uni.canvasToTempFilePath(
        {
          canvasId: that.canvasId,
          success: (res) => {
            let tempFilePath = res.tempFilePath;
            that.$emit("chartImgUrl", tempFilePath);
          },
          fail: (res) => {
            console.log(res);
          },
        },
        this
      );
    },
...
}
​

最后展示效果:

869febb1fca7e01afba415933d2a771.jpg