円落下の JavaScript 版

obelisk.hatenablog.comここの JavaScript 版です。ボタンをクリックしてみて下さい。

コード。

<script type="text/javascript">
function circleFall(width, height) {
    var w = window.open("", null, "width=" + width + ",height=" + height);
    w.focus();
    w.document.open();
    var st = `
    <!DOCTYPE html>
    <html lang="ja">
    <title>Canvas</title>
    <body style="margin: 0; overflow: hidden;">
    <canvas id="circleFallCanvas" width="${width}" height="${height}"></canvas>
    </body>
    </html>
`;
    w.document.write(st);
    w.document.close();
    
    var canvas = w.document.getElementById("circleFallCanvas");
    var wd = w.innerWidth; var ht = w.innerHeight;    //Chrome は 0 を返す
    if (wd != 0) {width  = wd;}
    if (ht != 0) {height = ht;}
    canvas.width  = width;
    canvas.height = height;
    var context = canvas.getContext("2d");
    
    var max_r, min_r, colorMax, maxNum;
    max_r = 40; min_r = 10;
    colorMax = 256;
    maxNum = 60;
    
    var Circle = function(f) {
        function rnd(num) {return Math.random() * num;}
        
        this.maxR = rnd(max_r - min_r) + min_r;
        this.x = rnd(width);
        this.color = "rgb(" + String(rnd(colorMax)) + "," + String(rnd(colorMax)) +
                          "," + String(rnd(colorMax)) + ")";
        this.fallStep = rnd(3) + 1;
        this.r = 1;
        this.r_step = rnd(0.8) + 0.2;
        if (f) {
            this.y = rnd(height);
        } else {
            this.y = -rnd(max_r);
        }
        
        this.paint = function() {
            context.beginPath();
            context.fillStyle = this.color;
            context.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
            context.fill();
            this.y += this.fallStep;
            this.r += this.r_step;
            if (this.r > this.maxR || this.r < 1) {this.r_step *= -1}
            if (this.y > height + max_r) {return true}
            return false;
        }
    }
    
    var circles = [];
    for (var i = 0; i < maxNum; i++) {circles[i] = new Circle(true);}
    
    window.draw = function() {
        context.fillStyle = "black";
        context.fillRect(0, 0, width, height);
        
        for (var i = 0; i < maxNum; i++) {
            if (circles[i].paint()) {circles[i] = new Circle(false);}
        }
    }
    
    var id = setInterval("draw()", 80);
    function bul() {clearInterval(id);}
    w.onbeforeunload = bul;
}
</script>
<form><input type="button" value="アニメーション" onclick="circleFall(1000, 700)">
 <input type="button" value="(小さい版)" onclick="circleFall(800, 600)"></form>