פיתוח דיאגרמות טרמיפ להמחשת היררכיה

כאשר נתונים היררכיים טובעים בטבלאות ופרופורציות מפתח הולכות לאיבוד בין השורות, דיאגרמת treemap מחזירה את הבהירות. אנחנו בונים treemaps אינטראקטיביים עם React ו-D3.js שהופכים מבנים מורכבים לוויזואליות ברורה. הצוות שלנו מספק את הפרויקט במפתח מלא—מהקונספט ועד התמיכה—ומבטיח פתרון אמין וסקלאבילי.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
פיתוח דיאגרמות טרמיפ להמחשת היררכיה
בינוני
מ- 1 יום עד 3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

תארו לעצמכם שיש לכם לוח מחוונים תקציבי לחברה עם יותר מ-500 פריטי הוצאה. אתם מבזבזים 20 דקות בחיפוש אחר "החור". מפת העץ שלנו מראה אותו תוך 2 שניות. מפת עץ עושה דבר אחד היטב: מציגה את היחס בין חלקים במבנה היררכי. השטח של כל מלבן פרופורציונלי לערך, והצבע מקודד ממד נוסף — צמיחה/ירידה, קטגוריה, סטטוס. אם אתם צריכים להבין מה תופס הכי הרבה מקום בתקציב, בעץ קטגוריות, במערכת קבצים או בתיק נכסים, מפת עץ נקראת מהר יותר מכל טבלה. פיתוח על React 18 ו-D3.js v7 נותן שליטה מלאה על כל פיקסל: טולטיפ מותאם אישית, אנימציה בעת ירידה ברמות, ופריסה רספונסיבית לכל רזולוציה. אפילו עם 10,000 צמתים, הפריסה נבנית תוך 30–50 אלפיות שנייה — מספיק לעדכונים בזמן אמת. החל מ-$1,500 למפת עץ בסיסית, פתרון זה חוסך בדרך כלל 30% מזמן הניתוח.

אילו בעיות מפת עץ פותרת?

קריאות לקויה של טבלאות עם היררכיה. כאשר לטבלה יש 10,000 שורות, מציאת 5 הקטגוריות המובילות דורשת גלילה ומיון. מפת עץ הופכת אותן לברורות ויזואלית — המוח מעבד שטח באופן מיידי.

קושי בתפיסת פרופורציות. לטבלאות יש מספרים, אבל כדי להבין כמה תקציב השיווק עולה על תקציב הפיתוח, צריך לחלק. מפת עץ נותנת את התשובה ללא חישוב.

צורך בהשוואת חלקים בזמן אמת. כאשר נתונים מתעדכנים (לדוגמה, שערי מניות), מפת עץ עם שינויי צבע מקודדים מראה אילו נכסים עולים ואילו יורדים — במבט אחד.

כיצד אנו מיישמים מפת עץ עם React ו-D3.js

אנו משתמשים ב-React 18 וב-D3.js v7. D3 אינה ספריית תרשימים אלא אולר שוויצרי לעבודה עם נתונים ו-SVG. היא נותנת שליטה מלאה על כל מלבן. איננו משתמשים בעטיפות ברמה גבוהה כי כל פרויקט דורש פתרונות מותאמים אישית: טולטיפ מותאם, אנימציית ירידה ברמות, פריסה רספונסיבית. הליבה של ויזואליזציית ההיררכיה שלנו היא רכיב React מותאם אישית שמשתלב בצורה חלקה עם הנתונים שלכם.

הנה יישום טיפוסי של רכיב מפת עץ:

import { useEffect, useRef } from 'react';
import * as d3 from 'd3';

interface TreeNode {
  name: string;
  value?: number;
  children?: TreeNode[];
  change?: number; // % изменения для цветового кодирования
}

interface TreemapProps {
  data: TreeNode;
  width?: number;
  height?: number;
  colorBy?: 'category' | 'change';
}

