ויזואליזציות נתונים מותאמות אישית עם Visx (D3 + React)

ספריות תרשימים סטנדרטיות לרוב אינן מספקות במשימות לא שגרתיות: הן מגבילות התאמה אישית או נכשלות עם נתונים בהיקף גדול. אנו בונים ויזואליזציות מותאמות אישית עם Visx, המשלבות את הגמישות של D3 עם העוצמה של React ליצירת דיאגרמות אינטראקטיביות בכל מורכבות. הצוות שלנו מספק פרויקטים סוהר—מהקונספט ועד היישום והתמיכה השוטפת—תוך הבטחת ביצועים חלקים גם עם מערכי נתונים גדולים.

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

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

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
ויזואליזציות נתונים מותאמות אישית עם Visx (D3 + React)
בינוני
~3-5 ימים

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

שאלות נפוצות

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

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

מתי צריך ויזואליזציה מותאמת אישית עם Visx?

בקשה טיפוסית של לקוח: הצגת דינמיקת מחירי מניות בשנה האחרונה עם זום וטולטיפים. Recharts לא יכול להתמודד עם אנימציות לא שגרתיות, Chart.js מאט על 50,000 נקודות. צריך ויזואליזציה מותאמת אישית עם Visx — ספרייה מ-Airbnb המשלבת את הגמישות של D3 עם פרדיגמת React. Visx מאפשרת בניית כל תרשים SVG עם שליטה מלאה וביצועים גבוהים. אנחנו משתמשים בה בפרויקטים עם מערכי נתונים של עד 100,000 רשומות, ומבטיחים אנימציה חלקה ועדכונים מיידיים.

אילו בעיות Visx פותרת?

התאמה אישית מוגבלת של ספריות מוכנות

Recharts מציעה הרבה הגדרות, אבל כל סטייה מהתבנית הופכת לפריצה עם עקיפת פרופס. Visx נותנת שליטה מלאה על SVG: אתה מנהל כל אלמנט, סקאלה ומעבר. עם Visx, אפשר להשיג פי 5 יותר התאמה אישית מאשר Recharts לויזואליזציות מורכבות.

ביצועים על נפחי נתונים גדולים

רינדור D3 דרך מחזור החיים של React יכול לגרום ל-re-renders מיותרים. Visx מייעלת זאת עם רכיבים טהורים ו-memoization. על מערכי נתונים של 10,000 נקודות, Visx מספקת 60 FPS — עד פי 3 מהר יותר מ-Chart.js.

אינטגרציה עם אפליקציית React

רכיבי Visx הם רכיבי React רגילים שמשתלבים בקלות בכל ארכיטקטורה: Next.js, CRA, Vite. אין צורך ב-renderer נפרד או קונטקסט. לצוות שלנו יש ניסיון של 5+ שנים בבניית ויזואליזציות React, מה שמבטיח קוד באיכות גבוהה ומסירה בזמן.

איך Visx פותרת את משימת ההתאמה האישית?

Visx מספקת פרימיטיבים ברמה נמוכה: scaleTime, scaleLinear, LinePath, AreaClosed, AxisLeft, AxisBottom. אפשר להרכיב כל תרשים מאלה ללא תלות בתבניות. לאינטראקטיביות — הוק useTooltip ואירועי עכבר. כל הקוד הוא TypeScript עם נתונים מטיפוסים.

ניקח משימה טיפוסית: תרשים קו עם טולטיפ ואזור ממולא. דוגמת יישום ב-Visx (קטע קוד):

import { scaleTime, scaleLinear } from '@visx/scale';
import { LinePath, AreaClosed } from '@visx/shape';
import { AxisLeft, AxisBottom } from '@visx/axis';
import { GridRows, GridColumns } from '@visx/grid';
import { useTooltip, TooltipWithBounds, defaultStyles } from '@visx/tooltip';
import { localPoint } from '@visx/event';
import { bisector } from 'd3-array';
import { curveMonotoneX } from 'd3-shape';

interface DataPoint {
  date: Date;
  value: number;
}

