۱

معرفی و نصب

D3.js چیست؟

D3.js (مخفف Data-Driven Documents) یک کتابخانه JavaScript است که توسط Mike Bostock ساخته شده. برخلاف Chart.js که نمودارهای آماده دارد، D3 ابزار سطح پایین‌تری است که کنترل کامل بر DOM، SVG و انیمیشن می‌دهد.

💡 D3 در مقابل Chart.js: اگر نمودارهای ساده می‌خواهید → Chart.js. اگر ویژوالیزیشن سفارشی، نقشه، tree، force layout یا هر چیز پیچیده‌ای → D3.

SVG در مقابل Canvas

ویژگیSVGCanvas
نوعبرداریرستری
DOM Element✓ بله✗ خیر
رویداد (click/hover)✓ راحت✗ دستی
عملکرد داده زیادخوبعالی
D3 پیش‌فرض✓ بلهممکن

نصب

<!-- فایل لوکال -->
<script src="d3.min.js"></script>

<!-- CDN -->
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>

/* NPM */
// npm install d3
import * as d3 from "d3";
D3 یک ماژول monolith است (~۶۰۰KB). در پروژه‌های بزرگ فقط ماژول‌های مورد نیاز را import کنید.
۲

Selection و DOM

Selection در D3 شبیه document.querySelector است اما با قابلیت‌های بیشتر برای تغییر دسته‌جمعی المان‌ها.

انتخاب و تغییر المان‌ها

// انتخاب یک المان
d3.select('#my-svg')
// انتخاب همه المان‌ها
d3.selectAll('circle')

// تغییر خاصیت‌ها
d3.select('#title')
  .text('D3.js')              // محتوای متنی
  .style('color', '#88ce02')  // CSS style
  .attr('class', 'title');   // HTML attribute

// اضافه و حذف کلاس
d3.select('.box').classed('active', true);

// اضافه کردن المان
d3.select('#container')
  .append('p')
  .text('یک پاراگراف جدید');

// حذف المان
d3.select('.old').remove();

// رویداد
d3.select('button').on('click', event => {
  console.log('کلیک شد!', event);
});
دمو: Selection زنده
۳

Data Binding — اتصال داده

Data Binding قلب D3 است: داده‌ها را به عناصر DOM متصل می‌کنید. با .data(array) هر عنصر آرایه به یک DOM element مرتبط می‌شود.

const data = [10, 30, 20, 50, 40];

d3.select('#chart')
  .selectAll('div')   // انتخاب مجازی (هنوز وجود ندارند)
  .data(data)          // اتصال داده
  .join('div')         // ایجاد/آپدیت/حذف خودکار
    .style('width', d => d * 5 + 'px')   // d = مقدار هر داده
    .style('background', '#88ce02')
    .text(d => d);

// آرایه‌ای از object
const products = [
  { name: 'لپتاپ',   sales: 45 },
  { name: 'موبایل', sales: 80 },
  { name: 'تبلت',   sales: 30 }
];

d3.select('#list')
  .selectAll('li')
  .data(products)
  .join('li')
    .text(d => `${d.name}: ${d.sales} فروش`);
دمو: Data Binding — نمودار ساده
۴

Enter / Update / Exit

این مفهوم کلیدی D3 است که تعیین می‌کند وقتی داده‌ها تغییر می‌کنند، DOM چطور آپدیت شود.

حالتمعنیکاربرد
.enter()داده بیشتر از DOMاضافه کردن المان جدید
.exit()DOM بیشتر از دادهحذف المان اضافی
update (selection)تطابق داده و DOMآپدیت المان‌های موجود
.join()هر سه با همساده‌ترین روش
// روش قدیمی (کنترل کامل)
const sel = svg.selectAll('circle').data(newData);

sel.enter().append('circle')    // المان‌های جدید
  .attr('r', 0)
  .transition()
  .attr('r', d => d);

sel.exit()                         // المان‌های اضافی
  .transition()
  .attr('r', 0)
  .remove();

sel                                  // المان‌های موجود
  .transition()
  .attr('r', d => d);

// روش جدید (D3 v5+) — توصیه‌شده
svg.selectAll('circle')
  .data(newData, d => d.id)    // key function
  .join(
    enter  => enter.append('circle').attr('r', 0).transition().attr('r', d => d.r),
    update => update.transition().attr('r', d => d.r),
    exit   => exit.transition().attr('r', 0).remove()
  );
دمو: Enter/Update/Exit زنده
۵

SVG و رسم اشکال

// ساختار پایه SVG
const margin = { top: 20, right: 20, bottom: 40, left: 50 };
const width  = 600 - margin.left - margin.right;
const height = 400 - margin.top  - margin.bottom;

const svg = d3.select('#chart')
  .append('svg')
    .attr('width',  width  + margin.left + margin.right)
    .attr('height', height + margin.top  + margin.bottom)
  .append('g')   // گروه اصلی
    .attr('transform', `translate(${margin.left},${margin.top})`);

