当前位置: 首页 > news >正文

ReactVChart矩形转化漏斗图配置示例

文章目录

    • React VChart使用
      • 矩形转化漏斗图示例
      • 实际项目使用

React VChart使用

  • react-vchart 文档
    • @visactor/react-vchart
      • 该组件主要对 VChart 的图表部件做 React 组件化的封装,相关的配置项均与 VChart 一致
    • react-vchart图表

矩形转化漏斗图示例

  • 漏斗图,形如“漏斗”,用于单流程分析,在开始和结束之间由 N 个流程环节组成,通常这 N 个流程环节,有逻辑上的顺序关系。
const root = document.getElementById(CONTAINER_ID);
const { VChart, FunnelChart, Pie, Legend } = ReactVChart;
const { useState, useRef, useEffect, useCallback } = React;const data = [{id: "funnel",values: [{value: 100,name: "Resume Screening",percent: 1,},{value: 80,name: "Resume Evaluation",percent: 0.8,},{value: 50,name: "Evaluation Passed",percent: 0.5,},{value: 30,name: "Interview",percent: 0.3,},{value: 10,name: "Final Pass",percent: 0.1,},],},
];const Card = () => {const chartRef = useRef(null);useEffect(() => {window["vchart"] = null;}, []);return (<FunnelChartref={chartRef}style={{ width: 390, height: 286 }}spec={{type: "funnel",categoryField: "name",valueField: "value",data,maxSize: "60%",minSize: "27%",// 转化率梯形打开isTransform: true,// 漏斗层与转化层的图形高度比例heightRatio: 0.46,// 转化率transformLabel: {visible: true,style: {fill: "black",},formatMethod: (text, datum) => {console.log("转化率", text, datum);return `${(datum.__VCHART_FUNNEL_VALUE_RATIO * 100).toFixed(2)}%`;},},// 矩形shape: "rect",// 矩形值label: {visible: true,// 数据标签内容格式化函数formatMethod: (text, datum) => {return {type: "rich",text: [{text: `${datum.name}`,},{text: `\n${datum.value}`,fontSize: 14,fontWeight: "bold",textAlign: "center",},],};},style: {fontSize: 12,lineHeight: 18,limit: Infinity,// text: datum => [`${datum.name}`, `${datum.value}`]},},color: {type: "ordinal",// 矩形块颜色range: ["#2778E2", "#005FC5", "#0048AA", "#00328E"],},padding: {left: "-25%",top: 0,},funnel: {style: {cornerRadius: 2,stroke: "white",lineWidth: 2,},state: {hover: {stroke: "#4e83fd",lineWidth: 1,},},},transform: {style: {stroke: "white",lineWidth: 2,},state: {hover: {stroke: "#4e83fd",lineWidth: 1,},},},tooltip: {visible: true,mark: {// tooltip 内容的回调,在最终显示 tooltip 前调用,可以在这个回调中修改 tooltip 内容的文本和样式,以及对 tooltip 内容行进行增、删、改、重新排序。updateContent: (content) => console.log("content", content),},},// 图例legends: {visible: true,orient: "bottom",},extensionMark: [{// 多边形 箭头线type: "polygon",dataId: "funnel",style: {points: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const curIndex = data.findIndex((d) => d.name === datum.name);if (curIndex !== 0) return;if (curIndex === data.length - 1) {return;}const nextDatum = data[curIndex + 2];const firstDatum = data[0];const points = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(datum);const nextPoints = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(nextDatum);const firstPoints = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(firstDatum);const tr = points[1];const tb = points[2];const next_tr = nextPoints[1];const first_tr = firstPoints[1];const result = [{ x: tr.x + 5, y: (tr.y + tb.y) / 2 },{ x: first_tr.x + 20, y: (tr.y + tb.y) / 2 },{x: first_tr.x + 20,y: (tr.y + tb.y) / 2 + (next_tr.y - tr.y) - 10,},{x: next_tr.x + 5,y: (tr.y + tb.y) / 2 + (next_tr.y - tr.y) - 10,},];return result;},cornerRadius: 5,stroke: "rgb(200,200,200)",strokeOpacity: 0.5,lineWidth: 2,closePath: false,},},{// 点图形 箭头type: "symbol",dataId: "funnel",style: {visible: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const curIndex = data.findIndex((d) => d.name === datum.name);console.log("curIndex", datum, ctx, params, dataView, curIndex);if (curIndex !== 1) return false;if (curIndex === data.length - 1) {return false;}return true;},x: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const curIndex = data.findIndex((d) => d.name === datum.name);if (curIndex === data.length - 1) {return;}const nextDatum = data[curIndex + 1];const nextPoints = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(nextDatum);const next_tr = nextPoints[1];return next_tr.x + 5;},y: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const curIndex = data.findIndex((d) => d.name === datum.name);if (curIndex === data.length - 1) {return;}const nextDatum = data[curIndex + 1];const points = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(datum);const nextPoints = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(nextDatum);const tr = points[1];const tb = points[2];const next_tr = nextPoints[1];return (tr.y + tb.y) / 2 + (next_tr.y - tr.y) - 10;},size: 8,scaleX: 0.8,symbolType: "triangleLeft",cornerRadius: 2,fill: "rgb(200,200,200)",},},{// 文本type: "text",dataId: "funnel",style: {// 返回数组:换行展示文案text: (datum) => [datum.name, `   ${datum.percent * 100}%`],textAlign: "left",visible: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const curIndex = data.findIndex((d) => d.name === datum.name);if (curIndex !== 2) return false;if (curIndex === data.length - 1) {return false;}return true;},x: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const firstDatum = data[0];const firstPoints = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(firstDatum);const tr = firstPoints[1];return tr.x + 20 + 10;},y: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) return;const curIndex = data.findIndex((d) => d.name === datum.name);if (curIndex === data.length - 1) {return;}const points = ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(datum);const tr = points[1];return (tr.y * 2) / 3;},fontSize: 12,fill: "black",},},],}}/>);
};ReactDom.createRoot(root).render(<Card style={{ width: 390, height: 350 }} />);// release react instance, do not copy
window.customRelease = () => {ReactDom.unmountComponentAtNode(root);
};

实际项目使用

import { useRef } from "react";
import { FunnelChart } from "@visactor/react-vchart";const funnelChartData = [{id: "funnel",values: [{value: 100,name: "Resume Screening",percent: 1,},{value: 80,name: "Resume Evaluation",percent: 0.8,},{value: 50,name: "Evaluation Passed",percent: 0.5,},{value: 30,name: "Interview",percent: 0.3,},{value: 10,name: "Final Pass",percent: 0.1,},],},
];/*** 获取图形数据所有点位的x、y坐标*/
const getPoints = (ctx, datum) =>ctx.vchart.getChart().getSeriesInIndex(0)[0].getPoints(datum);/*** 通用判断*/
const checkDataExists = (dataView, datum) => {const config = {isExist: true,curIndex: -1,data: dataView.latestData ?? null,};if (!config?.data) {config.isExist = false;return config;}config.curIndex = config.data.findIndex((d) => d.name === datum.name);if (config.curIndex === config.data.length - 1) {config.isExist = false;return config;}return config;
};/*** 绘制任意内容的自定义接口*/
const extensionMark = [{// 文本type: "text",dataId: "funnel",style: {// 返回数组:换行展示文案text: (datum) => [datum.name, `  ${datum.percent}`],textAlign: "left",visible: (datum, ctx, params, dataView) => {const { isExist, curIndex, data } = checkDataExists(dataView, datum);if (!isExist) {return false;}// 自定义多边形右侧:展示第三个梯形的文案和百分比if (curIndex !== 2) {return false;}return true;},x: (datum, ctx, params, dataView) => {const data = dataView.latestData;if (!data) {return;}const firstDatum = data[0];const firstPoints = getPoints(ctx, firstDatum);const tr = firstPoints[1];return tr.x + 20 + 10;},y: (datum, ctx, params, dataView) => {const { isExist, data } = checkDataExists(dataView, datum);if (!isExist) {return;}const points = getPoints(ctx, datum);const tr = points[1];return (tr.y * 2) / 3;},fontSize: 12,fill: "black",},},{// 多边形 箭头线type: "polygon",dataId: "funnel",style: {points: (datum, ctx, params, dataView) => {const { isExist, curIndex, data } = checkDataExists(dataView, datum);if (!isExist) {return;}// 让箭头线只在第一个梯形右侧开始显示if (curIndex !== 0) {return;}const nextDatum = data[curIndex + 2];const firstDatum = data[0];const points = getPoints(ctx, datum);const nextPoints = getPoints(ctx, nextDatum);const firstPoints = getPoints(ctx, firstDatum);const tr = points[1];const tb = points[2];const next_tr = nextPoints[1];const first_tr = firstPoints[1];// 箭头线四个点位const result = [{ x: tr.x + 5, y: (tr.y + tb.y) / 2 },{ x: first_tr.x + 20, y: (tr.y + tb.y) / 2 },{x: first_tr.x + 20,y: (tr.y + tb.y) / 2 + (next_tr.y - tr.y) - 10,},{x: next_tr.x + 5,y: (tr.y + tb.y) / 2 + (next_tr.y - tr.y) - 10,},];return result;},cornerRadius: 5,stroke: "rgb(200,200,200)",strokeOpacity: 0.5,lineWidth: 2,closePath: false,},},{// 点图形 箭头type: "symbol",dataId: "funnel",style: {visible: (datum, ctx, params, dataView) => {const { isExist, curIndex } = checkDataExists(dataView, datum);if (!isExist) {return;}// 要让箭头指向第三个梯形,需要判断当前是否等于第二个梯形if (curIndex !== 1) {return false;}return true;},x: (datum, ctx, params, dataView) => {const { isExist, curIndex, data } = checkDataExists(dataView, datum);if (!isExist) {return;}const nextDatum = data[curIndex + 1];const nextPoints = getPoints(ctx, nextDatum);const next_tr = nextPoints[1];return next_tr.x + 5;},y: (datum, ctx, params, dataView) => {const { isExist, curIndex, data } = checkDataExists(dataView, datum);if (!isExist) {return;}const nextDatum = data[curIndex + 1];const points = getPoints(ctx, datum);const nextPoints = getPoints(ctx, nextDatum);const tr = points[1];const tb = points[2];const next_tr = nextPoints[1];return (tr.y + tb.y) / 2 + (next_tr.y - tr.y) - 10;},size: 8,scaleX: 0.8,symbolType: "triangleLeft",cornerRadius: 2,fill: "rgb(200,200,200)",},},
];const getSpec = (data) => {return {type: "funnel",categoryField: "name",valueField: "value",data,maxSize: "60%",minSize: "30%",padding: {top: 0,left: "-10%",},// 转化率梯形打开isTransform: true,// 转化率transformLabel: {visible: true,style: {fill: "black",},formatMethod: (text, datum) =>`${(datum?.__VCHART_FUNNEL_VALUE_RATIO * 100).toFixed(2)}%`,},// 矩形shape: "rect",// 矩形值label: {visible: true,// 数据标签内容格式化函数formatMethod: (text, datum) => ({type: "rich",text: [{text: `${datum.name}`,},{text: `\n${datum.value}`,fontSize: 14,fontWeight: "bold",textAlign: "center",},],}),style: {fontSize: 12,lineHeight: 18,limit: Infinity,// text: datum => [`${datum.name}`, `${datum.value}`]},},color: {type: "ordinal",// 矩形块颜色range: ["#2778E2", "#005FC5", "#0048AA", "#00328E"],},funnel: {style: {cornerRadius: 2,stroke: "white",lineWidth: 2,},state: {hover: {stroke: "#4e83fd",lineWidth: 1,},},},transform: {style: {stroke: "white",lineWidth: 2,},state: {hover: {stroke: "#4e83fd",lineWidth: 1,},},},// 图例legends: {visible: true,orient: "bottom",},// 在图表系列上补充绘制任意内容的自定义接口extensionMark,};
};const FunnelChart = (props) => {const { data } = props;const chartRef = useRef(null);return (<FunnelChartref={chartRef}style={{ width: 400, height: 300 }}spec={getSpec(funnelChartData)}/>);
};export default FunnelChart;

http://www.mrgr.cn/news/9816.html

相关文章:

  • VIM的简单用法
  • 基于单片机的数字时钟设计
  • 使用FFmpeg更改视频播放速度的方法
  • json传参数和表单传参
  • 图片工具箱:一键批量加水印,守护创意,提升效率!
  • uniapp实现苹果账号登录
  • 【JavaEE】SpringBoot 统一功能处理:拦截器、统一数据返回与异常处理的综合应用与源码解析
  • sourcetree 拉取代码时报错
  • 做项目过程中问题小汇总2 | mongodb express
  • tensorflow新建op (cpp)
  • 等保测评中的安全测试方法
  • 基于imx6ull平台opencv的图像采集和显示屏LCD显示功能(不带Qt界面)
  • 科技温柔拥抱梦乡!康姿百德柔压磁性枕舒适与科技的甜蜜邂逅
  • 搭建自己的金融数据源和量化分析平台(七):定时更新上市公司所属行业门类及大类
  • React学习笔记(二)——react基础
  • 【D-DCVRP】求解DCVRP改进贪婪算法(三)
  • Maven的一些相关知识【重修】《包括私服搭建!》
  • 【QA-MISRA】在客户端如何修改当前用户的密码
  • Android笔试面试题AI答之Kotlin常见考点总结
  • linux——驱动——GPIO子系统