我正在使用 AmChart 5 的 XY 图表来显示一些数据。单击按钮时,用户可以在 MUI 对话框中看到该图表并对其进行一些操作。但是,当我想在 MUI 对话框中显示具有唯一 ID 的图表时,它会给出“无法找到具有 id 的 HTML 元素”错误。当我将相同的图表放置在对话框外部的同一组件中时,一切都完美运行。
我定义图表的部分:
useLayoutEffect(() => {
let root = am5.Root.new(id);
root.utc = true;
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([am5themes_Animated.new(root)]);
// Create chart
// https://www.amcharts.com/docs/v5/charts/xy-chart/
let chart = root.container.children.push(
am5xy.XYChart.new(root, {
panX: false,
panY: false,
pinchZoomX: false,
pinchZoomY: false,
})
);
// Add cursor
// https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/
let cursor = chart.set("cursor", am5xy.XYCursor.new(root, {}));
cursor.lineY.set("visible", false);
cursor.lineX.set("visible", false);
// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
let xAxis = chart.xAxes.push(
am5xy.DateAxis.new(root, {
baseInterval: {
timeUnit: "month",
count: 3,
},
gridIntervals: [{ timeUnit: "month", count: 3 }],
renderer: am5xy.AxisRendererX.new(root, {}),
tooltip: am5.Tooltip.new(root, {}),
dateFormats: {
month: "MM/yy",
},
tooltipDateFormats: {
month: "MM/yy",
},
})
);
let yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
})
);
let series = chart.series.push(
am5xy.LineSeries.new(root, {
name: "Series",
xAxis: xAxis,
yAxis: yAxis,
valueYField: "value",
valueXField: "date",
tooltip: am5.Tooltip.new(root, {
labelText: "{valueY}",
}),
fill: am5.color("#fdce97"),
stroke: am5.color("#fdce97"),
width: 4,
})
);
let otherSeries = chart.series.push(
am5xy.LineSeries.new(root, {
name: "Other Series",
xAxis: xAxis,
yAxis: yAxis,
valueYField: "value",
valueXField: "date",
tooltip: am5.Tooltip.new(root, {
labelText: "{valueY}",
}),
fill: am5.color("#19214d"),
stroke: am5.color("#19214d"),
})
);
seriesRef.current = otherSeries; // chart assigned to the reference to use later
series.bullets.push(function () {
let bulletCircle = am5.Circle.new(root, {
radius: 2,
stroke: am5.color("#fdce97"),
strokeWidth: 1.5,
});
return am5.Bullet.new(root, {
sprite: bulletCircle,
});
});
otherSeries.bullets.push(function () {
let bulletCircle = am5.Circle.new(root, {
radius: 2,
stroke: am5.color("#19214d"),
strokeWidth: 1.5,
});
return am5.Bullet.new(root, {
sprite: bulletCircle,
});
});
// Invisible bullet which will be dragged (to avoid some conflicting between
// drag position and bullet position which results flicker)
otherSeries.bullets.push(() => {
let bulletCircle = am5.Circle.new(root, {
radius: 6,
fillOpacity: 0,
fill: otherSeries.get("fill"),
draggable: true,
cursorOverStyle: "pointer",
});
bulletCircle.events.on("dragged", function (e) {
handleDrag(e);
});
bulletCircle.events.on("dragstop", function (e) {
//handleChange(e, otherSeries);
});
return am5.Bullet.new(root, {
sprite: bulletCircle,
});
});
// Drag handler
const handleDrag = (e: any) => {
let point = chart.plotContainer.toLocal(e.point);
let date = xAxis.positionToValue(xAxis.coordinateToPosition(point.x));
let value = round(
yAxis.positionToValue(yAxis.coordinateToPosition(point.y)),
1
);
let dataItem = e.target.dataItem;
dataItem.set("valueX", date);
dataItem.set("valueXWorking", date);
dataItem.set("valueY", value);
dataItem.set("valueYWorking", value);
};
// Set data
const draggableData: any = [];
draggableValues.forEach((value: any, index: number) => {
draggableData.push({
value: value,
date: dates[index],
});
});
const originalData: any = [];
originalValues.forEach((value: any, index: number) => {
originalData.push({
value: value,
date: dates[index],
});
});
//setOriginalData(originalData);
series.data.processor = am5.DataProcessor.new(root, {
numericFields: ["value"],
dateFields: ["date"],
dateFormat: "dd/MM/yyyy",
});
otherSeries.data.processor = am5.DataProcessor.new(root, {
numericFields: ["value"],
dateFields: ["date"],
dateFormat: "dd/MM/yyyy",
});
otherSeries.data.setAll(draggableData);
series.data.setAll(originalData);
return () => {
root.dispose();
};
}, []);
我称之为图表的部分:
return (
<Dialog open={true} onClose={handleCloseDialog} maxWidth={dialogMaxWidth}>
<Box
sx={{
display: "flex",
flexDirection: "column",
backgroundColor: "grey.200",
minWidth: 500,
}}
>
<Box
sx={{
display: "inline-flex",
alignItems: "center",
px: 2.5,
py: 2.5,
}}
>
<Box
sx={{
display: "inline-flex",
flexDirection: "column",
backgroundColor: "white",
width: "100%",
boxShadow: 1,
borderRadius: "6px",
p: 3,
}}
>
<Box id={id} sx={{ width: "326px", height: "300px" }}></Box>
</Box>
</Box>
</Box>
</Dialog>
);
如果我像这样调用这个函数,其中图表位于图表之外,它会完美地工作:
return (
<>
<Box id={id} sx={{ width: "326px", height: "300px" }}></Box>
<Dialog open={true} onClose={handleCloseDialog} maxWidth={dialogMaxWidth}>
<Box
sx={{
display: "flex",
flexDirection: "column",
backgroundColor: "grey.200",
minWidth: 500,
}}
>
<Box
sx={{
display: "inline-flex",
alignItems: "center",
px: 2.5,
py: 2.5,
}}
>
<Box
sx={{
display: "inline-flex",
flexDirection: "column",
backgroundColor: "white",
width: "100%",
boxShadow: 1,
borderRadius: "6px",
p: 3,
}}
>
</Box>
</Box>
</Box>
</Dialog>
</>
);
要解决此问题,您需要确保仅在
amChart 5
打开并安装元素后创建 Dialog
实例。您可以使用 ref
而不是 id
来访问元素,并使用 onEntered
组件的 Dialog
属性来触发创建图表的回调函数。
例如:
const chartRef = useRef(null);
const chartInstanceRef = useRef(null);
useEffect(() => {
return () => {
if (chartInstanceRef.current) {
chartInstanceRef.current.dispose();
}
};
}, []);
const handleEntered = () => {
const element = chartRef.current;
const root = am5.Root.new(element);
/* define chart */
chartInstanceRef.current = root;
};
return (
<Dialog
open={open}
onClose={onClose}
onEntered={handleEntered}
maxWidth={dialogMaxWidth}
>
<div ref={chartRef} style={{ width: "100%", height: "500px" }}></div>
</Dialog>
);