// اشکال اصلی SVG
svg.append('rect').attr('x',0).attr('y',0).attr('width',100).attr('height',60).attr('fill','#88ce02');
svg.append('circle').attr('cx',150).attr('cy',50).attr('r',40).attr('fill','#3498db');
svg.append('line').attr('x1',0).attr('y1',100).attr('x2',300).attr('y2',100).attr('stroke','#e74c3c').attr('stroke-width',2);
svg.append('text').attr('x',50).attr('y',150).text('متن SVG').attr('fill','#333');
دمو: اشکال SVG
۶

Scales — مقیاس‌ها

Scale در D3 یک تابع تبدیل است که مقادیر داده (domain) را به مختصات تصویر (range) تبدیل می‌کند.

// ۱. scaleLinear — برای اعداد پیوسته
const yScale = d3.scaleLinear()
  .domain([0, 100])    // محدوده داده (min → max)
  .range([height, 0]);  // محدوده SVG (پایین → بالا)

yScale(50);   // → وسط height
yScale(0);    // → height (پایین)
yScale(100);  // → 0 (بالا)

// ۲. scaleBand — برای دسته‌ها (Bar chart)
const xScale = d3.scaleBand()
  .domain(['A', 'B', 'C', 'D'])
  .range([0, width])
  .padding(0.2);  // فاصله بین میله‌ها

xScale.bandwidth(); // عرض هر میله

// ۳. scaleTime — برای تاریخ
const tScale = d3.scaleTime()
  .domain([new Date('2024-01-01'), new Date('2024-12-31')])
  .range([0, width]);

// ۴. scaleOrdinal — رنگ‌بندی
const colorScale = d3.scaleOrdinal(d3.schemeTableau10);
colorScale('فروردین');  // رنگ اول
colorScale('اردیبهشت'); // رنگ دوم

// ۵. scaleSequential — گرادیان رنگ
const heatScale = d3.scaleSequential(d3.interpolateRdYlGn)
  .domain([0, 100]);
heatScale(0);   // قرمز
heatScale(50);  // زرد
heatScale(100); // سبز
دمو: انواع Scales
۷

Axes — محورها

// ساختن محور X
const xAxis = d3.axisBottom(xScale)  // محور پایین
  .tickSize(6)
  .tickPadding(8);

// ساختن محور Y
const yAxis = d3.axisLeft(yScale)    // محور چپ
  .ticks(5)
  .tickFormat(d => d + '٪');        // فرمت‌بندی

// اعمال محورها به SVG
svg.append('g')
  .attr('class', 'x-axis')
  .attr('transform', `translate(0,${height})`)
  .call(xAxis);

svg.append('g')
  .attr('class', 'y-axis')
  .call(yAxis);

// خطوط راهنما (Grid Lines)
svg.append('g')
  .attr('class', 'grid')
  .call(d3.axisLeft(yScale)
    .tickSize(-width)
    .tickFormat('')           // بدون متن
  );

// برچسب محور
svg.append('text')
  .attr('transform', `translate(${width/2},${height+35})`)
  .text('ماه')
  .style('text-anchor', 'middle');
دمو: محورها با Grid Lines
۸

نمودار میله‌ای کامل

ترکیب همه مفاهیم: Data Binding، Scales، Axes، Tooltip و Transition.

const data = [
  { month: 'فروردین',  value: 45 },
  { month: 'اردیبهشت', value: 72 },
  // ...
];

const margin = {top:20, right:20, bottom:60, left:60};
const w = 560 - margin.left - margin.right;
const h = 320 - margin.top  - margin.bottom;

const svg = d3.select('#bar-chart')
  .append('svg')
  .attr('width',  w + margin.left + margin.right)
  .attr('height', h + margin.top  + margin.bottom)
  .append('g')
  .attr('transform', `translate(${margin.left},${margin.top})`);

const x = d3.scaleBand().domain(data.map(d=>d.month)).range([0,w]).padding(0.25);
const y = d3.scaleLinear().domain([0, d3.max(data, d=>d.value)*1.1]).range([h,0]);

// میله‌ها
svg.selectAll('.bar').data(data).join('rect')
  .attr('class', 'bar')
  .attr('x',      d => x(d.month))
  .attr('width',  x.bandwidth())
  .attr('y',      h)       // شروع از پایین
  .attr('height', 0)      // ارتفاع صفر
  .attr('fill',   '#88ce02')
  .transition().duration(800).delay((d,i) => i*80)
  .attr('y',      d => y(d.value))
  .attr('height', d => h - y(d.value));
دمو: نمودار میله‌ای با Tooltip
۹

نمودار خطی و ناحیه‌ای

// d3.line — تولید path برای خط
const lineGen = d3.line()
  .x(d => xScale(d.date))
  .y(d => yScale(d.value))
  .curve(d3.curveMonotoneX);  // نوع منحنی

svg.append('path')
  .datum(data)                // .datum() برای یک مجموعه داده
  .attr('fill', 'none')
  .attr('stroke', '#88ce02')
  .attr('stroke-width', 2.5)
  .attr('d', lineGen);         // d = SVG path string