export function Treemap({ data, width = 800, height = 500, colorBy = 'category' }: TreemapProps) {
  const svgRef = useRef<SVGSVGElement>(null);

  useEffect(() => {
    if (!svgRef.current) return;

    const svg = d3.select(svgRef.current);
    svg.selectAll('*').remove();

    // Иерархия
    const root = d3.hierarchy(data)
      .sum(d => d.value ?? 0)
      .sort((a, b) => (b.value ?? 0) - (a.value ?? 0));

    // Layout
    d3.treemap()
      .size([width, height])
      .paddingOuter(3)
      .paddingInner(2)
      .paddingTop(18)
      .round(true)(root);

    const colorScale = colorBy === 'change'
      ? d3.scaleDiverging(d3.interpolateRdYlGn).domain([-30, 0, 30])
      : d3.scaleOrdinal(d3.schemeTableau10);

    const tooltip = d3.select('body').append('div')
      .style('position', 'absolute')
      .style('display', 'none')
      .style('background', 'rgba(15,23,42,0.9)')
      .style('color', '#f1f5f9')
      .style('padding', '8px 12px')
      .style('border-radius', '4px')
      .style('font-size', '13px')
      .style('pointer-events', 'none')
      .style('max-width', '220px');

    // Все узлы с потомками (для группировок)
    const leaves = root.leaves();
    const ancestors = root.descendants().filter(d => d.depth === 1);

    // Фоновые прямоугольники для групп
    svg.selectAll('.group-rect')
      .data(ancestors)
      .join('rect')
      .attr('class', 'group-rect')
      .attr('x', (d: any) => d.x0)
      .attr('y', (d: any) => d.y0)
      .attr('width', (d: any) => d.x1 - d.x0)
      .attr('height', (d: any) => d.y1 - d.y0)
      .attr('fill', (d: any) => colorBy === 'change' ? '#e2e8f0' : d3.color(colorScale(d.data.name))!.brighter(0.7).toString())
      .attr('stroke', '#fff')
      .attr('stroke-width', 2);

    // Заголовки групп
    svg.selectAll('.group-label')
      .data(ancestors)
      .join('text')
      .attr('class', 'group-label')
      .attr('x', (d: any) => d.x0 + 6)
      .attr('y', (d: any) => d.y0 + 13)
      .attr('font-size', 11)
      .attr('font-weight', '600')
      .attr('fill', '#374151')
      .text((d: any) => d.data.name);

    // Листья
    const cell = svg.selectAll('.cell')
      .data(leaves)
      .join('g')
      .attr('class', 'cell')
      .attr('transform', (d: any) => `translate(${d.x0},${d.y0})`);

    cell.append('rect')
      .attr('width', (d: any) => d.x1 - d.x0)
      .attr('height', (d: any) => d.y1 - d.y0)
      .attr('fill', (d: any) => {
        if (colorBy === 'change') return colorScale(d.data.change ?? 0);
        return colorScale((d.parent?.data.name ?? '') as string);
      })
      .attr('fill-opacity', 0.85)
      .attr('stroke', '#fff')
      .attr('stroke-width', 1)
      .on('mouseover', (event, d: any) => {
        d3.select(event.currentTarget).attr('fill-opacity', 1);
        const pct = d.data.change != null ? `<br/>Изменение: ${d.data.change > 0 ? '+' : ''}${d.data.change.toFixed(1)}%` : '';
        tooltip
          .style('display', 'block')
          .style('left', `${event.pageX + 12}px`)
          .style('top', `${event.pageY - 28}px`)
          .html(`<strong>${d.data.name}</strong><br/>${d3.format(',.0f')(d.value ?? 0)}${pct}`);
      })
      .on('mouseout', (event) => {
        d3.select(event.currentTarget).attr('fill-opacity', 0.85);
        tooltip.style('display', 'none');
      });

    // Текст внутри ячеек (только если достаточно места)
    cell.append('text')
      .attr('x', 4)
      .attr('y', 14)
      .attr('font-size', 11)
      .attr('fill', '#fff')
      .attr('font-weight', '500')
      .text((d: any) => {
        const w = d.x1 - d.x0;
        const h = d.y1 - d.y0;
        return w > 40 && h > 20 ? d.data.name : '';
      })
      .each(function(d: any) {
        const el = d3.select(this);
        const maxWidth = d.x1 - d.x0 - 8;
        // Обрезаем текст если не помещается
        let text = d.data.name;
        while (this.getComputedTextLength() > maxWidth && text.length > 3) {
          text = text.slice(0, -1);
          el.text(text + '…');
        }
      });

    // Значение под названием
    cell.append('text')
      .attr('x', 4)
      .attr('y', 26)
      .attr('font-size', 10)
      .attr('fill', 'rgba(255,255,255,0.8)')
      .text((d: any) => {
        const w = d.x1 - d.x0;
        const h = d.y1 - d.y0;
        return w > 50 && h > 35 ? d3.format(',.0f')(d.value ?? 0) : '';
      });

    return () => {
      tooltip.remove();
    };
  }, [data, width, height, colorBy]);

  return <svg ref={svgRef} width={width} height={height} style={{ display: 'block' }} />;
}
פרטי יישום בקוד שלמעלה, אנו יוצרים היררכיה באמצעות d3.hierarchy, מגדירים את הפריסה עם ריפוד, מציירים מלבני רקע לקבוצות, תוויות, תאים עם טולטיפ וטקסט. התנהגות רספונסיבית מבטיחה קריאות בכל מסך.

