移动端taro中可视化探索实践

技术栈

react ,微信小程序,taro -taro-ui

测试情况
在taro中测试
Echart正常使用
F2正常使用
Ant Design Charts报错

对比

在这里插入图片描述

ECharts

ECharts 和微信小程序官方团队合作,提供了 ECharts 的微信小程序版本。开发者可以通过熟悉的 ECharts 配置方式,快速开发图表,满足各种可视化需求。

https://echarts.apache.org

GitHub地址 ecomfe/echarts-for-weixin
在这里插入图片描述
F2
专注于移动端的可视化解决方案,兼容 H5/小程序/Weex 等多端环境
官网https://antv.vision/zh

在这里插入图片描述

地址https://github.com/antvis/g2
在这里插入图片描述

在项目中使用echart

根据文档说明,在echart官网定制,导出插件引入项目中
在这里插入图片描述

index.config.js
export default {
    navigationBarTitleText: '柱状图',
    usingComponents: {
        // 定义需要引入的第三方组件
        // 1. key 值指定第三方组件名字,以小写开头
        // 2. value 值指定第三方组件 js 文件的相对路径
        'ec-canvas': '../../components/ec-canvas/ec-canvas',
    },
};

import React, { useState } from 'react';

import * as echarts from '@/components/ec-canvas/echarts.js';
import { View } from '@tarojs/components';
import './index.scss';

let chart = null;
function initChart(canvas, width, height, dpr) {
    chart = echarts.init(canvas, null, {
        width,
        height,
        devicePixelRatio: dpr, // new
    });
    canvas.setChart(chart);

    const option = {
        xAxis: {
            type: 'category',
            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
        },
        yAxis: {
            type: 'value',
        },
        series: [
            {
                data: [150, 230, 224, 218, 135, 147, 260],
                type: 'line',
            },
        ],
    };

    chart.setOption(option);
    return chart;
}

export default () => {
    const [ec] = useState({ onInit: initChart });
    return (
        <View className="bar">
            <ec-canvas id="mychart-dom-area" canvas-id="mychart-line" ec={ec} />
        </View>
    );
};

F2移动可视化方案

https://f2.antv.vision/zh/docs/tutorial/getting-started
yarn add @antv/f2 --save
在这里插入图片描述

import React, { useEffect } from "react";
import { View, Text, Canvas } from "@tarojs/components";
import { AtButton } from "taro-ui";
import F2 from "@antv/f2";
import "./index.scss";

export default () => {
  const initChart = () => {
    // F2 对数据源格式的要求,仅仅是 JSON 数组,数组的每个元素是一个标准 JSON 对象。
    const data = [
      { genre: "Sports", sold: 275 },
      { genre: "Strategy", sold: 115 },
      { genre: "Action", sold: 120 },
      { genre: "Shooter", sold: 350 },
      { genre: "Other", sold: 150 },
    ];

    // Step 1: 创建 Chart 对象
    const chart = new F2.Chart({
      id: "myChart",
      pixelRatio: window.devicePixelRatio, // 指定分辨率
    });

    // Step 2: 载入数据源
    chart.source(data);

    // Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴
    chart.interval().position("genre*sold").color("genre");

    // Step 4: 渲染图表
    chart.render();
  };
  useEffect(() => {
    initChart();
  }, []);
  return (
    <>
      <View>
        <Text>Hello world!</Text>
        <Canvas style="width: 300px; height: 200px;" canvasId="myChart" />
      </View>
    </>
  );
};


import Taro from "@tarojs/taro";
import React, { useEffect } from "react";
import { View, Text, Canvas } from "@tarojs/components";
import F2 from "@antv/f2";
import "./index.scss";

