Published
Edited
Jul 13, 2022
Insert cell
md`# Animate drawing of a map`
Insert cell
x = html`<div style="position: relative;">
${c}
${c2}
</div>`
Insert cell
Insert cell
Insert cell
Insert cell
{
c2.height = 2160;
c2.width = 3840;
const size = 4;
ctx2.clearRect(0, 0, 3840, 2160)
c2.style.height = `${c2.height / size}px`;
c2.style.width = `${c2.width / size}px`;
x.style.height = `${c2.height / size}px`;
x.style.width = `${c2.width / size}px`;
// c2.style.background = '#2D3142'
ctx2.lineWidth = size
ctx2.strokeStyle = 'rgba(82, 235, 179, 1)'
ctx2.fillStyle = 'rgba(0, 0, 0, 0.9)'
ctx2.lineJoin = 'round'
}
Insert cell
{
c.height = 2160;
c.width = 3840;
const size = 4;
c.style.height = `${c.height / size}px`;
c.style.width = `${c.width / size}px`;
c.style.background = '#2D3142'
ctx.lineWidth = size
ctx.strokeStyle = '#050505'
ctx.lineJoin = 'round'
// ctx.stroke(p);
// animateSegment(ctx, 500, 300, 1100, 800, 1000)
async function d() {
ctx.clearRect(0, 0, 3840, 2160)
const s = 1000;
const l = f.features[0].geometry.coordinates;
await animatePath2(l[1][0], ctx, s)
await animatePath2(l[1][1], ctx, s)
await animatePath2(l[0][0], ctx, s)
await animatePath2(l[0][1], ctx, s)
await animatePath2(l[3][0], ctx, s)
await animatePath2(l[2][0], ctx, s)
await animatePath2(l[2][1], ctx, s)
for(let i = 0; i<20; i = i + 1) {
ctx2.globalCompositeOperation = 'destination-in';
ctx2.fillRect(0, 0, 3840, 2160);
ctx2.globalCompositeOperation = 'source-over';
// mergeCanvas()
}
setTimeout(d, 1500)
}
d()
// f.features[0].geometry.coordinates[0][0].forEach((c) => animateSegment(ctx, c[0], c[1]))
}
Insert cell
ctx = c.getContext('2d');
Insert cell
ctx2 = c2.getContext('2d');
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3-geo@1")
Insert cell
water = FileAttachment("water@3.topojson").json()
Insert cell
f = topojson.feature(water, '$dissolved');
Insert cell
t = (point) => {
return [point[0] - water.transform.translate[0], point[1] - water.transform.translate[1]]
}
Insert cell
Insert cell
Insert cell
function animatePath2(path, context, speed) {
// ctx.strokeStyle = '#AAAAAA'
return new Promise((resolve) => {
// const pathDistance = path.map((coordPair, index, arr) => {
// if (index + 1 < arr.length) {
// return segmentDistance(
// coordPair[0],
// coordPair[1],
// arr[index + 1][0],
// arr[index + 1][1],
// )
// }
// return 0;
// }).reduce((sum, num) => sum + num, 0);
// const numberOfFrames = pathDistance / speed * 60;
const distancePerFrame = speed / 60;
function draw(prevX, prevY, nextX, nextY) {
const s = t([prevX, prevY])
const e = t([nextX, nextY])
// context.lineTo(s[0], s[1]);
context.lineTo(e[0], e[1]);
}
function frameDraw(coords) {
coords.forEach((coord, index) => {
if (index <= coords.length - 2) {
draw(coords[index][0], coords[index][1], coords[index + 1][0], coords[index + 1][1])
}
});
}
const chunks = [];
let reducedPath = path;
let chunking = true;
while (chunking) {
const c = chunkPath(reducedPath, distancePerFrame);
chunks.push(c.chunk);
reducedPath = c.reducedPath;
chunking = c.end === false;
}
let chunkIndex = 0;
function chunkLoop() {
frameDraw(chunks[chunkIndex]);
if (chunkIndex + 1 < chunks.length) {
context.stroke();
window.requestAnimationFrame(chunkLoop);
chunkIndex = chunkIndex + 1;
} else {
context.fillStyle = '#2D3142'
// context.fillStyle = '#FFFFFF'
ctx.fill();
ctx.globalCompositeOperation = 'source-atop';
context.shadowBlur = 25;
context.shadowColor = '#000000';
context.beginPath();
context.moveTo(...t(path[0][0], path[0][0]));
frameDraw(path);
ctx.lineWidth = 2.5
ctx.strokeStyle = '#131313'
context.stroke();
context.stroke();
context.closePath();
ctx.globalCompositeOperation = 'source-over';
context.shadowBlur = null;
context.shadowColor = null;
ctx.lineWidth = 1
context.stroke();
ctx.lineWidth = c.height / parseInt(c.style.height.substring(0, c.style.height.length - 1), 10);
resolve();
}
highlight(chunks[chunkIndex]);
// mergeCanvas();
}
context.beginPath();
context.moveTo(...t(chunks[0][0][0], chunks[0][0][1]));
chunkLoop();
});
}
Insert cell
function highlight(chunk) {
ctx2.globalCompositeOperation = 'destination-in';
ctx2.fillRect(0, 0, 3840, 2160);
ctx2.globalCompositeOperation = 'source-over';
function draw(prevX, prevY, nextX, nextY) {
const s = t([prevX, prevY])
const e = t([nextX, nextY])
ctx2.lineTo(e[0], e[1]);
}
function frameDraw(coords) {
coords.forEach((coord, index) => {
if (index <= coords.length - 2) {
draw(coords[index][0], coords[index][1], coords[index + 1][0], coords[index + 1][1])
}
});
}
ctx2.beginPath();
ctx2.moveTo(...t([chunk[0][0], chunk[0][1]]));
frameDraw(chunk);
// const c = t(chunk[0][0], chunk[0][1])
// ctx2.arc(c[0], c[1], 8, 0, Math.PI * 2, true);
// ctx2.closePath();
ctx2.stroke();
// ctx2.fillStyle = 'rgba(82, 235, 179, 0.5)';
// ctx2.fill();
// ctx2.fillStyle = 'rgba(0, 0, 0, 0.7)';
}
Insert cell
// function mergeCanvas() {
// ctx3.clearRect(0, 0, 3840, 2160);
// // const l = ctx.getImageData(0, 0, 3840, 2160);
// // const h = ctx2.getImageData(0, 0, 3840, 2160);
// // ctx3.putImageDate(l,0,0);
// // ctx3.putImageDate(h,0,0);
// ctx3.drawImage(c, 0, 0);
// ctx3.drawImage(c2, 0, 0);
// }
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more