const bisectDate = bisector<DataPoint, Date>(d => d.date).left;

function CustomLineChart({
  data,
  width,
  height,
  margin = { top: 20, right: 20, bottom: 40, left: 60 }
}: {
  data: DataPoint[];
  width: number;
  height: number;
  margin?: { top: number; right: number; bottom: number; left: number };
}) {
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  const xScale = scaleTime({
    range: [0, innerWidth],
    domain: [
      Math.min(...data.map(d => d.date.getTime())),
      Math.max(...data.map(d => d.date.getTime()))
    ]
  });

  const yScale = scaleLinear({
    range: [innerHeight, 0],
    domain: [0, Math.max(...data.map(d => d.value)) * 1.1],
    nice: true
  });

  const { tooltipData, tooltipLeft, tooltipTop, showTooltip, hideTooltip } = useTooltip<DataPoint>();

  const handleTooltip = (event: React.MouseEvent<SVGRectElement>) => {
    const { x } = localPoint(event) || { x: 0 };
    const x0 = xScale.invert(x - margin.left);
    const index = bisectDate(data, x0, 1);
    const d0 = data[index - 1];
    const d1 = data[index];
    const d = !d1 || Math.abs(x0.getTime() - d0.date.getTime()) < Math.abs(x0.getTime() - d1.date.getTime()) ? d0 : d1;
    showTooltip({
      tooltipData: d,
      tooltipLeft: xScale(d.date) + margin.left,
      tooltipTop: yScale(d.value) + margin.top
    });
  };

  return (
    <div style={{ position: 'relative' }}>
      <svg width={width} height={height}>
        <g transform={`translate(${margin.left}, ${margin.top})`}>
          <GridRows scale={yScale} width={innerWidth} stroke="#f0f0f0" />
          <GridColumns scale={xScale} height={innerHeight} stroke="#f0f0f0" />
          <defs>
            <linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor="#3b82f6" stopOpacity={0.3} />
              <stop offset="100%" stopColor="#3b82f6" stopOpacity={0} />
            </linearGradient>
          </defs>
          <AreaClosed
            data={data}
            x={d => xScale(d.date)}
            y={d => yScale(d.value)}
            yScale={yScale}
            fill="url(#areaGradient)"
            curve={curveMonotoneX}
          />
          <LinePath
            data={data}
            x={d => xScale(d.date)}
            y={d => yScale(d.value)}
            stroke="#3b82f6"
            strokeWidth={2}
            curve={curveMonotoneX}
          />
          <AxisLeft scale={yScale} tickFormat={v => `${(v as number / 1000).toFixed(0)}k`} />
          <AxisBottom top={innerHeight} scale={xScale} tickFormat={d => format(d as Date, 'dd MMM')} />
          <rect
            width={innerWidth}
            height={innerHeight}
            fill="transparent"
            onMouseMove={handleTooltip}
            onMouseLeave={hideTooltip}
          />
          {tooltipData && (
            <g>
              <line
                x1={tooltipLeft! - margin.left}
                x2={tooltipLeft! - margin.left}
                y1={0}
                y2={innerHeight}
                stroke="#3b82f6"
                strokeDasharray="4,4"
                strokeWidth={1}
              />
              <circle
                cx={tooltipLeft! - margin.left}
                cy={tooltipTop! - margin.top}
                r={5}
                fill="#3b82f6"
                stroke="white"
                strokeWidth={2}
              />
            </g>
          )}
        </g>
      </svg>
      {tooltipData && (
        <TooltipWithBounds
          top={tooltipTop}
          left={tooltipLeft}
          style={{ ...defaultStyles, background: '#1e293b', color: 'white' }}
        >
          <div>
            <strong>{format(tooltipData.date, 'dd.MM.yyyy')}</strong>
            <br />
            {tooltipData.value.toLocaleString('ru')} $
          </div>
        </TooltipWithBounds>
      )}
    </div>
  );
}

ParentSize לרספונסיביות

import { ParentSize } from '@visx/responsive';

