技术栈
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>
</>
);
};