مقدمة لطيفة لـ D3: كيفية إنشاء مخطط فقاعي قابل لإعادة الاستخدام

الابتداء مع D3

عندما بدأت أتعلم D3 ، لم يكن هناك أي معنى بالنسبة لي. أصبحت الأمور أكثر وضوحًا فقط عندما بدأت في التعرف على المخططات القابلة لإعادة الاستخدام.

في هذه المقالة ، سأوضح لك كيفية إنشاء مخطط فقاعات قابلة لإعادة الاستخدام ومنحك مقدمة لطيفة عن D3 على طول الطريق. تتكون مجموعة البيانات التي سنستخدمها من قصص منشورة في freeCodeCamp في يناير 2017.

هذا هو المخطط الذي ستبنيه

حول D3

D3 هي مكتبة JavaScript لتصور البيانات. إنه يجلب البيانات إلى الحياة باستخدام HTML و SVG و CSS.

نحتاج غالبًا إلى إعادة استخدام مخطط في مشروع آخر أو مشاركة الحدث في المخطط مع الآخرين. لهذا الغرض ، اقترح مايك بوستوك (منشئ D3) نموذجًا يسمى المخططات القابلة لإعادة الاستخدام. سنستخدم منهجه مع بعض التعديلات الصغيرة ، كما قدمها بابلو نافارو كاستيلو في كتاب Mastering D3.js.

نحن نستخدم الإصدار D3 4.6.0 هنا.

المخططات القابلة لإعادة الاستخدام

تتميز الرسوم البيانية التي تتبع نمط المخطط القابل لإعادة الاستخدام بخاصيتين:

  • كونفيغورابيليتي. نريد تعديل مظهر وسلوك الرسم البياني دون الحاجة إلى تعديل الكود نفسه.
  • القدرة على أن تبنى بطريقة مستقلة. نريد كل عنصر من عناصر الرسم البياني يرتبط بنقطة بيانات من مجموعة البيانات الخاصة بنا بشكل مستقل. يتعلق هذا بالطريقة التي يربط بها D3 مثيلات البيانات بعناصر DOM. هذا سوف يصبح أكثر وضوحا في دقيقة واحدة.
"لتلخيص: تنفيذ المخططات كإغلاقات مع أساليب الضرب". - مايك بوستوك

الرسم البياني فقاعة

تحتاج أولاً إلى تحديد عناصر المخطط التي يمكن تخصيصها:

  • حجم المخطط
  • مجموعة بيانات الإدخال

تحديد حجم المخطط

لنبدأ بإنشاء وظيفة لتغليف جميع متغيرات الرسم البياني وتعيين القيم الافتراضية. هذا الهيكل يسمى إغلاق.

// bubble_graph.js
var bubbleChart = function () {
    فار العرض = 600 ،
    الارتفاع = 400

    مخطط الوظائف (اختيار) {
        / / ستحصل هنا
    }

    مخطط العودة ؛
}

تريد إنشاء مخططات بأحجام مختلفة دون الحاجة إلى تغيير الكود. لهذا ستقوم بإنشاء مخططات كما يلي:

// bubble_graph.html
var chart = bubbleChart (). العرض (300). الارتفاع (200) ؛

للقيام بذلك ، ستقوم الآن بتعريف أدوات الوصول لمتغيرات العرض والارتفاع.

// bubble_graph.js
var bubbleChart = function () {
    فار العرض = 600
    الارتفاع = 400
    مخطط الوظائف (اختيار) {
        / / سنصل هنا
    }

    chart.width = دالة (قيمة) {
        if (! arguments.length) {return width؛ }
        العرض = القيمة ؛

        مخطط العودة ؛
    }
    المخطط. الارتفاع = الوظيفة (القيمة)
        if (! arguments.length) {ارتفاع الإرجاع ؛ }
        ارتفاع = القيمة ؛

        مخطط العودة ؛
    }

    مخطط العودة ؛
}

إذا قمت باستدعاء bubbleChart () (بدون سمات العرض أو الارتفاع) ، فسيتم إنشاء الرسم البياني باستخدام قيم العرض والارتفاع الافتراضية التي حددتها داخل الإغلاق. إذا تم استدعاؤها بدون وسيطات ، فتُرجع الطريقة القيمة المتغيرة.