function ResponsiveChart({ data }) {
  return (
    <ParentSize>
      {({ width, height }) => (
        <CustomLineChart data={data} width={width} height={height || 300} />
      )}
    </ParentSize>
  );
}

למה Visx ולא Chart.js?

Visx עולה על Chart.js במצבים הדורשים צורות תרשים לא סטנדרטיות או תרחישים אינטראקטיביים מורכבים. Chart.js קל יותר להגדרה ומהיר יותר לתרשימים פשוטים, אבל הארכיטקטורה שלו מתקשה להתרחב למשימות מותאמות אישית. Visx מספקת גישה מלאה ל-SVG, ומאפשרת כל ויזואליזציה. הלקוחות שלנו חוסכים עד 40% מזמן הפיתוח לדשבורדים מורכבים באמצעות Visx. פיתוח ויזואליזציה מותאמת אישית טיפוסי מתחיל ב-$2,500 לסוג תרשים.

השוואת Visx מול Recharts

קריטריון Visx Recharts
רמת שליטה מלאה (פרימיטיבי SVG) בינונית (הגדרת פרופס)
התאמה אישית בלתי מוגבלת מוגבלת על ידי תבניות
ביצועים גבוהים (שליטה ידנית) בינוניים (re-renders אוטומטיים)
זמן פיתוח 3-5 ימים לסוג 1-2 ימים לסוג
עקומת למידה בינונית (דורש ידע ב-D3) נמוכה (React בלבד)

טעויות נפוצות בעבודה עם Visx

  • חוסר memoization של סקאלות. סקאלות צריכות להיווצר בתוך import { scaleTime, scaleLinear } from '@visx/scale'; import { LinePath, AreaClosed } from '@visx/shape'; import { AxisLeft, AxisBottom } from '@visx/axis'; import { GridRows, GridColumns } from '@visx/grid'; import { useTooltip, TooltipWithBounds, defaultStyles } from '@visx/tooltip'; import { localPoint } from '@visx/event'; import { bisector } from 'd3-array'; import { curveMonotoneX } from 'd3-shape'; interface DataPoint { date: Date; value: number; } const bisectDate = bisector<DataPoint, Date>(d => d.date).left; function CustomLineChart({ data, width, height, margin = { top: 20, right: 20, bottom: 40, left: 60 } }: { data: DataPoint[]; width: number; height: number; margin?: { top: number; right: number; bottom: number; left: number }; }) { const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; const xScale = scaleTime({ range: [0, innerWidth], domain: [ Math.min(...data.map(d => d.date.getTime())), Math.max(...data.map(d => d.date.getTime())) ] }); const yScale = scaleLinear({ range: [innerHeight, 0], domain: [0, Math.max(...data.map(d => d.value)) * 1.1], nice: true }); const { tooltipData, tooltipLeft, tooltipTop, showTooltip, hideTooltip } = useTooltip<DataPoint>(); const handleTooltip = (event: React.MouseEvent<SVGRectElement>) => { const { x } = localPoint(event) || { x: 0 }; const x0 = xScale.invert(x - margin.left); const index = bisectDate(data, x0, 1); const d0 = data[index - 1]; const d1 = data[index]; const d = !d1 || Math.abs(x0.getTime() - d0.date.getTime()) < Math.abs(x0.getTime() - d1.date.getTime()) ? d0 : d1; showTooltip({ tooltipData: d, tooltipLeft: xScale(d.date) + margin.left, tooltipTop: yScale(d.value) + margin.top }); }; return ( <div style={{ position: 'relative' }}> <svg width={width} height={height}> <g transform={`translate(${margin.left}, ${margin.top})`}> <GridRows scale={yScale} width={innerWidth} stroke="#f0f0f0" /> <GridColumns scale={xScale} height={innerHeight} stroke="#f0f0f0" /> <defs> <linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stopColor="#3b82f6" stopOpacity={0.3} /> <stop offset="100%" stopColor="#3b82f6" stopOpacity={0} /> </linearGradient> </defs> <AreaClosed data={data} x={d => xScale(d.date)} y={d => yScale(d.value)} yScale={yScale} fill="url(#areaGradient)" curve={curveMonotoneX} /> <LinePath data={data} x={d => xScale(d.date)} y={d => yScale(d.value)} stroke="#3b82f6" strokeWidth={2} curve={curveMonotoneX} /> <AxisLeft scale={yScale} tickFormat={v => `${(v as number / 1000).toFixed(0)}k`} /> <AxisBottom top={innerHeight} scale={xScale} tickFormat={d => format(d as Date, 'dd MMM')} /> <rect width={innerWidth} height={innerHeight} fill="transparent" onMouseMove={handleTooltip} onMouseLeave={hideTooltip} /> {tooltipData && ( <g> <line x1={tooltipLeft! - margin.left} x2={tooltipLeft! - margin.left} y1={0} y2={innerHeight} stroke="#3b82f6" strokeDasharray="4,4" strokeWidth={1} /> <circle cx={tooltipLeft! - margin.left} cy={tooltipTop! - margin.top} r={5} fill="#3b82f6" stroke="white" strokeWidth={2} /> </g> )} </g> </svg> {tooltipData && ( <TooltipWithBounds top={tooltipTop} left={tooltipLeft} style={{ ...defaultStyles, background: '#1e293b', color: 'white' }} > <div> <strong>{format(tooltipData.date, 'dd.MM.yyyy')}</strong> <br /> {tooltipData.value.toLocaleString('ru')} $ </div> </TooltipWithBounds> )} </div> ); } או מחוץ לרכיב; אחרת, כל re-render יוצר אותן מחדש, מה שמוביל לאובדן ביצועים.
  • התעלמות מ-ParentSize. ללא קונטיינר אדפטיבי, התרשים לא יגיב לשינוי גודל החלון, מה שפוגע בחוויית המשתמש במכשירים ניידים.
  • טיפול לא נכון בקצוות נתונים. אם הנתונים מכילים NaN או null, Visx עלולה לזרוק שגיאה. תמיד סנן נתונים לפני העברתם לרכיבים.
  • שכחת נגישות. גרפיקת SVG צריכה להיות נגישה לקוראי מסך: הוסף import { ParentSize } from '@visx/responsive'; function ResponsiveChart({ data }) { return ( <ParentSize> {({ width, height }) => ( <CustomLineChart data={data} width={width} height={height || 300} /> )} </ParentSize> ); } ו-scaleTime.

