D3 Canvas Scatter

Creating a scatter chart using canvas and D3

by IPWright83

HTML

<div id="vis">
</div>
<ul>TODO
    <li>Use Shadow DOM for events <a href="https://engineering.mongodb.com/post/d3-round-two-how-to-blend-html5-canvas-with-svg-to-speed-up-rendering">See here</a></li>
    <li>d3.drag</li>
    <li>Other styles that can be applied (stroke dasharray etc)</li>
    <li>Transitions <a href="https://bocoup.com/blog/d3js-and-canvas">Maybe Useful Link</a></li>
    <li>Render a Rectangle</li>
    <li>Render a Path</li>
    <li>Render an Area</li>
    <li>Render a Line</li>
    <li>Mix with SVG Axis <a href="http://bl.ocks.org/syntagmatic/2409451">Example</a></li>
</ul>

CSS

canvas {
    border: thin solid red;
}

JavaScript

const DURATION = 1000;

const renderCanvas = function(context, join) {
    const renderCircle = (node) => {
        const selection = d3.select(node);
        const cx = selection.attr("cx");
        const cy = selection.attr("cy");
        const r = selection.attr("r");
        const fill = selection.style("fill");
        const stroke = selection.style("stroke");
        const strokeWidth = selection.style("stroke-width") || 1;

        context.beginPath();
        context.arc(cx, cy, r, 0, 2 * Math.PI);

        if (fill) {
            context.fillStyle = fill;
            context.fill();
        }

        if (stroke) {
            context.strokeStyle = stroke;
            context.lineWidth = strokeWidth;
            context.stroke();
        }
    };

    join.each((d, i, elements) => {
        const node = elements[i];
        switch (node.nodeName) {
            case "CIRCLE":
                renderCircle(node);
        }
    });
}

class CanvasLayer {
		test() { console.log("CANVAS"); }
}

class SVGLayer {
	test() { console.log("SVG"); }
}

class Layer {
	constructor(canvas) {
  	  if(canvas) this.prototype = CanvasLayer;
      else this.prototype = SVGLayer;
  }
}


class Scatter extends Layer {

    constructor() {
    		super(true);
        //this.test();
        this.custom = document.createElement("custom");
        this.bind = d3.select(this.custom);
        return this;
    }

    append(target) {
        this.target = d3.select(target);
        this.container = this.target
            .append("canvas")
            .attr("width", this.width)
            .attr("height", this.height);

        this.context = this.container.node().getContext("2d");
        return this;
    }

    render() {
        let timer = null;
        const start = () => {
            timer = d3.timer(() => {
                this.context.clearRect(0, 0, this.width, this.height);
                renderCanvas(this.context, update);
                renderCanvas(this.context,...