// d3.area — تولید path برای ناحیه زیر خط
const areaGen = d3.area()
  .x(d => xScale(d.date))
  .y0(height)                  // خط پایین (baseline)
  .y1(d => yScale(d.value))    // خط بالا (مقدار)
  .curve(d3.curveMonotoneX);

svg.append('path')
  .datum(data)
  .attr('fill', 'rgba(136,206,2,0.15)')
  .attr('d', areaGen);
ⓘ نوع‌های منحنی: curveLinear (خط مستقیم) | curveMonotoneX (منحنی بدون دشواری) | curveCatmullRom (منحنی نرم) | curveStep (پله‌ای)
دمو: نمودار خطی با ناحیه و Tooltip
۱۰

نمودار دایره‌ای و دونات

// d3.pie — محاسبه زوایا
const pieGen = d3.pie()
  .value(d => d.value)
  .sort(null);               // حفظ ترتیب اصلی

// d3.arc — رسم قوس
const arcGen = d3.arc()
  .innerRadius(0)           // 0 = pie | > 0 = doughnut
  .outerRadius(radius);

// arc برای hover (کمی بزرگتر)
const arcHover = d3.arc()
  .innerRadius(0)
  .outerRadius(radius + 10);

// رسم قطعه‌ها
const arcs = svg.selectAll('.slice')
  .data(pieGen(data))
  .join('path')
  .attr('class', 'slice')
  .attr('d', arcGen)
  .attr('fill', (d,i) => colorScale(i))
  .on('mouseenter', function(event, d) {
    d3.select(this).attr('d', arcHover);  // بزرگ شدن
  })
  .on('mouseleave', function(event, d) {
    d3.select(this).attr('d', arcGen);   // برگشت
  });
دمو: Pie و Doughnut با Hover
۱۱

Tooltip و تعامل

// ایجاد tooltip div
const tooltip = d3.select('body')
  .append('div')
  .attr('class', 'tooltip')
  .style('position', 'absolute')
  .style('opacity', '0')
  .style('background', 'rgba(0,0,0,.8)')
  .style('color', 'white')
  .style('padding', '8px 12px')
  .style('border-radius', '6px');

// رویدادهای hover
bars
  .on('mouseover', (event, d) => {
    tooltip
      .html(`${d.month}
فروش: ${d.value} میلیون`) .style('opacity', '1') .style('left', event.pageX + 10 + 'px') .style('top', event.pageY - 20 + 'px'); }) .on('mousemove', event => { tooltip .style('left', event.pageX + 10 + 'px') .style('top', event.pageY - 20 + 'px'); }) .on('mouseleave', () => { tooltip.style('opacity', '0'); }); // Bisector — یافتن نزدیک‌ترین نقطه به موس در Line Chart const bisect = d3.bisector(d => d.date).left; svg.on('mousemove', event => { const xVal = xScale.invert(d3.pointer(event)[0]); const idx = bisect(data, xVal); const point = data[idx]; tooltip.html(`${point.date}: ${point.value}`); });
دمو: نمودار با Crosshair Tooltip
۱۲

Transitions و انیمیشن

// انیمیشن ساده
d3.select('#circle')
  .transition()
  .duration(1000)
  .ease(d3.easeElastic)
  .attr('r', 50)
  .attr('fill', '#3498db');

// Easing انواع
d3.easeLinear     // یکنواخت
d3.easeQuad       // شتاب quadratic
d3.easeCubicInOut // شروع و پایان کند
d3.easeBounce     // پرشی
d3.easeElastic    // ارتجاعی
d3.easeBack       // کمی از target رد می‌شود

// زنجیره transition
d3.select('#box')
  .transition().duration(500)
    .attr('width', 200)
  .transition().duration(300)
    .style('fill', '#e74c3c')
  .transition().duration(500)
    .attr('width', 50);

// on('end') — بعد از تمام انیمیشن
d3.select('.bar')
  .transition()
  .on('end', () => console.log('تمام شد!'));
دمو: انواع Easing

خلاصه مهم‌ترین توابع D3

تابعکاربرد
d3.select / selectAllانتخاب DOM
selection.data().join()Data Binding
d3.scaleLinear/Band/Timeتبدیل داده به مختصات
d3.axisBottom/Leftرسم محور
d3.line / areaمولد مسیر SVG
d3.pie / arcنمودار دایره‌ای
d3.max / min / extentمحاسبه domain
d3.bisectorجستجوی باینری در آرایه
selection.transition()انیمیشن
d3.schemeTableau10پالت رنگ

🚀زمین بازی کد (Playground)

کد D3.js را همین‌جا اجرا کن و نتیجه را روی SVG زنده ببین. data binding، scales و transitions را تغییر بده و ▶ اجرا را بزن. تب HTML شامل <svg> است. نسخهٔ ۷.۹.۰ آفلاین بارگذاری می‌شود.

💡 محیط ایزوله است؛ خروجی console.log() در پنل پایین نمایش داده می‌شود.