איך לבנות תרשים מותאם אישית מאפס?

  1. הגדר סקאלות — scaleLinear לזמן, LinePath לערכים.
  2. תאר גאומטריה — AreaClosed לקו, AxisLeft למילוי.
  3. הוסף צירים — AxisBottom ו-useTooltip.
  4. יישם טולטיפ — השתמש בהוק ParentSize ובאירועי עכבר.
  5. עטוף בקונטיינר אדפטיבי — ParentSize לשינוי גודל אוטומטי.

תהליך ולוח זמנים

שלב משך
ניתוח ואיסוף דרישות 0.5–1 יום
אב-טיפוס MVP 1–2 ימים
פיתוח גרסה סופית 3–5 ימים לסוג
בקרת איכות ואופטימיזציית ביצועים 1–2 ימים
פריסה ואינטגרציה 0.5–1 יום

המחזור המלא לסוג ויזואליזציה אחד אורך 3 עד 5 ימים. סט של 3-4 סוגים שונים אורך 1.5 עד 2 שבועות. העלות מחושבת באופן אישי בהתאם למורכבות האינטראקציה ונפח הנתונים.

מה כלול בעבודה?

אנחנו מספקים פיתוח ויזואליזציית נתונים סוהר על Visx:

  • ניתוח דרישות ועיצוב פריסה;
  • קידוד מאפס באמצעות React + TypeScript;
  • בדיקות על נתונים אמיתיים (עד 100,000 רשומות);
  • התאמה למסכים ניידים ולתצוגות רטינה;
  • תיעוד ב-README והערות בקוד;
  • העברת מאגר הקוד וגישת פריסה;
  • הכשרת הצוות שלך על Visx (סמינר מקוון של שעה).

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