استكشف إمكانيات D3.js من خلال هذه الأمثلة العملية. يمكنك تعديل وتخصيص هذه الأمثلة لاستخدامها في مشاريعك الخاصة.
يعد مخطط الأعمدة من أبسط وأكثر أنواع المخططات استخدامًا. يوضح هذا المثال كيفية إنشاء مخطط أعمدة أساسي باستخدام D3.js.
// البيانات
const data = [
{ month: "يناير", value: 45 },
{ month: "فبراير", value: 65 },
{ month: "مارس", value: 35 },
{ month: "أبريل", value: 78 },
{ month: "مايو", value: 52 },
{ month: "يونيو", value: 88 }
];
// إعداد أبعاد الرسم البياني
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// إنشاء عنصر SVG
const svg = d3.select("#bar-chart-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// إنشاء مقياس x (نطاق أفقي)
const x = d3.scaleBand()
.domain(data.map(d => d.month))
.range([0, width])
.padding(0.2);
// إنشاء مقياس y (نطاق رأسي)
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.nice()
.range([height, 0]);
// إضافة محور x
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "rotate(-45)")
.style("text-anchor", "end");
// إضافة محور y
svg.append("g")
.call(d3.axisLeft(y));
// إنشاء الأعمدة
svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => x(d.month))
.attr("width", x.bandwidth())
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value))
.attr("fill", "#4f46e5");
// إضافة عنوان للمحور س
svg.append("text")
.attr("text-anchor", "middle")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 5)
.text("الشهر");
// إضافة عنوان للمحور ص
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", -margin.left + 15)
.text("القيمة");
x
لتحديد موضع الأعمدة أفقيًا و y
لتحديد ارتفاع الأعمدة.x
و y
لإظهار القيم.المخطط الخطي مناسب لعرض البيانات المستمرة والتغير عبر الزمن. يوضح هذا المثال كيفية إنشاء مخطط خطي بمنطقة ملونة تحته.
// البيانات
const data = [
{ date: "2023-01", value: 20 },
{ date: "2023-02", value: 25 },
{ date: "2023-03", value: 18 },
{ date: "2023-04", value: 30 },
{ date: "2023-05", value: 28 },
{ date: "2023-06", value: 35 },
{ date: "2023-07", value: 40 },
{ date: "2023-08", value: 45 },
{ date: "2023-09", value: 48 },
{ date: "2023-10", value: 52 },
{ date: "2023-11", value: 60 },
{ date: "2023-12", value: 58 }
];
// تحويل البيانات
const parseDate = d3.timeParse("%Y-%m");
const lineData = data.map(d => ({
...d,
parsedDate: parseDate(d.date)
}));
// إعداد أبعاد الرسم البياني
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// إنشاء SVG
const svg = d3.select("#line-chart-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// إنشاء مقياس X
const x = d3.scaleTime()
.domain(d3.extent(lineData, d => d.parsedDate))
.range([0, width]);
// إنشاء مقياس Y
const y = d3.scaleLinear()
.domain([0, d3.max(lineData, d => d.value) * 1.1])
.nice()
.range([height, 0]);
// تعريف دالة الخط
const line = d3.line()
.x(d => x(d.parsedDate))
.y(d => y(d.value))
.curve(d3.curveMonotoneX);
// تعريف دالة المنطقة
const area = d3.area()
.x(d => x(d.parsedDate))
.y0(height)
.y1(d => y(d.value))
.curve(d3.curveMonotoneX);
// إضافة محور X
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%b")));
// إضافة محور Y
svg.append("g")
.call(d3.axisLeft(y));
// إضافة المنطقة الملونة تحت الخط
svg.append("path")
.datum(lineData)
.attr("fill", "rgba(79, 70, 229, 0.2)")
.attr("d", area);
// إضافة الخط
svg.append("path")
.datum(lineData)
.attr("fill", "none")
.attr("stroke", "#4f46e5")
.attr("stroke-width", 2)
.attr("d", line);
// إضافة نقاط البيانات
svg.selectAll(".dot")
.data(lineData)
.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", d => x(d.parsedDate))
.attr("cy", d => y(d.value))
.attr("r", 4)
.attr("fill", "#4f46e5");
// إضافة عنوان للمحور Y
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left + 15)
.attr("x", -height / 2)
.attr("text-anchor", "middle")
.text("القيمة");
// إضافة عنوان للمحور X
svg.append("text")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 10)
.attr("text-anchor", "middle")
.text("الشهر (2023)");
المخطط الدائري مناسب لعرض النسب والتوزيعات النسبية للبيانات. يوضح هذا المثال كيفية إنشاء مخطط دائري تفاعلي.
// البيانات
const data = [
{ label: "الفئة أ", value: 30 },
{ label: "الفئة ب", value: 15 },
{ label: "الفئة ج", value: 25 },
{ label: "الفئة د", value: 10 },
{ label: "الفئة هـ", value: 20 }
];
// إعداد أبعاد الرسم البياني
const width = 500;
const height = 400;
const margin = 40;
const radius = Math.min(width, height) / 2 - margin;
// إنشاء SVG
const svg = d3.select("#pie-chart-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width} ${height}`)
.append("g")
.attr("transform", `translate(${width / 2},${height / 2})`);
// نطاق الألوان
const color = d3.scaleOrdinal()
.domain(data.map(d => d.label))
.range(d3.schemeSet2);
// تعريف دالة الرسم الدائري
const pie = d3.pie()
.value(d => d.value)
.sort(null);
// تعريف دالة القوس
const arc = d3.arc()
.innerRadius(0)
.outerRadius(radius);
// تعريف دالة قوس النص
const labelArc = d3.arc()
.innerRadius(radius * 0.6)
.outerRadius(radius * 0.6);
// إنشاء tooltip
const tooltip = d3.select("#pie-chart-container")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("border-radius", "4px")
.style("padding", "6px")
.style("box-shadow", "0 2px 4px rgba(0,0,0,0.1)");
// إنشاء الأقواس
const arcs = svg.selectAll(".arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
// إضافة المسارات
arcs.append("path")
.attr("d", arc)
.attr("fill", d => color(d.data.label))
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("transition", "opacity 0.3s")
.on("mouseover", function(event, d) {
d3.select(this)
.style("opacity", 0.8);
tooltip
.style("visibility", "visible")
.html(`${d.data.label}: ${d.data.value} (${(d.data.value * 100 / d3.sum(data, d => d.value)).toFixed(1)}%)`)
.style("left", (event.pageX - 100) + "px")
.style("top", (event.pageY - 50) + "px");
})
.on("mouseout", function() {
d3.select(this)
.style("opacity", 1);
tooltip.style("visibility", "hidden");
});
// إضافة النصوص
arcs.append("text")
.attr("transform", d => `translate(${labelArc.centroid(d)})`)
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.text(d => d.data.label);
// إضافة عنوان
svg.append("text")
.attr("text-anchor", "middle")
.attr("font-size", "16px")
.attr("y", -height / 2 + margin / 2)
.text("توزيع البيانات حسب الفئة");
مخطط الانتشار مفيد لعرض العلاقة بين متغيرين وتحديد الأنماط والارتباطات.
// البيانات
const data = [
{ x: 10, y: 40, group: "أ", size: 8 },
{ x: 20, y: 30, group: "أ", size: 12 },
{ x: 30, y: 60, group: "أ", size: 10 },
{ x: 40, y: 40, group: "ب", size: 15 },
{ x: 50, y: 30, group: "ب", size: 8 },
{ x: 60, y: 50, group: "ب", size: 12 },
{ x: 70, y: 70, group: "ج", size: 18 },
{ x: 80, y: 60, group: "ج", size: 10 },
{ x: 90, y: 50, group: "ج", size: 14 }
];
// إعداد أبعاد الرسم البياني
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// إنشاء SVG
const svg = d3.select("#scatter-plot-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// إنشاء مقياس X
const x = d3.scaleLinear()
.domain([0, d3.max(data, d => d.x) * 1.1])
.range([0, width]);
// إنشاء مقياس Y
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.y) * 1.1])
.range([height, 0]);
// إنشاء مقياس اللون
const color = d3.scaleOrdinal()
.domain(["أ", "ب", "ج"])
.range(["#4f46e5", "#10b981", "#f59e0b"]);
// إنشاء tooltip
const tooltip = d3.select("#scatter-plot-container")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("border-radius", "4px")
.style("padding", "6px")
.style("pointer-events", "none")
.style("box-shadow", "0 2px 4px rgba(0,0,0,0.1)");
// إضافة محور X
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
// إضافة محور Y
svg.append("g")
.call(d3.axisLeft(y));
// إضافة النقاط
svg.selectAll(".dot")
.data(data)
.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", d => x(d.x))
.attr("cy", d => y(d.y))
.attr("r", d => d.size)
.attr("fill", d => color(d.group))
.attr("stroke", "white")
.attr("stroke-width", 1.5)
.on("mouseover", function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr("r", d => d.size * 1.3);
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html(`المجموعة: ${d.group}
X: ${d.x}
Y: ${d.y}`)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px");
})
.on("mouseout", function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr("r", d => d.size);
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// إضافة عنوان للمحور X
svg.append("text")
.attr("text-anchor", "middle")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 5)
.text("قيمة X");
// إضافة عنوان للمحور Y
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", -margin.left + 15)
.text("قيمة Y");
// إضافة مفتاح الألوان
const legend = svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(["أ", "ب", "ج"])
.enter().append("g")
.attr("transform", (d, i) => `translate(0,${i * 20})`);
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(d => `المجموعة ${d}`);
يوضح هذا المثال كيفية إنشاء مخطط أعمدة تفاعلي يستجيب لحركة المؤشر ويعرض معلومات إضافية.
// البيانات
const data = [
{ category: "فئة أ", value: 45, color: "#4f46e5" },
{ category: "فئة ب", value: 72, color: "#10b981" },
{ category: "فئة ج", value: 35, color: "#f59e0b" },
{ category: "فئة د", value: 62, color: "#ef4444" },
{ category: "فئة هـ", value: 53, color: "#8b5cf6" }
];
// إعداد أبعاد الرسم البياني
const margin = { top: 30, right: 30, bottom: 50, left: 60 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// إنشاء SVG
const svg = d3.select("#interactive-bars-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// إنشاء مقياس X
const x = d3.scaleBand()
.range([0, width])
.domain(data.map(d => d.category))
.padding(0.3);
// إنشاء مقياس Y
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value) * 1.1])
.range([height, 0]);
// إضافة محور X
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x))
.selectAll("text")
.attr("font-size", "12px");
// إضافة محور Y
svg.append("g")
.call(d3.axisLeft(y));
// إنشاء tooltip
const tooltip = d3.select("#interactive-bars-container")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("border-radius", "4px")
.style("padding", "8px")
.style("box-shadow", "0 2px 8px rgba(0,0,0,0.15)")
.style("pointer-events", "none");
// إنشاء الأعمدة
svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => x(d.category))
.attr("y", d => y(d.value))
.attr("width", x.bandwidth())
.attr("height", d => height - y(d.value))
.attr("fill", d => d.color)
.attr("rx", 4)
.style("transition", "all 0.3s ease")
.on("mouseover", function(event, d) {
d3.select(this)
.attr("fill", d3.color(d.color).brighter(0.3))
.attr("y", d => y(d.value) - 10)
.attr("height", d => height - y(d.value) + 10);
tooltip.transition()
.duration(200)
.style("opacity", 1);
tooltip.html(`
${d.category}
القيمة: ${d.value}
`)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 40) + "px");
})
.on("mouseout", function(event, d) {
d3.select(this)
.attr("fill", d.color)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// إضافة قيم على الأعمدة
svg.selectAll(".label")
.data(data)
.enter()
.append("text")
.attr("class", "label")
.attr("text-anchor", "middle")
.attr("x", d => x(d.category) + x.bandwidth() / 2)
.attr("y", d => y(d.value) - 8)
.attr("font-size", "12px")
.attr("fill", "#333")
.text(d => d.value);
// إضافة العناوين
svg.append("text")
.attr("x", width / 2)
.attr("y", height + margin.bottom - 10)
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.text("الفئات");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left + 20)
.attr("x", -height / 2)
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.text("القيم");
الرسوم البيانية المتحركة تجذب الانتباه وتوضح التغيرات في البيانات بشكل فعال. هذا المثال يوضح كيفية إنشاء رسم بياني متحرك مع انتقالات سلسة.
// البيانات الأولية
let data = [
{ category: "أ", value: Math.floor(Math.random() * 50) + 10 },
{ category: "ب", value: Math.floor(Math.random() * 50) + 10 },
{ category: "ج", value: Math.floor(Math.random() * 50) + 10 },
{ category: "د", value: Math.floor(Math.random() * 50) + 10 },
{ category: "هـ", value: Math.floor(Math.random() * 50) + 10 },
];
// إعداد أبعاد الرسم البياني
const margin = { top: 50, right: 30, bottom: 40, left: 50 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// إنشاء SVG
const svg = d3.select("#animated-chart-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// إنشاء مقياس X
const x = d3.scaleBand()
.range([0, width])
.domain(data.map(d => d.category))
.padding(0.4);
// إنشاء مقياس Y
const y = d3.scaleLinear()
.domain([0, 100]) // استخدام نطاق ثابت للتمكين من الرسوم المتحركة السلسة
.range([height, 0]);
// إضافة محور X
const xAxis = svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
// إضافة محور Y
const yAxis = svg.append("g")
.call(d3.axisLeft(y));
// دالة تحديث الرسم البياني
function update(data) {
// تحديث نطاق X
x.domain(data.map(d => d.category));
// تحديث محور X
xAxis.transition()
.duration(1000)
.call(d3.axisBottom(x));
// ربط البيانات بالأعمدة
const bars = svg.selectAll(".bar")
.data(data, d => d.category);
// إزالة الأعمدة القديمة
bars.exit()
.transition()
.duration(500)
.attr("y", height)
.attr("height", 0)
.remove();
// تحديث الأعمدة الموجودة
bars.transition()
.duration(1000)
.attr("x", d => x(d.category))
.attr("width", x.bandwidth())
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value))
.attr("fill", d => d3.schemeCategory10[Math.floor(Math.random() * 10)]);
// إضافة أعمدة جديدة
bars.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => x(d.category))
.attr("width", x.bandwidth())
.attr("y", height) // تبدأ من الأسفل للانتقال
.attr("height", 0)
.attr("fill", d => d3.schemeCategory10[Math.floor(Math.random() * 10)])
.transition()
.duration(1000)
.attr("y", d => y(d.value))
.attr("height", d => height - y(d.value));
// تحديث النصوص
const labels = svg.selectAll(".label")
.data(data, d => d.category);
// إزالة النصوص القديمة
labels.exit()
.transition()
.duration(500)
.style("opacity", 0)
.remove();
// تحديث النصوص الموجودة
labels.transition()
.duration(1000)
.attr("x", d => x(d.category) + x.bandwidth() / 2)
.attr("y", d => y(d.value) - 8)
.text(d => d.value);
// إضافة نصوص جديدة
labels.enter()
.append("text")
.attr("class", "label")
.attr("text-anchor", "middle")
.attr("x", d => x(d.category) + x.bandwidth() / 2)
.attr("y", d => y(d.value) - 8)
.attr("font-size", "12px")
.attr("fill", "#333")
.style("opacity", 0)
.text(d => d.value)
.transition()
.duration(1000)
.style("opacity", 1);
}
// البدء بالرسم الأولي
update(data);
// تحديث البيانات عند النقر على الزر
d3.select("#update-data").on("click", function() {
// إنشاء بيانات جديدة بشكل عشوائي
data = [];
const categories = ["أ", "ب", "ج", "د", "هـ", "و", "ز"];
const numCategories = Math.floor(Math.random() * 3) + 3; // 3 إلى 5 فئات
for (let i = 0; i < numCategories; i++) {
data.push({
category: categories[i],
value: Math.floor(Math.random() * 80) + 10
});
}
// تحديث الرسم البياني
update(data);
});
مخططات القوة مفيدة لتمثيل الشبكات والعلاقات بين العناصر. هذا المثال يوضح كيفية إنشاء مخطط قوة تفاعلي.
// البيانات: العقد والروابط
const nodes = [
{ id: 1, name: "العقدة 1", group: 1 },
{ id: 2, name: "العقدة 2", group: 1 },
{ id: 3, name: "العقدة 3", group: 1 },
{ id: 4, name: "العقدة 4", group: 2 },
{ id: 5, name: "العقدة 5", group: 2 },
{ id: 6, name: "العقدة 6", group: 2 },
{ id: 7, name: "العقدة 7", group: 3 },
{ id: 8, name: "العقدة 8", group: 3 },
{ id: 9, name: "العقدة 9", group: 3 },
{ id: 10, name: "العقدة 10", group: 4 }
];
const links = [
{ source: 1, target: 2, value: 1 },
{ source: 1, target: 3, value: 1 },
{ source: 2, target: 3, value: 1 },
{ source: 3, target: 4, value: 1 },
{ source: 4, target: 5, value: 1 },
{ source: 4, target: 6, value: 1 },
{ source: 5, target: 6, value: 1 },
{ source: 6, target: 7, value: 1 },
{ source: 7, target: 8, value: 1 },
{ source: 7, target: 9, value: 1 },
{ source: 8, target: 9, value: 1 },
{ source: 9, target: 10, value: 1 }
];
// إعداد أبعاد الرسم البياني
const width = 600;
const height = 400;
// إنشاء SVG
const svg = d3.select("#force-directed-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("preserveAspectRatio", "xMidYMid meet");
// إنشاء tooltip
const tooltip = d3.select("#force-directed-container")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("border-radius", "4px")
.style("padding", "8px")
.style("box-shadow", "0 2px 4px rgba(0,0,0,0.1)")
.style("pointer-events", "none");
// إنشاء مقياس للألوان
const color = d3.scaleOrdinal(d3.schemeCategory10);
// إنشاء المحاكاة
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-200))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collision", d3.forceCollide().radius(30));
// إنشاء الروابط
const link = svg.append("g")
.selectAll("line")
.data(links)
.enter()
.append("line")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.attr("stroke-width", d => Math.sqrt(d.value));
// إنشاء العقد
const node = svg.append("g")
.selectAll("g")
.data(nodes)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// إضافة دوائر للعقد
node.append("circle")
.attr("r", 15)
.attr("fill", d => color(d.group))
.attr("stroke", "#fff")
.attr("stroke-width", 2)
.on("mouseover", function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr("r", 20);
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html(`
${d.name}
المجموعة: ${d.group}
`)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px");
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(200)
.attr("r", 15);
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
// إضافة نصوص للعقد
node.append("text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("fill", "#fff")
.attr("font-size", "10px")
.text(d => d.id);
// تحديث موضع العناصر عند كل خطوة من المحاكاة
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("transform", d => `translate(${d.x},${d.y})`);
});
// وظائف السحب
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
الخرائط الشجرية مفيدة لعرض البيانات الهرمية وتوزيع المساحة بشكل متناسب مع القيم.
// البيانات الهرمية
const data = {
name: "البيانات",
children: [
{
name: "الفئة أ",
children: [
{ name: "أ1", value: 25 },
{ name: "أ2", value: 15 },
{ name: "أ3", value: 10 }
]
},
{
name: "الفئة ب",
children: [
{ name: "ب1", value: 20 },
{ name: "ب2", value: 30 }
]
},
{
name: "الفئة ج",
children: [
{ name: "ج1", value: 35 },
{ name: "ج2", value: 15 },
{ name: "ج3", value: 20 },
{ name: "ج4", value: 25 }
]
}
]
};
// إعداد أبعاد الرسم البياني
const width = 600;
const height = 450;
const margin = { top: 10, right: 10, bottom: 10, left: 10 };
// إنشاء SVG
const svg = d3.select("#treemap-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${width} ${height}`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// إنشاء تسلسل هرمي
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
// إنشاء وظيفة الخريطة الشجرية
const treemap = d3.treemap()
.size([width - margin.left - margin.right, height - margin.top - margin.bottom])
.paddingOuter(3)
.paddingTop(15)
.paddingInner(2)
.round(true);
// تطبيق الوظيفة
treemap(root);
// إنشاء نطاق ألوان
const color = d3.scaleOrdinal()
.domain(["الفئة أ", "الفئة ب", "الفئة ج"])
.range(d3.schemeSet1);
// إضافة المستطيلات
const cell = svg.selectAll("g")
.data(root.descendants())
.enter()
.append("g")
.attr("transform", d => `translate(${d.x0},${d.y0})`);
// إضافة المستطيلات للخلايا الأم
cell.filter(d => d.depth === 1)
.append("rect")
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0)
.attr("fill", d => color(d.data.name))
.attr("stroke", "#fff")
.attr("stroke-width", 2)
.attr("opacity", 0.7);
// إضافة المستطيلات للخلايا النهائية
cell.filter(d => d.depth === 2)
.append("rect")
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0)
.attr("fill", d => d3.color(color(d.parent.data.name)).brighter(0.5))
.attr("stroke", "#fff")
.attr("stroke-width", 1)
.on("mouseover", function() {
d3.select(this)
.transition()
.duration(200)
.attr("opacity", 0.8);
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(200)
.attr("opacity", 1);
});
// إضافة النصوص للخلايا الأم
cell.filter(d => d.depth === 1)
.append("text")
.attr("x", 5)
.attr("y", 15)
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", "#fff")
.text(d => d.data.name);
// إضافة النصوص للخلايا النهائية
cell.filter(d => d.depth === 2)
.append("text")
.attr("x", 5)
.attr("y", 15)
.attr("font-size", "12px")
.attr("fill", "#333")
.text(d => `${d.data.name}: ${d.data.value}`);
// إضافة النص للمستوى الأعلى
svg.append("text")
.attr("x", width / 2)
.attr("y", 20)
.attr("text-anchor", "middle")
.attr("font-size", "16px")
.attr("font-weight", "bold")
.text("خريطة شجرية لتوزيع البيانات");