export default () => {
  const initChart = (config) => {
    // F2 对数据源格式的要求,仅仅是 JSON 数组,数组的每个元素是一个标准 JSON 对象。
    const data = [
      { genre: "Sports", sold: 275 },
      { genre: "Strategy", sold: 115 },
      { genre: "Action", sold: 120 },
      { genre: "Shooter", sold: 350 },
      { genre: "Other", sold: 150 },
    ];

    // Step 1: 创建 Chart 对象
    // const chart = new F2.Chart({
    //   id: "myChart",
    //   pixelRatio: window.devicePixelRatio, // 指定分辨率
    // });
    console.log("Object.assign", Object.assign(config));
    const chart = new F2.Chart(
      Object.assign(config, {
        appendPadding: [10, 15, 10, 15],
        // 预留展示tooltip高度
        padding: [40, "auto", "auto"],
      })
    );

    // Step 2: 载入数据源
    chart.source(data);

    // Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴
    chart.interval().position("genre*sold").color("genre");

    // Step 4: 渲染图表
    chart.render();
  };
  const onWxCanvas = () => {
    const query = Taro.createSelectorQuery();
    query
      .select("#" + "chart-id")
      .fields({
        node: true,
        size: true,
      })
      .exec((res) => {
        console.log("res", res);
        const { node, width, height } = res[0];
        const context = node.getContext("2d");
        const pixelRatio = Taro.getSystemInfoSync().pixelRatio;
        // 高清设置
        node.width = width * pixelRatio;
        node.height = height * pixelRatio;
        //  chart全局设置
        const appendPadding = 5;
        const config = { context, width, height, pixelRatio, appendPadding };
        console.log("config", config);
        const chart = initChart(config);
        console.log("chart", chart);
        if (chart) {
          //   this.chart = chart;
          //   this.canvasEl = chart.get("el");
        }
      });
  };

  useEffect(() => {
    setTimeout(() => {
      onWxCanvas();
      console.log("999");
    }, 500);
  }, []);
  return (
    <>
      <View>
        <Text>Hello world!</Text>
        <Canvas
          style="width: 300px; height: 200px;"
          //   canvasId="chart-id"
          type="2d"
          id="chart-id"
        />
      </View>
    </>
  );
};

初步选用f2

封装Canvas组件

F2Canvas.js
在这里插入图片描述

封装组件

F2Canvas
src/components/F2Canvas/index.jsx

import React, { useEffect, useState } from "react";
import Taro from "@tarojs/taro";
import { Canvas } from "@tarojs/components";

function wrapEvent(e) {
  if (!e) return;
  if (!e.preventDefault) {
    e.preventDefault = function () {};
  }
  return e;
}

export default ({ className, style, id, onInit }) => {
  const [currentChart, setCurrentChart] = useState("");
  const [canvasEl, seCanvasEl] = useState("");
  console.log("onInit", onInit);

  const onWxCanvas = () => {
    const query = Taro.createSelectorQuery();
    query
      .select("#" + id)
      .fields({
        node: true,
        size: true,
      })
      .exec((res) => {
        console.log("res", res);
        const { node, width, height } = res[0];
        const context = node.getContext("2d");
        const pixelRatio = Taro.getSystemInfoSync().pixelRatio;
        // 高清设置
        node.width = width * pixelRatio;
        node.height = height * pixelRatio;
        //  chart全局设置
        const appendPadding = 5;
        const config = { context, width, height, pixelRatio, appendPadding };
        console.log("config", config);
        const chart = onInit(config);

        if (chart) {
          const CH = chart;
          const El = chart.get("el");
          console.log("CH", CH, El);
          seCanvasEl();
        }
      });
  };
  useEffect(() => {
    setTimeout(() => {
      onWxCanvas();
      console.log("999");
    }, 500);
  }, []);

  const touchStart = (e) => {
    if (canvasEl) {
      canvasEl.dispatchEvent("touchstart", wrapEvent(e));
    }
  };

  const touchMove = (e) => {
    e.stopPropagation();
    e.preventDefault();
    if (canvasEl) {
      canvasEl.dispatchEvent("touchmove", wrapEvent(e));
    }
  };

  const touchEnd = (e) => {
    if (canvasEl) {
      canvasEl.dispatchEvent("touchend", wrapEvent(e));
    }
  };

  return (
    <Canvas
      className={className}
      style={style}
      type="2d"
      id={id}
      onTouchStart={touchStart}
      onTouchMove={touchMove}
      onTouchEnd={touchEnd}
    />
  );
};