// bubble_graph.html
var chart = bubbleChart () ؛
bubbleChart () عرض ()؛ // يعود 600

قد تتساءل لماذا ترجع الطرق وظيفة المخطط. هذا هو نمط JavaScript المستخدم لتبسيط التعليمات البرمجية. يطلق عليه طريقة التسلسل. باستخدام هذا النمط ، يمكنك إنشاء كائنات جديدة مثل هذا:

// bubble_graph.html
var chart = bubbleChart (). العرض (600). الارتفاع (400) ؛

بدلا من:

// bubble_graph.html
var chart = bubbleChart () ؛
chart.setWidth (600)؛
chart.setHeight (400)؛

ربط البيانات مع الرسم البياني لدينا

الآن دعونا نتعلم كيفية ربط البيانات مع عناصر المخطط. إليك كيفية تنظيم المخطط: يحتوي div مع الرسم البياني على عنصر SVG ، وكل نقطة بيانات تتوافق مع دائرة في المخطط.

// bubble_graph.html ، بعد استدعاء الدالة bubbleChart ()

      // قصة من البيانات
      // قصة أخرى من البيانات
    ...

🗄 d3.data ()

ترجع الدالة d3.selection.data ([data [، key]]) تحديدًا جديدًا يمثل عنصرًا منضمًا بنجاح إلى البيانات. للقيام بذلك ، تحتاج أولاً إلى تحميل البيانات من ملف .csv. ستستخدم الدالة d3.csv (url [[، row] ، callback]).

