1

I'm developing a simple animation using d3.js library and show it through canvas, but my canvas isn't responsive.

In the beginning of the code, I set original width to 960 and height to 500, but how can I make it resizable (responsive)? I'm not using CSS to apply some style (width and height) to the canvas, I'm using JavaScript to do it. The solution can be CSS or JavaScript.

var width = 960,
    height = 500,
    τ = 2 * Math.PI,
    gravity = .05;

var sample = poissonDiscSampler(width, height, 30),
    nodes = [{
        x: 0,
        y: 0
    }],
    s;

while (s = sample()) nodes.push(s);

var force = d3.layout.force()
    .size([width, height])
    .nodes(nodes.slice())
    .gravity(0)
    .charge(function(d, i) {
        return i ? -30 : -3000;
    })
    .on("tick", ticked)
    .start();

var voronoi = d3.geom.voronoi()
    .x(function(d) {
        return d.x;
    })
    .y(function(d) {
        return d.y;
    });

var root = nodes.shift();

root.fixed = true;

var links = voronoi.links(nodes);

var canvas = d3.select("#canvas").append("canvas")
    .attr("width", width)
    .attr("height", height)
    .on("ontouchstart" in document ? "touchmove" : "mousemove", moved);

var context = canvas.node().getContext("2d");



function moved() {
    var p1 = d3.mouse(this);
    root.px = p1[0];
    root.py = p1[1];
    force.resume();
}

function ticked() {
    force.resume();

    for (var i = 0, n = nodes.length; i < n; ++i) {
        var node = nodes[i];
        node.y += (node.cy - node.y) * gravity;
        node.x += (node.cx - node.x) * gravity;
    }

    context.clearRect(0, 0, width, height);

    context.beginPath();
    for (var i = 0, n = links.length; i < n; ++i) {
        var link = links[i];
        context.moveTo(link.source.x, link.source.y);
        context.lineTo(link.target.x, link.target.y);
    }
    context.lineWidth = 1;
    context.strokeStyle = "#bbb";
    context.stroke();

    context.beginPath();
    for (var i = 0, n = nodes.length; i < n; ++i) {
        var node = nodes[i];
        context.moveTo(node.x, node.y);
        context.arc(node.x, node.y, 2, 0, τ);
    }
    context.lineWidth = 3;
    context.strokeStyle = "#fff";
    context.stroke();
    context.fillStyle = "#000";
    context.fill();
}

// Based on https://www.jasondavies.com/poisson-disc/
function poissonDiscSampler(width, height, radius) {
    var k = 30, // maximum number of samples before rejection
        radius2 = radius * radius,
        R = 3 * radius2,
        cellSize = radius * Math.SQRT1_2,
        gridWidth = Math.ceil(width / cellSize),
        gridHeight = Math.ceil(height / cellSize),
        grid = new Array(gridWidth * gridHeight),
        queue = [],
        queueSize = 0,
        sampleSize = 0;

    return function() {
        if (!sampleSize) return sample(Math.random() * width, Math.random() * height);

        // Pick a random existing sample and remove it from the queue.
        while (queueSize) {
            var i = Math.random() * queueSize | 0,
                s = queue[i];

            // Make a new candidate between [radius, 2 * radius] from the existing sample.
            for (var j = 0; j < k; ++j) {
                var a = 2 * Math.PI * Math.random(),
                    r = Math.sqrt(Math.random() * R + radius2),
                    x = s.x + r * Math.cos(a),
                    y = s.y + r * Math.sin(a);

                // Reject candidates that are outside the allowed extent,
                // or closer than 2 * radius to any existing sample.
                if (0 <= x && x < width && 0 <= y && y < height && far(x, y)) return sample(x, y);
            }

            queue[i] = queue[--queueSize];
            queue.length = queueSize;
        }
    };

    function far(x, y) {
        var i = x / cellSize | 0,
            j = y / cellSize | 0,
            i0 = Math.max(i - 2, 0),
            j0 = Math.max(j - 2, 0),
            i1 = Math.min(i + 3, gridWidth),
            j1 = Math.min(j + 3, gridHeight);

        for (j = j0; j < j1; ++j) {
            var o = j * gridWidth;
            for (i = i0; i < i1; ++i) {
                if (s = grid[o + i]) {
                    var s,
                        dx = s.x - x,
                        dy = s.y - y;
                    if (dx * dx + dy * dy < radius2) return false;
                }
            }
        }

        return true;
    }

    function sample(x, y) {
        var s = {
            x: x,
            y: y,
            cx: x,
            cy: y
        };
        queue.push(s);
        grid[gridWidth * (y / cellSize | 0) + (x / cellSize | 0)] = s;
        ++sampleSize;
        ++queueSize;
        return s;
    }
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<section>

    <div id="canvas">

    </div>

</section>
BSMP
  • 4,596
  • 8
  • 33
  • 44
Wesley Heron
  • 413
  • 2
  • 12
  • 27

1 Answers1

1

Canvas is like an image. It has it's own physical dimensions but can still be scaled with CSS. Use the width and height attributes to set the physical dimensions and then use CSS to scale it with a % to make it responsive.

Example...

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.rect(20, 20, 150, 100);
ctx.stroke();
<canvas id="myCanvas" width="300" height="150" style="width:100%;"></canvas>
Robert Parham
  • 704
  • 3
  • 10
  • the problem in this case is cause the javascript file that set width and height and it overrides all css – Wesley Heron Oct 04 '16 at 16:41
  • @coreid - as demonstrated in the example, the canvas width and height do not override css width and height. they are two different things and they are intended to be used together. – Robert Parham Oct 04 '16 at 16:47
  • in other words, all you have to do to make the canvas responsive (regardless of the canvas' width and height ) is to set a width (using CSS) to a percentage. just add `style='width:100%;'` to the canvas itself and it will be responsive. – Robert Parham Oct 04 '16 at 16:51
  • I undestand you! But i created a css style for it. set `width: 100%` and `height: 500px;` but, only works because js file overrides everthing – Wesley Heron Oct 04 '16 at 18:01
  • @coreid - please make a fiddle demonstrating this. – Robert Parham Oct 04 '16 at 18:04