כיצד להוסיף ירידה ברמות?

ירידה ברמות היא תכונה מרכזית ללוחות מחוונים אינטראקטיביים. המשתמש לוחץ על קבוצה — ומפת העץ מצוירת מחדש עם נתוני רמת הילדים. יישמנו רכיב DrilldownTreemap עם פירורי לחם לניווט חזרה.

function DrilldownTreemap({ data }: { data: TreeNode }) {
  const [currentNode, setCurrentNode] = useState<TreeNode>(data);
  const [breadcrumb, setBreadcrumb] = useState<TreeNode[]>([data]);

  function drillDown(node: TreeNode) {
    if (!node.children?.length) return;
    setCurrentNode(node);
    setBreadcrumb(prev => [...prev, node]);
  }

  function drillUp(index: number) {
    const target = breadcrumb[index];
    setCurrentNode(target);
    setBreadcrumb(prev => prev.slice(0, index + 1));
  }

  return (
    <div>
      <nav className="flex gap-2 text-sm mb-3">
        {breadcrumb.map((node, i) => (
          <span key={i}>
            {i > 0 && <span className="text-gray-400 mx-1">/</span>}
            <button
              onClick={() => drillUp(i)}
              className={i === breadcrumb.length - 1 ? 'font-semibold' : 'text-blue-600 hover:underline'}
            >
              {node.name}
            </button>
          </span>
        ))}
      </nav>
      <Treemap data={currentNode} onCellClick={drillDown} />
    </div>
  );
}

באיזה אלגוריתם פריסה לבחור?

D3.js מספקת ארבעה אלגוריתמי ריצוף. השוואה:

אלגוריתם תכונה מתי להשתמש
treemapSquarify תאים מרובעים ככל האפשר, יחס גובה-רוחב טוב ברירת מחדל — הקריאות הטובה ביותר
treemapSliceDice מחליף בין פרוסות אופקיות ואנכיות כאשר לנתונים יש היררכיה דו-כיוונית קפדנית
treemapSlice רק פרוסות אופקיות להצגת חלקים בשורה אחת
treemapResquarify עושה שימוש חוזר בפריסה הקודמת, ממזער תנועה לעדכוני נתונים מונפשים

