React中Highcharts自定义:修复Plotline文本截断及新数据点标记
问题描述
我正在用Highcharts结合highcharts-react-official开发ETH实时价格图表,从父组件获取startedRoundData展示当前轮次详情,已为startTime和endTime添加plotlines,但plotline文本出现截断问题(见附图)。我希望复刻目标图表的所有细节与功能,以下是我的代码:
React组件代码
import React, { useEffect, useState } from "react"; import Highcharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; import moment from "moment"; const Chart = ({ data, startedRoundData }) => { const [chartOptions, setChartOptions] = useState({}); useEffect(() => { const pricesData = data?.prices || []; const chartData = pricesData .slice(0, -1) .map((item) => [item.timestamp * 1000, parseFloat(item.price)]); const lastTimestamp = chartData[chartData.length - 1]?.[0] || 0; const futureTimestamp = lastTimestamp + 10000; // 10 seconds in the future const options = { chart: { type: "spline", backgroundColor: "#131722", height: "400px", marginRight: 10, marginLeft: 0, marginBottom: 30, marginTop: 10, animation: Highcharts.svg, }, title: null, xAxis: { type: "datetime", lineColor: "#2a2e39", tickColor: "#2a2e39", labels: { style: { color: "#787b86", fontSize: "10px" }, y: 20, formatter: function () { return moment(this.value).format("HH:mm:ss"); }, }, tickLength: 0, minPadding: 0, maxPadding: 0, gridLineWidth: 1, gridLineColor: "#2a2e39", tickInterval: 10000, // 10 seconds interval tickAmount: 6, min: lastTimestamp - 50000, // Show 50 seconds of past data max: futureTimestamp, // Extend to 10 seconds in the future plotLines: [ { color: "#ff9800", width: 1, value: startedRoundData?.startTime ? moment(startedRoundData?.startTime).utc().valueOf() : null, // Convert to UTC dashStyle: "dash", zIndex: 5, label: { useHTML: true, // Allows for custom HTML align: "center", y: 0, x: 0, formatter: function () { return ` <div class="custom-plotline-label"> <span class="label-text">Current</span> </div> `; }, }, events: { render: function () { // Access and style after rendering const plotLineLabel = this.plotLinesAndBands[0].label.element; plotLineLabel.style.transform = "rotate(270deg)"; plotLineLabel.style.transformOrigin = "100% 12px"; plotLineLabel.style.position = "absolute"; plotLineLabel.style.left = "150px"; plotLineLabel.style.top = "10px"; }, }, }, { color: "#ff9800", width: 1, value: startedRoundData?.lockTime ? moment(startedRoundData?.lockTime).utc().valueOf() : null, // Convert to UTC dashStyle: "dash", zIndex: 5, label: { text: "End", align: "center", style: { color: "#ffffff", fontSize: "12px" }, y: 15, }, }, ], }, yAxis: { title: null, labels: { align: "right", x: 45, style: { color: "#787b86", fontSize: "10px" }, formatter: function () { return this.value.toFixed(2); }, }, gridLineColor: "#2a2e39", gridLineWidth: 1, tickAmount: 5, opposite: true, plotLines: [ { color: "#ff9800", width: 1, value: startedRoundData?.startTime ? moment(startedRoundData?.startTime).utc().valueOf() : null, // Convert to UTC dashStyle: "dash", zIndex: 5, label: { text: startedRoundData?.startPrice ? startedRoundData?.startPrice : null, align: "center", style: { color: "#ffffff", fontSize: "12px", whiteSpace: "nowrap", // To ensure the text doesn't break }, y: 15, }, }, ], }, legend: { enabled: false }, series: [ { name: "Price", data: chartData, color: "#00ff00", lineWidth: 2, marker: { enabled: false, }, }, ], tooltip: { enabled: false }, credits: { enabled: false }, plotOptions: { spline: { animation: { duration: 1000, easing: "easeOutQuart", }, }, }, }; setChartOptions(options); }, [data]); useEffect(() => { if (chartOptions.series) { const chart = Highcharts.charts[0]; if (chart) { const series = chart.series[0]; const lastPoint = series.data[series.data.length - 1]; if (lastPoint) { lastPoint.update( { marker: { enabled: true, radius: 5, symbol: "circle", fillColor: "#00ff00", lineColor: "#00ff00", lineWidth: 2, states: { hover: { enabled: true, radius: 5, fillColor: "#00ff00", lineColor: "#00ff00", lineWidth: 2, }, }, }, }, false ); chart.redraw(); } } } }, [chartOptions]); return ( <div className="bg-[#131722] p-4 rounded-lg h-[485px]"> <HighchartsReact highcharts={Highcharts} options={chartOptions} /> </div> ); }; export default Chart;
依赖脚本
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
解决方案
1. 修复Plotline文本截断问题
- X轴"Current"标签:
- 调整图表
marginLeft为40,给旋转后的标签留出左侧空间; - 移除硬编码的
left:150px,改用更合理的定位逻辑,同时确保标签不超出容器:label: { useHTML: true, align: "center", y: 200, // 垂直居中 x: -20, // 左移避免超出容器 style: { whiteSpace: 'nowrap', overflow: 'visible' }, formatter: function () { return ` <div class="custom-plotline-label" style="white-space: nowrap;"> <span class="label-text">Current</span> </div> `; }, }, events: { render: function () { const plotLineLabel = this.plotLinesAndBands[0].label.element; plotLineLabel.style.transform = "rotate(270deg)"; plotLineLabel.style.transformOrigin = "center center"; plotLineLabel.style.position = "absolute"; }, },
- 调整图表
- Y轴plotline修正:你错误地将时间戳作为Y轴plotline的
value,应该替换为价格值,同时调整标签对齐方式避免截断:yAxis: { // ...其他配置 plotLines: [ { color: "#ff9800", width: 1, value: startedRoundData?.startPrice ? parseFloat(startedRoundData.startPrice) : null, dashStyle: "dash", zIndex: 5, label: { text: startedRoundData?.startPrice ? startedRoundData.startPrice : null, align: "right", style: { color: "#ffffff", fontSize: "12px", whiteSpace: "nowrap", overflow: "visible" }, y: 0, x: -10 }, }, ], }, - 通用调整:把图表
marginRight改为40,给右侧标签留出显示空间。
2. 代码优化与细节完善
- 实时数据更新优化:将最后一个点的marker更新逻辑依赖改为
data,避免不必要的重绘:useEffect(() => { const chart = Highcharts.charts[0]; if (chart && chart.series.length) { const series = chart.series[0]; const lastPoint = series.data[series.data.length - 1]; if (lastPoint) { lastPoint.update( { marker: { enabled: true, radius: 5, symbol: "circle", fillColor: "#00ff00", lineColor: "#00ff00", lineWidth: 2, states: { hover: { enabled: true, radius: 5, fillColor: "#00ff00", lineColor: "#00ff00", lineWidth: 2, }, }, }, }, false ); chart.redraw(); } } }, [data]); - Plotline条件渲染:当
startedRoundData为空时,不渲染plotlines,避免空值异常:plotLines: [ ...(startedRoundData?.startTime ? [{/* startTime plotline配置 */}] : []), ...(startedRoundData?.lockTime ? [{/* lockTime plotline配置 */}] : []) ] - X轴范围动态修正:直接取最新数据的时间戳计算范围,确保图表始终显示最近50秒+未来10秒:
const lastTimestamp = pricesData.length ? pricesData[pricesData.length - 1].timestamp * 1000 : 0; const futureTimestamp = lastTimestamp + 10000;
3. 自定义CSS补充
添加以下CSS确保自定义标签样式生效:
.custom-plotline-label { color: #ffffff; font-size: 12px; padding: 2px 4px; background-color: #ff9800; border-radius: 2px; }
内容的提问来源于stack exchange,提问作者kashan ilyas
相关产品推荐
相关产品推荐

