如何使用D3库制作响应式画布动画

How to make a responsive canvas animation using the D3 library

本文关键字:布动画 动画 响应 D3 何使用      更新时间:2023-09-26

我正在使用d3.js库开发一个简单的动画,并通过画布显示它,但是我的画布没有响应。

在代码的开始,我将原始宽度设置为960,高度设置为500,但我如何使其可调整大小(响应)?我没有使用CSS将一些样式(宽度和高度)应用到画布上,我使用JavaScript来完成它。解决方案可以是CSS或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>

Canvas就像一个图像。它有自己的物理尺寸,但仍然可以用CSS缩放。使用widthheight属性设置物理尺寸,然后使用CSS缩放%,使其响应。

例子…

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>