Wie erstelle ich eine reaktionsschnelle Canvas-Animation mit der D3-Bibliothek?
Ich entwickle eine einfache Animation mit der Bibliothek d3.js und zeige sie über die Leinwand, aber meine Leinwand reagiert nicht.
Zu Beginn des Codes habe ich die ursprüngliche Breite auf 960 und die Höhe auf 500 festgelegt. Wie kann ich die Größe ändern (ansprechend)? Ich verwende kein CSS, um der Zeichenfläche einen bestimmten Stil (Breite und Höhe) zuzuweisen. Dazu verwende ich JavaScript. Die Lösung kann CSS oder JavaScript sein.
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>