You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@amcharts/amcharts5词云图:如何基于dataItem动态设置背景?

解决AmCharts5词云图label背景色动态设置时dataItem为undefined的问题

问题描述

在React项目中使用@amcharts/amcharts5开发词云图,需要根据频率阈值动态为每个词设置背景色,逻辑与已实现的文字填充色一致。但在设置背景色的代码中,始终无法获取到label对应的dataItem(返回undefined),导致无法进行频率判断。

问题代码片段:

series.labels.template.setup = function (label) {
    let backgroundColor;
    let borderColor;
    const dataItem = label.dataItem;
    console.log("dataItem Background", dataItem);
    const frequency = dataItem?.get("frequency");
    console.log("background frequency", frequency)
    // ... 后续背景色逻辑
};

原因分析

AmCharts5的setup方法是在元素初始化阶段执行的,此时label还未与对应的dataItem完成绑定,所以label.dataItem会返回undefined。而之前文字填充色使用的adapters.add能正常获取dataItem,是因为适配器会在元素绑定数据后、属性生效前触发,此时dataItem已经存在。

解决方案

改用**适配器(adapter)**来动态设置背景属性,与文字填充色逻辑保持一致,确保能获取到绑定后的dataItem。

修改后的完整代码

import React, { useEffect, useRef } from "react";
import * as am5 from "@amcharts/amcharts5";
import * as am5wc from "@amcharts/amcharts5/wc";
import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";

interface DataPoint {
    name: string;
    frequency: number;
}

interface WordChartProps {
    data: DataPoint[];
}

const WordChart: React.FC<WordChartProps> = ({ data = [] }) => {
    const chartRef = useRef<HTMLDivElement>(null);
    const dataSet = [
        { name: "Pricing", frequency: 100 },
        { name: "Availability", frequency: 50 },
        { name: "Online", frequency: 20 },
        { name: "Process", frequency: 10 },
        { name: "Work", frequency: 30 },
        { name: "Pricing", frequency: 100 },
        { name: "Availability", frequency: 50 },
        { name: "Online", frequency: 20 },
        { name: "Process", frequency: 10 },
        { name: "Work", frequency: 30 },
        { name: "Pricing", frequency: 100 },
        { name: "Availability", frequency: 50 },
        { name: "Online", frequency: 20 },
        { name: "Process", frequency: 10 },
        { name: "Work", frequency: 30 },
        { name: "Pricing", frequency: 100 },
        { name: "Availability", frequency: 50 },
        { name: "Online", frequency: 20 },
        { name: "Process", frequency: 10 },
        { name: "Work", frequency: 30 },
    ]
    dataSet.sort((a, b) => b.frequency - a.frequency);
    const groupSize = Math.ceil(dataSet.length / 3);
    const highThreshold = dataSet[groupSize - 1]?.frequency || 0; // Lowest frequency in "high"
    const mediumThreshold = dataSet[groupSize * 2 - 1]?.frequency || 0; // Lowest frequency in "medium"

    useEffect(() => {
        if (!chartRef.current) return;
        const root = am5.Root.new(chartRef.current);
        root.setThemes([am5themes_Animated.new(root)]);
        const series = root.container.children.push(
            am5wc.WordCloud.new(root, {
                maxFontSize: am5.percent(12),
                minFontSize: am5.percent(5),
                categoryField: "name",
                valueField: "frequency",
                rotation: 0,
                centerX: am5.percent(0),
                centerY: am5.percent(0),
                angles: [0],
            })
        );

        series.labels.template.setAll({
            paddingTop: 10,
            paddingBottom: 10,
            paddingLeft: 10,
            paddingRight: 10,
            fontFamily: "Roboto",
            fontSize: "14px",
            fontStyle: "normal",
            fontWeight: "400",
            textAlign: "center",
            tooltipText: "{category}: {value}"
        });

        // Dynamically setting the `fill` color for each label
        series.labels.template.adapters.add("fill", (fill, target) => {
            const dataItem = target.dataItem; // Access the associated data item
            const frequency: any = dataItem?.get("value"); // Retrieve the frequency field from data
            if (frequency >= highThreshold) {
                return am5.color(0x52a3ff);
            } else if (frequency >= mediumThreshold) {
                return am5.color(0x7fb5f2);
            } else {
                return am5.color(0x5e93c2);
            }
        });

        // 动态设置label的背景色(替换原来的setup方法)
        series.labels.template.adapters.add("background", (background, target) => {
            const dataItem = target.dataItem;
            if (!dataItem) return background;
            
            const frequency = dataItem.get("value");
            let backgroundColor;
            let borderColor = am5.color(0x52a3ff);

            if (frequency >= highThreshold) {
                backgroundColor = am5.color(0x52A3FF);
            } else if (frequency >= mediumThreshold) {
                backgroundColor = am5.color(0xF4F5F8);
            } else {
                backgroundColor = am5.color(0x52A3FF);
            }

            // 复用或创建背景元素
            if (!background) {
                return am5.RoundedRectangle.new(root, {
                    cornerRadiusBL: 10,
                    cornerRadiusBR: 10,
                    cornerRadiusTL: 10,
                    cornerRadiusTR: 10,
                    fill: backgroundColor,
                    fillOpacity: 1,
                    stroke: borderColor,
                    strokeWidth: 1,
                    marginTop: 15,
                    marginBottom: 15,
                    marginLeft: 15,
                    marginRight: 15,
                });
            } else {
                background.set("fill", backgroundColor);
                background.set("stroke", borderColor);
                return background;
            }
        });

        series.data.setAll(dataSet);
        return () => {
            root.dispose();
        };
    }, []);

    return <div ref={chartRef} style={{ width: "100%", height: "300px" }} />;
};

export default WordChart;

代码说明

  1. 替换setup为适配器:使用adapters.add("background")拦截背景属性的设置,此时label已绑定dataItem,可正常获取频率值。
  2. 数据安全判断:先检查dataItem是否存在,避免空值报错。
  3. 元素复用:判断背景元素是否已创建,存在则更新属性,不存在则创建新元素,避免重复DOM操作提升性能。

内容的提问来源于stack exchange,提问作者Nensi Kardani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:37:11