|
|
@@ -1,5 +1,5 @@
|
|
|
-import React, { useEffect, useRef, useMemo } from 'react';
|
|
|
-import Taro, { useReady } from '@tarojs/taro';
|
|
|
+import React, { useState, useRef, useLayoutEffect } from 'react';
|
|
|
+import Taro from '@tarojs/taro';
|
|
|
import { Canvas } from '@tarojs/components';
|
|
|
import uChartsClass from '../lib/u-charts-original.js';
|
|
|
import type { ChartsConfig, TouchEvent } from '../lib/u-charts-original';
|
|
|
@@ -12,12 +12,10 @@ import type { ExtendedCanvasContext } from '../types';
|
|
|
export interface BaseChartOriginal2DProps {
|
|
|
/** Canvas 元素的 ID,必须唯一 */
|
|
|
canvasId: string;
|
|
|
- /** 图表宽度(像素),默认为屏幕宽度 */
|
|
|
+ /** 图表宽度(像素),默认为 750 */
|
|
|
width?: number;
|
|
|
- /** 图表高度(像素),默认根据宽高比计算 */
|
|
|
+ /** 图表高度(像素),默认为 500 */
|
|
|
height?: number;
|
|
|
- /** 设备像素比,默认根据环境自动设置 */
|
|
|
- pixelRatio?: number;
|
|
|
/** 图表类型 */
|
|
|
type: ChartsConfig['type'];
|
|
|
/** X 轴分类数据 */
|
|
|
@@ -43,9 +41,8 @@ export interface BaseChartOriginal2DProps {
|
|
|
export const BaseChartOriginal2D: React.FC<BaseChartOriginal2DProps> = (props) => {
|
|
|
const {
|
|
|
canvasId,
|
|
|
- width,
|
|
|
- height,
|
|
|
- pixelRatio,
|
|
|
+ width = 750,
|
|
|
+ height = 500,
|
|
|
type,
|
|
|
categories = [],
|
|
|
series = [],
|
|
|
@@ -55,37 +52,29 @@ export const BaseChartOriginal2D: React.FC<BaseChartOriginal2DProps> = (props) =
|
|
|
onTouchEnd,
|
|
|
} = props;
|
|
|
|
|
|
+ const [cWidth, setCWidth] = useState(750);
|
|
|
+ const [cHeight, setCHeight] = useState(500);
|
|
|
+
|
|
|
const chartRef = useRef<any>(null);
|
|
|
|
|
|
/**
|
|
|
- * 计算响应式尺寸和像素比
|
|
|
+ * 初始化图表实例
|
|
|
+ * 使用 Canvas 2D API + 原始 u-charts.js
|
|
|
+ * 参考 ColumnChartFCExample 的实现方式
|
|
|
*/
|
|
|
- const { cWidth, cHeight, actualPixelRatio } = useMemo(() => {
|
|
|
+ useLayoutEffect(() => {
|
|
|
+ console.debug('[BaseChartOriginal2D] useLayoutEffect 开始', { canvasId, width, height });
|
|
|
+
|
|
|
+ // 计算响应式尺寸
|
|
|
const sysInfo = Taro.getSystemInfoSync();
|
|
|
- const pr = pixelRatio ?? sysInfo.pixelRatio;
|
|
|
- // width 和 height 是逻辑像素(CSS 像素)
|
|
|
- const cw = width ?? (750 / 750 * sysInfo.windowWidth);
|
|
|
- const ch = height ?? (500 / 750 * cw);
|
|
|
- return { cWidth: cw, cHeight: ch, actualPixelRatio: pr };
|
|
|
- }, [width, height, pixelRatio]);
|
|
|
+ // 这里的第一个 750 对应 css .charts 的 width
|
|
|
+ const cw = width / 750 * sysInfo.windowWidth;
|
|
|
+ // 这里的 500 对应 css .charts 的 height
|
|
|
+ const ch = height / 750 * sysInfo.windowWidth;
|
|
|
|
|
|
- /**
|
|
|
- * Canvas props - Canvas 2D API
|
|
|
- * - width/height 属性:实际像素尺寸(逻辑像素 * pixelRatio)
|
|
|
- * - style.width/style.height:CSS 显示尺寸(逻辑像素)
|
|
|
- */
|
|
|
- const canvasProps = useMemo(() => ({
|
|
|
- width: String(cWidth * actualPixelRatio),
|
|
|
- height: String(cHeight * actualPixelRatio),
|
|
|
- style: { width: `${cWidth}px`, height: `${cHeight}px` }
|
|
|
- }), [cWidth, cHeight, actualPixelRatio]);
|
|
|
+ setCWidth(cw);
|
|
|
+ setCHeight(ch);
|
|
|
|
|
|
- /**
|
|
|
- * 初始化图表实例
|
|
|
- * 使用 Canvas 2D API + 原始 u-charts.js
|
|
|
- * 只在数据准备好后才初始化(参考官方示例)
|
|
|
- */
|
|
|
- useReady(() => {
|
|
|
// 确保数据已准备好:categories 和 series 都有数据
|
|
|
const isDataReady = categories.length > 0 && series.length > 0;
|
|
|
|
|
|
@@ -105,22 +94,20 @@ export const BaseChartOriginal2D: React.FC<BaseChartOriginal2DProps> = (props) =
|
|
|
const canvas = res[0].node;
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
|
- // 设置 canvas 的实际像素尺寸
|
|
|
- canvas.width = res[0].width * actualPixelRatio;
|
|
|
- canvas.height = res[0].height * actualPixelRatio;
|
|
|
+ console.debug('[BaseChartOriginal2D] canvas.width', canvas.width);
|
|
|
+ console.debug('[BaseChartOriginal2D] canvas.height', canvas.height);
|
|
|
|
|
|
// 将 Taro CanvasContext 转换为 uCharts 需要的 CanvasContext
|
|
|
const extendedCtx = ctx as ExtendedCanvasContext;
|
|
|
|
|
|
- // Canvas 2D: 传入 uCharts 的 width/height 需要乘以 pixelRatio
|
|
|
+ // Canvas 2D: 使用 canvas 的实际 width/height
|
|
|
const chartConfig: ChartsConfig = {
|
|
|
type,
|
|
|
context: extendedCtx,
|
|
|
categories,
|
|
|
series,
|
|
|
- width: cWidth * actualPixelRatio,
|
|
|
- height: cHeight * actualPixelRatio,
|
|
|
- pixelRatio: actualPixelRatio,
|
|
|
+ width: canvas.width,
|
|
|
+ height: canvas.height,
|
|
|
animation: true,
|
|
|
background: '#FFFFFF',
|
|
|
color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
|
|
|
@@ -146,7 +133,7 @@ export const BaseChartOriginal2D: React.FC<BaseChartOriginal2DProps> = (props) =
|
|
|
|
|
|
chartRef.current = new uChartsClass(chartConfig);
|
|
|
console.log('[BaseChartOriginal2D] 图表初始化完成:', canvasId, {
|
|
|
- cWidth, cHeight, actualPixelRatio,
|
|
|
+ cWidth, cHeight,
|
|
|
canvasWidth: canvas.width,
|
|
|
canvasHeight: canvas.height,
|
|
|
categoriesLength: categories.length,
|
|
|
@@ -156,61 +143,7 @@ export const BaseChartOriginal2D: React.FC<BaseChartOriginal2DProps> = (props) =
|
|
|
console.error('[BaseChartOriginal2D] 未获取到 canvas node:', canvasId);
|
|
|
}
|
|
|
});
|
|
|
- });
|
|
|
-
|
|
|
- // 当数据变化时重新绘制
|
|
|
- useEffect(() => {
|
|
|
- if (!chartRef.current) return;
|
|
|
-
|
|
|
- const isDataReady = categories.length > 0 && series.length > 0;
|
|
|
- if (!isDataReady) return;
|
|
|
-
|
|
|
- // 数据变化时重新绘制
|
|
|
- const query = Taro.createSelectorQuery();
|
|
|
- query.select('#' + canvasId).fields({ node: true, size: true }).exec((res) => {
|
|
|
- if (res[0]) {
|
|
|
- const canvas = res[0].node;
|
|
|
- const ctx = canvas.getContext('2d');
|
|
|
-
|
|
|
- // 清空画布
|
|
|
- ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
-
|
|
|
- // 重新创建图表实例
|
|
|
- const chartConfig: ChartsConfig = {
|
|
|
- type,
|
|
|
- context: ctx as ExtendedCanvasContext,
|
|
|
- categories,
|
|
|
- series,
|
|
|
- width: cWidth * actualPixelRatio,
|
|
|
- height: cHeight * actualPixelRatio,
|
|
|
- pixelRatio: actualPixelRatio,
|
|
|
- animation: true,
|
|
|
- background: '#FFFFFF',
|
|
|
- color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
|
|
|
- padding: [15, 15, 0, 5],
|
|
|
- enableScroll: false,
|
|
|
- legend: {},
|
|
|
- xAxis: {
|
|
|
- disableGrid: true
|
|
|
- },
|
|
|
- yAxis: {
|
|
|
- data: [{ min: 0 }]
|
|
|
- },
|
|
|
- extra: {
|
|
|
- column: {
|
|
|
- type: 'group',
|
|
|
- width: 30,
|
|
|
- activeBgColor: '#000000',
|
|
|
- activeBgOpacity: 0.08
|
|
|
- }
|
|
|
- },
|
|
|
- ...config,
|
|
|
- };
|
|
|
-
|
|
|
- chartRef.current = new uChartsClass(chartConfig);
|
|
|
- }
|
|
|
- });
|
|
|
- }, [categories, series, config]);
|
|
|
+ }, [canvasId, width, height, categories, series, config]);
|
|
|
|
|
|
/**
|
|
|
* 触摸事件处理
|
|
|
@@ -238,9 +171,11 @@ export const BaseChartOriginal2D: React.FC<BaseChartOriginal2DProps> = (props) =
|
|
|
onTouchEnd?.(e as TouchEvent);
|
|
|
};
|
|
|
|
|
|
+ const canvasProps = { style: { width: cWidth, height: cHeight } };
|
|
|
+
|
|
|
return (
|
|
|
<Canvas
|
|
|
- canvasId={canvasId}
|
|
|
+ canvas-id={canvasId}
|
|
|
id={canvasId}
|
|
|
{...canvasProps}
|
|
|
onTouchStart={handleTouchStart}
|