src/components/LineChart/index.jsx

import React, { Component } from "react";
import PropTypes from "prop-types";
import F2Canvas from "../F2Canvas";
import F2 from "@antv/f2";

export default ({ data, id, xField, yField, color }) => {
  const initChart = (config) => {
    const chart = new F2.Chart(
      Object.assign(config, {
        appendPadding: [10, 15, 10, 15],
        // 预留展示tooltip高度
        padding: [40, "auto", "auto"],
      })
    );

    // Step 2: 载入数据源
    chart.source(data, {
      value: {
        tickCount: 5,
        min: 0,
      },
      date: {
        type: "timeCat",
        range: [0, 1],
        tickCount: 3,
      },
    });

    // Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴
    chart
      .line()
      .position(`${xField}*${yField}`)
      .color(color || "#2FC25B");

    // Step 4: 渲染图表
    chart.render();
  };
  return (
    <F2Canvas
      id={id}
      style={{ width: "100%", height: "200px" }}
      onInit={initChart}
    ></F2Canvas>
  );
};

src/components/HistogramChart

import React, { Component } from "react";
import PropTypes from "prop-types";
import F2Canvas from "../F2Canvas";
import F2 from "@antv/f2";

export default ({ data, id, xField, yField, color }) => {
  const initChart = (config) => {
    const chart = new F2.Chart(
      Object.assign(config, {
        appendPadding: [10, 15, 10, 15],
        // 预留展示tooltip高度
        padding: [40, "auto", "auto"],
      })
    );

    // Step 2: 载入数据源
    chart.source(data);

    // Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴
    chart
      .interval()
      .position(`${xField}*${yField}`)
      .color(color || "#2FC25B");

    // Step 4: 渲染图表
    chart.render();
  };
  return (
    <F2Canvas
      id={id}
      style={{ width: "100%", height: "200px" }}
      onInit={initChart}
    ></F2Canvas>
  );
};

使用组件

import { useEffect } from "react";
import Taro from "@tarojs/taro";
import { View, Text, Button } from "@tarojs/components";
import HistogramChart from "../../components/HistogramChart";
import LineChart from "../../components/LineChart";
import "./index.scss";

export default () => {
  const data = [
    { genre: "Sports", sold: 275 },
    { genre: "Strategy", sold: 115 },
    { genre: "Action", sold: 120 },
    { genre: "Shooter", sold: 350 },
    { genre: "Other", sold: 150 },
  ];
  const dataLine = [
    {
      date: "2017-06-05",
      value: 116,
    },
    {
      date: "2017-06-06",
      value: 129,
    },
    {
      date: "2017-06-07",
      value: 135,
    },
    {
      date: "2017-06-08",
      value: 86,
    },
    {
      date: "2017-06-09",
      value: 73,
    },
    {
      date: "2017-06-10",
      value: 85,
    },
    {
      date: "2017-06-11",
      value: 73,
    },
    {
      date: "2017-06-12",
      value: 68,
    },
    {
      date: "2017-06-13",
      value: 92,
    },
    {
      date: "2017-06-14",
      value: 130,
    },
    {
      date: "2017-06-15",
      value: 245,
    },
    {
      date: "2017-06-16",
      value: 139,
    },
    {
      date: "2017-06-17",
      value: 115,
    },
    {
      date: "2017-06-18",
      value: 111,
    },
  ];
  const config01 = {
    id: "confi01",
    data,
    xField: "genre",
    yField: "sold",
    color: "#2FC25B",
  };
  const config02 = {
    id: "confi02",
    data: dataLine,
    xField: "date",
    yField: "value",
    color: "#2FC25B",
  };
  return (
    <View>
      <Text>1</Text>
      <HistogramChart {...config01}></HistogramChart>
      <Text>2</Text>
      <LineChart {...config02}></LineChart>
    </View>
  );
};


在这里插入图片描述

