pie chart

browserSVGexamples ⟩ pie chart

💾 replit: pie chart, SVGElemnt()

⬆️ 需要: SVGElement()

// ⭐ SVGElement
function SVGElement(tagName, attributes, config) {
    // create SVG element (<svg>, <path>, <rect>, <text> ...)
    let elem = document.createElementNS("http://www.w3.org/2000/svg", tagName);
    // set attributes (non-string value is converted automatically into string)
    Object.entries(attributes).forEach(([key, value]) => elem.setAttribute(key, value));
    // furthur configuration if necessary
    if (config) config(elem);
    // return the element
    return elem;
}

/** 
 * Create an <svg> element and draw a pie chart into it.
 *
 * first (only) parameter: an object with the following properties:
 * - width, height: size of SVG (in pixels)
 * - cx, cy, r    : center and radius of the pie
 * - lx, ly       : upper-left corner of the chart legend
 * - data         : dictionary object with `label: value` pairs
 *
 * The function returns an <svg> element. The caller must insert it into
 * the document in order to make it visible.
 */
function pieChart(options) {

    let { width, height, cx, cy, r, lx, ly, data } = options;

    // -----------
    //     SVG
    // -----------

    // ⭐ create chart with <svg> element
    let chart = SVGElement('svg', {
        width: width, height: height, 
        viewBox: `0 0 ${width} ${height}`,
        // text styles for the chart (can be set with CSS instead)
        'font-family': "sans-serif",
        'font-size': 18,
    });

    // Get labels and values as arrays  
    let labels = Object.keys(data);
    let values = Object.values(data);
    
    // sum of the values
    let total = values.reduce((x, y) => x + y);

    // angles for all slices
    // slice i: angles[i] ~ angles[i+1] (in radians)
    let angles = [0];
    values.forEach((x, i) => angles.push(angles[i] + x / total * 2 * Math.PI));

    // loop through the slices of the pie 
    values.forEach((value, i) => {

        // two points for slice arc 
        // angle of 0 is at 12 o'clock (increase clockwise)
        let x1 = cx + r * Math.sin(angles[i]);
        let y1 = cy - r * Math.cos(angles[i]);
        let x2 = cx + r * Math.sin(angles[i + 1]);
        let y2 = cy - r * Math.cos(angles[i + 1]);

        // flag for angles larger than a half circle 
        // required by the SVG arc drawing component 
        let big = (angles[i + 1] - angles[i] > Math.PI) ? 1 : 0;

        // commands to draw a slice:
        let path = `M${cx},${cy}` +                  // Move to circle center.
            `L${x1},${y1}` +                         // Line to (x1,y1).
            `A${r},${r} 0 ${big} 1 ${x2},${y2}` +    // Arc of radius r to (x2,y2).
            "Z";                                     // cloZe path back to (cx,cy).

        // CSS color for this slice. This formula works for only
        // about 15 colors. So don't include more than 15 slices in a chart.
        let color = `hsl(${(i * 40) % 360},${90 - 3 * i}%,${50 + 2 * i}%)`;

        // --------------
        //     <path>
        // --------------

        // ⭐ create a slice with <path> element
        let slice = SVGElement('path', {
            d: path, fill: color, stroke: 'black', 'stroke-width': 1,
        });

        chart.append(slice);                       // ⭐ add slice to chart

        // --------------
        //     <rect>
        // --------------

        // ⭐ legend icon square for each slice
        let icon = SVGElement('rect', {
            x: lx, y: ly + 30 * i,        // position
            width: 20, height: 20,        // size
            fill: color, stroke: 'black', 'stroke-width': 1,
        });
        
        chart.append(icon);                     // ⭐ add to chart

        // --------------
        //     <text>
        // --------------

        // ⭐ legend label    
        let label = SVGElement('text', {
            x: lx + 30, y: ly + 30 * i + 16,          // position
        });
        
        label.append(`${labels[i]} (${value})`);      // ⭐ add text to label 
        chart.append(label);                          // ⭐ add label to chart 
    });

    return chart;
}

// main
document.querySelector("#chart").append(pieChart({
    
    // chart size
    width: 640, height: 400, 
    
    // center/radius of pie
    cx: 200, cy: 200, r: 180,
    
    // legend position
    lx: 400, ly: 10,
    
    // chart data
    data: { 
        "JavaScript": 71.5,
        "Java"      : 45.4,
        "Bash/Shell": 40.4,
        "Python"    : 37.9,
        "C#" : 35.3,
        "PHP": 31.4,
        "C++": 24.6,
        "C"  : 22.1,
        "TypeScript" : 18.3,
        "Ruby"       : 10.3,
        "Swift"      : 8.3,
        "Objective-C": 7.3,
        "Go": 7.2,
    }
    
}));

Last updated