אנו תמיד משתמשים ב-treemapSquarify בסביבת ייצור — הוא נותן את יחס גובה-הרוחב הטוב ביותר. אם יש צורך בשינויי נתונים חלקים (לדוגמה, עדכוני מניות), אנו עוברים ל-treemapResquarify. כפי שצוין ב-תיעוד D3.js, אלגוריתם Squarify מספק יחס גובה-רוחב אופטימלי ברוב המקרים.

כיצד להכין נתונים למפת עץ?

כדי שמפת העץ תעבוד, יש להפוך נתוני צד לקוח להיררכיה. לעתים קרובות ה-API מחזיר רשימה שטוחה של שורות. הפונקציה buildHierarchy מקבצת אותן לפי קטגוריות וקטגוריות משנה:

// Трансформация плоских данных в иерархию
function buildHierarchy(items: { category: string; subcategory: string; name: string; value: number }[]): TreeNode {
  const root: TreeNode = { name: 'root', children: [] };
  items.forEach(item => {
    let cat = root.children!.find(c => c.name === item.category);
    if (!cat) {
      cat = { name: item.category, children: [] };
      root.children!.push(cat);
    }
    let subcat = cat.children!.find(c => c.name === item.subcategory);
    if (!subcat) {
      subcat = { name: item.subcategory, children: [] };
      cat.children!.push(subcat);
    }
    subcat.children!.push({ name: item.name, value: item.value });
  });
  return root;
}

תהליך עבודה ותזמון

  1. ניתוח — לימוד מבנה הנתונים, הגדרת רמות היררכיה, מדדים לשטח וצבע. (יום אחד)
  2. עיצוב — יצירת אב טיפוס על בסיס הנתונים שלכם, הסכמה על עיצוב ואינטראקטיביות. (יום אחד)
  3. יישום — כתיבת הרכיב ב-React + D3.js, הגדרת טולטיפ, ירידה ברמות, אנימציה. (2–3 ימים)
  4. בדיקות — בדיקה על נתונים אמיתיים: נפחים גדולים, עדכונים בזמן אמת. (יום אחד)
  5. פריסה — שילוב בפרויקט שלכם, מתן הוראות שימוש. (יום אחד)
תוצר לוח זמנים
מפת עץ בסיסית עם טולטיפ וירידה ברמות 2–3 ימים
עם אנימציית מעבר וקידוד צבע 4–6 ימים
מחזור מלא (מניתוח לפריסה) החל משבוע אחד

טעויות נפוצות בפיתוח מפת עץ

  • התעלמות מ-paddingTop. תוויות קבוצה חופפות תאים — הטקסט הופך לבלתי קריא. אנו תמיד מגדירים paddingTop: 18.
  • טקסט בתאים קטנים מדי. אנו בודקים רוחב וגובה לפני רינדור, אחרת — גלישה.
  • חוסר טיפול בנתונים ריקים. אם לצומת יש ערך = 0, הוא עדיין תופס מקום. השתמשו בסינון.
  • התעלמות מ-treemapResquarify לאנימציה. בלעדיו, כל רינדור מחדש מערבב תאים — משתמשים מאבדים הקשר.

מה כלול בעבודה ולמה לבחור בנו

  • קוד מקור של הרכיב (React + TypeScript + D3)
  • שילוב עם ה-API שלכם
  • תיעוד להתקנה והתאמה אישית
  • הוראות פריסה
  • הדרכת צוות (עד שעתיים)
  • אחריות ל-3 חודשים לפעולה ללא הפרעות

יש לנו ניסיון של 5+ שנים בוויזואליזציית נתונים בממשקים מורכבים ו-30+ פרויקטים שהושלמו עם מפות עץ, תרשימי שמש ודיאגרמות היררכיות אחרות. המפתחים שלנו מוסמכים ב-React וב-D3.js, וכל הרכיבים בנויים מאפס, ללא סיכוני רישוי. צרו קשר לייעוץ והזמינו מפת עץ שמדברת בעד עצמה. קבלו הערכת עלות לפרויקט תוך יום עסקים אחד — פשוט שלחו לנו נתונים לדוגמה ודרישות אינטראקטיביות.