Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
canvas { border: 1px solid #ccc; }
button {
display: inline-block;
position: absolute;
width: 6em;
margin: 0;
padding: .5em;
outline: none;
color: #555;
background-color: #f7f7f7;
border: 1px dotted #f0f0f0;
-webkit-transition: all 0.3s ease;
transition: all 0.3s ease;
cursor: pointer;
}
button:hover {
background-color: rgba(173, 206, 255, .7);
}
button#enter { top: 20px; left: 625px; }
button#update { top: 140px; left: 625px; }
button#exit { top: 260px; left: 625px; }
button#rerun { top: 377px; left: 625px; }
</style>
</head>
<body>
<canvas id="main-canvas" width="600" height="400"></canvas>
<button id="enter"><strong>Enter</strong><br> forms a cloud</button>
<button id="update"><strong>Update</strong><br> lets it rain</button>
<button id="exit"><strong>Exit</strong><br> makes grass</button>
<button id="rerun">Again</button>
<script>
/* Setting up canvas and context */
/* ============================= */
var canvas = d3.select('#main-canvas').node();
var context = canvas.getContext('2d');
/* Drawing the background */
/* ====================== */
function drawScene() {
context.save();
// The house
context.fillStyle = 'royalblue';
context.fillRect(50, 150, 200, 100);
// The door
context.fillStyle = 'rgba(255, 255, 255, 0.9)';
context.fillRect(60, 190, 40, 60);
// The window
context.save();
context.translate(140, 190);
context.fillRect(0, 0, 60, 30);
context.restore();
// The roof
context.beginPath();
context.moveTo(50, 150);
context.lineTo(250, 150);
context.lineTo(50+200/2, 100);
context.closePath();
context.fillStyle = '#A52A2A';
context.fill();
// The tree
context.beginPath();
context.lineWidth = 10;
context.strokeStyle = 'brown'
context.moveTo(300, 250);
context.lineTo(300, 125);
context.stroke();
context.beginPath();
context.fillStyle = 'green';
context.arc(300, 150, 25, 0, Math.PI * 2);
context.fill();
context.restore();
} // drawScene()
function drawRainScene() {
// cloud
// point cloud roughly from 60,10 to 540, 70
context.beginPath();
context.moveTo(65, 65);
context.lineTo(535, 65);
context.bezierCurveTo(560, 5, 441, 5, 441, 40);
context.bezierCurveTo(441, 0, 347, 0, 347, 40);
context.bezierCurveTo(347, 5, 253, 5, 253, 40);
context.bezierCurveTo(253, 10, 159, 10, 159, 40);
context.bezierCurveTo(159, 15, 40, 15, 65, 65);
context.closePath();
context.fillStyle = '#f7f7f7';
context.fill();
// Rain path
// cloud shape from 65, 65 to 535, 65
// puddle shape from 30, 250 to 560, 270
context.beginPath();
context.moveTo(65, 70);
context.lineTo(535, 70);
context.lineTo(530, 255);
context.lineTo(45, 253);
context. closePath();
context.fillStyle = '#ffffff';
context.fill();
// Puddle
context.beginPath();
context.moveTo(529.52,256.19);
context.bezierCurveTo(532.01,260.53,513.31,265.85,516.94,272.4);
context.bezierCurveTo(521.06,279.82,547.94,278.27,553.81,286.4);
context.bezierCurveTo(557.57,291.58,552.04,299.53,542.08,303.045);
context.bezierCurveTo(520.38,310.726,493.13,291.9,444.8,292.045);
context.bezierCurveTo(410.16,292.184,409.0,301.976,364.45,307.383);
context.bezierCurveTo(302.314,314.91,237.732,304.003,237.0,296.383);
context.bezierCurveTo(236.582,290.383,276.83,287.115,275.633,282.75);
context.bezierCurveTo(273.71,275.856,167.702,276.425,166.71,281.485);
context.bezierCurveTo(166.073,284.753,209.81,287.045,209.44,291.124);
context.bezierCurveTo(208.85,297.744,92.88,304.52,83.76,291.124);
context.bezierCurveTo(78.85,283.90,106.86,273.726,100.52,268.77);
context.bezierCurveTo(94.66,264.20,68.52,271.16,39.35,264.83);
context.bezierCurveTo(32.35,263.314,26.7303,261.36,25.118,258.26);
context.bezierCurveTo(23.78,255.71,34.735,251.362,43.54,250.814);
context.bezierCurveTo(56.446,250.982,453.02,250.753,458.2,250.883);
context.bezierCurveTo(506.06,249.68,525,248.36,529.52,256.19);
context.closePath();
context.fillStyle = "#ffffff";
context.fill();
} // drawRainScene()
/* Produce rain data */
/* ================= */
function getDrop(index) {
var x = Math.random() * (canvas.width * 0.9) + (canvas.width * 0.05);
var obj = {};
obj.index = index;
obj.xCloud = x;
obj.yCloud = Math.random() * 55 + 10;
obj.xPuddle = x;
obj.yPuddle = Math.random() * 60 + 253;
obj.radiusCloud = 1;
obj.radiusPuddle = 2;
obj.radiusGrass = 8;
return obj;
} // getDrop()
function getRainData(length, data) {
d3.range(length).forEach(function(el) {
var drop = getDrop(el);
data.push(drop);
});
return data;
} // getRainData()
/* Enter Exit Update and Draw the rain */
/* =================================== */
// Create in-memory base for elements
var customBase = document.createElement('custom')
var custom = d3.select(customBase);
function databind(data) {
var join = custom.selectAll('custom.drop')
.data(data, function(d) { return d.index; });
var enter = join
.enter().append('custom')
.attr('class', 'drop')
.attr('cx', function(d) { return d.xCloud; })
.attr('cy', function(d) { return d.yCloud; })
.attr('r', function(d) { return d.radiusCloud; })
.attr('fillStyle', 'rgba(0, 0, 255, 0')
.transition().delay(function(d, i) { return i * 2; })
.attr('fillStyle', 'rgba(0, 0, 255, 0.2');
var update = join
.transition()
.duration(function() { return Math.random() * 1000 + 900; })
.delay(function(d,i) { return (i / data.length) * dur; })
.ease(d3.easeLinear)
.attr('cx', function(d) { return d.xPuddle; })
.attr('cy', function(d) { return d.yPuddle; })
.attr('r', function(d) { return d.radiusPuddle; })
.attr('fillStyle', '#0000ff');
var exit = join
.exit().transition()
.duration(dur)
.delay(function(d,i) { return i ; })
.attr('r', function(d) { return d.radiusGrass; })
.attr('fillStyle', '#01A611');
} // databind()
function draw(ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRainScene();
drawScene();
var elements = custom.selectAll('custom.drop');
elements.each(function(d, i) {
var node = d3.select(this);
ctx.save();
ctx.beginPath();
ctx.globalCompositeOperation = 'source-atop'
ctx.fillStyle = node.attr('fillStyle');
ctx.arc(node.attr('cx'), node.attr('cy'), node.attr('r'), 0, 2 * Math.PI);
ctx.fill();
ctx.restore();
});
} // draw()
/* Draw the initial scene */
/* ====================== */
drawRainScene();
drawScene();
/* Animate */
/* ======= */
var max = 2500;
var raindrops = [];
var rainData = getRainData(max, raindrops);
var dur = 2000;
var running = false;
function rainAnimation(data) {
databind(data);
var t = d3.timer(function(elapsed) {
running = elapsed > 1 && elapsed < dur * 2 ? true : false;
draw(context);
d3.selectAll('button').style('color', '#aaa');
if (elapsed > dur * 2) {
d3.selectAll('button').style('color', '#555');
t.stop();
}
});
} // rainAnimation()
/* Interact */
/* ======== */
d3.select('button#enter').on('mousedown', enterRain);
d3.select('button#update').on('mousedown', updateRain);
d3.select('button#exit').on('mousedown', exitRain);
function enterRain() {
if (!running) rainAnimation(rainData);
};
function updateRain() {
if (!running) rainAnimation(rainData);
};
function exitRain() {
if (!running) rainAnimation([]);
};
d3.select('button#rerun').on('mousedown', function() {
location.reload();
});
</script>
</body>
https://d3js.org/d3.v4.min.js