// bubble_graph.html
d3.csv ('file.csv' ، دالة (خطأ ، بياناتنا) {
    var data = our_data؛ / / هنا يمكنك أن تفعل ما تريد مع البيانات
}
// medium_january.csv
| العنوان | الفئة | قلوب |
| -------------------------------------- | ---------- ---- | -------- |
| لا أحد يريد استخدام البرنامج | التنمية | 2700 |
| ضياع الملاحة على شبكة الإنترنت مع مسارات | تصميم | 688 |
| صعود مهندس البيانات | علوم البيانات | 862 |

🖍 D3 الاختيار

ستستخدم دالات select () و d3-data () لتمرير بياناتنا إلى المخطط.

تسمح التحديدات بتحويل قوي يستند إلى البيانات لنموذج كائن المستند (DOM): تعيين السمات والأنماط والخصائص ومحتوى HTML أو النص ، وأكثر من ذلك. - وثائق D3
// bubble_graph.html
d3.csv ('medium_january.csv' ، دالة (خطأ ، بياناتنا) {
    إذا (خطأ) {
        console.error ('خطأ في الحصول على أو تحليل البيانات.') ؛
        خطأ رمي
    }
    var chart = bubbleChart (). العرض (600). الارتفاع (400) ؛
    . d3.select ( '# الرسم البياني لل) البيانات (our_data) .call (الرسم البياني).
 })؛

محدد آخر مهم هو d3.selectAll (). لنفترض أن لديك البنية التالية:


    
    
    

d3.select ("body"). selectAll ("div") يختار كل تلك divs بالنسبة لنا.

3. d3.enter ()

والآن ستتعرف على وظيفة D3 مهمة: d3.enter (). دعنا نقول أن لديك علامة نص فارغة ومجموعة مع البيانات. تريد أن تذهب من خلال كل عنصر من عناصر مجموعة وإنشاء div جديد لكل عنصر. يمكنك القيام بذلك باستخدام الكود التالي:



 //فارغة
----
// شبيبة النصي
var our_data = [1 ، 2 ، 3]

var div = d3.select ("body")
 .selectAll ( "شعبة")
 .data (our_data)
 .أدخل()
 .append ( "شعبة")؛

---


    
    
    

لماذا تحتاج إلى selectAll ("div") إذا لم تكن divs موجودة حتى الآن؟ لأنه في D3 بدلاً من إخبارنا كيف نفعل شيئًا ما ، فإننا نخبر ما نريد.

في هذه الحالة ، تريد ربط كل div بعنصر من الصفيف. هذا ما تقوله مع selectAll ("div").

var div = d3.select ("body")
 .selectAll ("div") // هنا تقول "مرحبًا يا D3 ، كل عنصر من عناصر المصفوفة التي تأتي بعد ذلك سيكون مرتبطًا بـ div"
 .data (our_data)
 . .enter () إلحاق ( "شعبة")؛

تقوم بإدخال () بإرجاع التحديد مع ربط البيانات بعنصر المصفوفة. يمكنك أخيرًا إضافة هذا التحديد إلى DOM باستخدام .append ("div")

d3.forceSimulation ()

تحتاج إلى شيء لمحاكاة فيزياء الدوائر. لهذا سوف تستخدم d3.forceSimulation ([العقد]). تحتاج أيضًا إلى معرفة أي نوع من القوة سيغير موقف أو سرعة العقد.

في حالتنا ، سنستخدم d3.forceManyBody ().

// bubble_chart.js
فار المحاكاة = d3.forceSimulation (البيانات)
 .force ("شحن" ، d3.forceManyBody (). قوة ([- 50]))
 . Force ("x" ، d3.forceX ())
 . Force ("y"، d3.forceY ())
 .على ("علامة" ، علامة) ؛

تؤدي قيمة القوة الإيجابية إلى جذب العقد لبعضها البعض ، بينما تؤدي قيمة القوة السلبية إلى صد بعضها البعض.

قوة () تأثير

لا نريد انتشار العقد عبر مساحة SVG بأكملها ، لذلك نستخدم d3.forceX (0) وd3.forceY (0). هذا "يسحب" الدوائر إلى الموضع 0. المضي قدما وحاول إزالة هذا من التعليمات البرمجية لمعرفة ما يحدث.

عندما تقوم بتحديث الصفحة ، يمكنك أن ترى أن الدوائر تتكيف حتى تستقر في النهاية. تقوم الدالة ticked () بتحديث مواضع الدوائر. يحتفظ d3.forceManyBody () بتحديث موضع x و y لكل عقدة ، وتقوم الدالة ticked () بتحديث DOM بهذه القيم (سمات cx و cy).

// bubble_graph.js
وظيفة محددة (ه) {
    node.attr ("cx" ، الدالة (d) {return d.x؛})
        .attr ("cy" ، الدالة (d) {return d.y؛})؛
    "العقدة" هي كل دائرة من مخطط الفقاعة
 }

إليك الرمز مع كل شيء معًا:

فار المحاكاة = d3.forceSimulation (البيانات)
    .force ("شحن" ، d3.forceManyBody (). قوة ([- 50]))
    . Force ("x" ، d3.forceX ())
    . Force ("y"، d3.forceY ())
    .على ("علامة" ، علامة) ؛
وظيفة محددة (ه) {
    node.attr ("cx" ، الدالة (d) {return d.x؛})
        .attr ("cy" ، الدالة (d) {return d.y؛})؛
}

خلاصة القول ، كل هذا المحاكاة يفعل هو إعطاء كل دائرة وضع x و y.

3. d3.scales

هنا يأتي الجزء الأكثر إثارة: إضافة الدوائر فعليًا. تذكر وظيفة enter ()؟ سوف تستخدمه الآن. في الرسم البياني لدينا ، يتناسب نصف قطر كل دائرة مع عدد توصيات كل قصة. للقيام بذلك ، ستستخدم مقياس خطي: ​​d3.scaleLinear ()

لاستخدام المقاييس ، يلزمك تحديد شيئين:

  • المجال: الحد الأدنى والحد الأقصى لقيم بيانات الإدخال (في حالتنا ، الحد الأدنى والحد الأقصى للتوصيات). للحصول على الحد الأدنى والحد الأقصى للقيم ، ستستخدم الدالتين d3.min () و d3.max ().
  • النطاق: الحد الأدنى والحد الأقصى لقيم المخرجات في المقياس. في حالتنا ، نريد أصغر دائرة نصف قطرها حجم 5 وأكبر دائرة نصف قطرها حجم 18.
// bubble_graph.js
var scaleRadius = d3.scaleLinear ()
            .domain ([d3.min (data، function (d) {return + d.views؛})،
                    d3.max (البيانات ، الوظيفة (د) {return + d.views؛})])
            .range ([5،18])؛

ثم قمت أخيرًا بإنشاء الدوائر:

// bubble_graph.js
var node = svg.selectAll ("circle")
   .data (البيانات)
   .أدخل()
   .append ( "الدائرة")
   .attr ('r' ، الدالة (d) {return scaleRadius (d.views)})
})؛