优化

F2Canvas组件通过chartList依赖

  useEffect(() => {
    setTimeout(() => {
      onWxCanvas();
    }, 500);
  }, [chartList]);
import React, { useState, useEffect } from "react";
import { useDidShow } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
import F2 from "@antv/f2";
import F2Canvas from "../../components/F2Canvas";
import {
  AtNoticebar,
  AtTabs,
  AtTabsPane,
  AtCard,
  AtActivityIndicator,
} from "taro-ui";
import { useSelector, useDispatch } from "react-redux";
import LineChart from "../../components/LineChart";
import "./index.scss";

export default () => {
  const dispatch = useDispatch();
  const { list, chartList, noticeType } = useSelector((state) => state.message);
  const loading = useSelector((store) => store.loading);
  const [current, setCurrent] = useState(0);
  const [lineList, setLineList] = useState([]);
  const tabList = [
    { title: "公告", noticeType: 1 },
    { title: "通知", noticeType: 2 },
    { title: "统计", noticeType: 3 },
  ];
  useDidShow(() => {
    dispatch({
      type: "message/questMassage",
    });
  });

  console.log("list", list);
  const handleClick = (val) => {
    console.log(tabList[val]);
    setCurrent(val);
    dispatch({
      type: "message/overrideStateProps",
      payload: {
        noticeType: tabList[val].noticeType,
      },
    });
    dispatch({
      type: "message/questMassage",
    });
  };
  const notice = `${list[0]?.noticeTitle || ""}  ${list[0]?.createBy || ""}  ${
    list[0]?.createTime || ""
  }`;
  const config0R = {
    id: "confi02",
    data: list,
    xField: "date",
    yField: "value",
    color: "#2FC25B",
  };
  console.log("list", list);
  const initChart = (config) => {
    const chart = new F2.Chart(
      Object.assign(config, {
        appendPadding: [10, 15, 10, 15],
        // 预留展示tooltip高度
        padding: [40, "auto", "auto"],
      })
    );

    // Step 2: 载入数据源
    chart.source(chartList, {
      value: {
        tickCount: 5,
        min: 0,
      },
      date: {
        type: "timeCat",
        range: [0, 1],
        tickCount: 3,
      },
    });

    // Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴
    chart
      .line()
      .position(`${config0R.xField}*${config0R.yField}`)
      .color(config0R.color || "#2FC25B");

    // Step 4: 渲染图表
    chart.render();
    return chart;
  };
  console.log("chartList", chartList);
  return (
    <>
      <AtActivityIndicator
        isOpened={loading.effects["message/questMassage"]}
        mode="center"
        size={32}
        content="加载中..."
      ></AtActivityIndicator>
      <View className="index">
        <AtNoticebar marquee icon="volume-plus">
          {notice}
        </AtNoticebar>
        <AtTabs current={current} tabList={tabList} onClick={handleClick}>
          <AtTabsPane current={current} index={0}>
            {list.length > 0
              ? list?.map((item) => {
                  return (
                    <AtCard
                      note={item.createTime}
                      extra={`${item.createBy}`}
                      title={item.noticeTitle}
                      style={{ marginTop: "2px" }}
                      key={item.mesId}
                    >
                      {item.content}
                    </AtCard>
                  );
                })
              : null}
          </AtTabsPane>
          <AtTabsPane current={current} index={1}>
            {list.length > 0
              ? list?.map((item) => {
                  return (
                    <AtCard
                      note={item.createTime}
                      extra={`${item.createBy} `}
                      title={item.noticeTitle}
                      style={{ marginTop: "2px" }}
                      key={item.mesId}
                    >
                      {item.content}
                    </AtCard>
                  );
                })
              : null}
          </AtTabsPane>
          <AtTabsPane current={current} index={2}>
            <F2Canvas
              id="confi02"
              style={{ width: "100%", height: "200px" }}
              onInit={initChart}
              chartList={chartList}
            ></F2Canvas>
          </AtTabsPane>
        </AtTabs>
      </View>
    </>
  );
};

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值