لتلوين الدوائر ، ستستخدم مقياسًا قاطعًا: d3.scaleOrdinal (). هذا المقياس بإرجاع قيم منفصلة.

تحتوي مجموعة البيانات الخاصة بنا على 3 فئات: التصميم والتطوير وعلوم البيانات. سوف تقوم بتعيين كل فئة من هذه الفئات إلى لون. d3.schemeCategory10 يعطينا قائمة من 10 ألوان ، وهو ما يكفي بالنسبة لنا.

// bubble_graph.js
var colorCircles = d3.scaleOrdinal (d3.schemeCategory10) ؛

var node = svg.selectAll ("circle")
    .data (البيانات)
    .أدخل()
    .append ( "الدائرة")
    .attr ('r' ، الدالة (d) {return scaleRadius (d.views)})
    .style ("fill"، function (d) {return colorCircles (d.category)})؛

تريد الدوائر المرسومة في منتصف SVG ، لذلك ستنقل كل دائرة إلى المنتصف (نصف العرض ونصف الطول). المضي قدما وإزالة هذا من رمز لمعرفة ما يحدث.

// bubble_graph.js
var node = svg.selectAll ("circle")
 .data (البيانات)
 .أدخل()
 .append ( "الدائرة")
 .attr ('r' ، الدالة (d) {return scaleRadius (d.views)})
 .style ("fill" ، دالة (d) {return colorCircles (d.category)})
 .attr ('convert'، 'translate (' + [width / 2، height / 2] + ')')؛

الآن ستضيف تلميحات الأدوات إلى المخطط. يجب أن يظهروا كلما وضعنا الماوس فوق الدوائر.

فار تلميح = الاختيار
 .append ( "شعبة")
 .نمط ("الموضع" ، "المطلق")
 . نمط ("رؤية" ، "مخفية")
 .نمط ("اللون" ، "الأبيض")
 .style ("padding"، "8px")
 .style ("لون الخلفية" ، "# 626D71")
 . نمط ("نصف قطر الحدود" ، "6px")
 .style ("محاذاة النص" ، "الوسط")
 .style ("عائلة الخط" ، "monospace")
 . نمط ("العرض" ، "400 بكسل")
 .نص("")؛
var node = svg.selectAll ("circle")
 .data (البيانات)
 .أدخل()
 .append ( "الدائرة")
 .attr ('r' ، الدالة (d) {return scaleRadius (d.views)})
 .style ("fill" ، دالة (d) {return colorCircles (d.category)})
 .attr ('convert'، 'translate (' + [width / 2، height / 2] + ')')
 . على ("mouseover" ، الدالة (d) {
     tooltip.html (d.category + "
" + d.title + "
" + d.views)؛      إرجاع tooltip.style ("الرؤية" ، "المرئية") ؛})  . on ("mousemove" ، function () {    إرجاع tooltip.style ("top" ، (d3.event.pageY- 10) + "px"). style ("left" ، (d3.event.pageX + 10) + "px")؛})  .on ("mouseout"، function () {return tooltip.style ("visibility"، "hidden")؛})؛

يتبع mousemove المؤشر عندما يتحرك الماوس. إرجاع d3.event.pageX و d3.event.pageY إحداثيات الماوس.

وهذا كل شيء! تستطيع أن ترى الرمز النهائي هنا.

يمكنك أن تلعب مع الرسم البياني فقاعة هنا.

هل وجدت هذه المادة مفيدة؟ أنا أبذل قصارى جهدي لكتابة مقال غوص عميق كل شهر ، يمكنك تلقي رسالة بريد إلكتروني عند نشر واحدة جديدة.

أي أسئلة أو اقتراحات؟ ترك لهم في التعليقات. شكرا للقراءة!

شكر خاص لجون كارمايكل وألكسندر سيسنيروس.