Ajax Loader
HTML
<canvas id="c"></canvas>
1
<canvas id="c"></canvas>
2
 
3
 
4
 
5
 
6
 
7
<script src="http://dat-gui.googlecode.com/git/build/dat.gui.min.js"></script>
 
CSS
body {
1
body {
2
  font-family: sans-serif;
3
  padding: 0;
4
  margin: 0;
5
  background-color: #f5f5f5;
6
  overflow: hidden;
7
}
 
JavaScript
/**
1
/**
2
 * requestAnimationFrame
3
 */
4
var requestAnimationFrame = (function(){
5
    return  window.requestAnimationFrame       ||
6
            window.webkitRequestAnimationFrame ||
7
            window.mozRequestAnimationFrame    ||
8
            window.oRequestAnimationFrame      ||
9
            window.msRequestAnimationFrame     ||
10
            function (callback) {
11
                window.setTimeout(callback, 1000 / 60);
12
            };
13
})();
14
 
15
/**
16
 * Delaunay
17
 */
18
var Delaunay = (function() {
19
 
20
    /**
21
     * Node
22
     * @public
23
     */
24
    function Node(x, y, id) {
25
        this.x = x;
26
        this.y = y;
27
        this.id = !isNaN(id) && isFinite(id) ? id : null;
28
    }
29
 
30
    Node.prototype = {
31
        eq: function(p) {
32
            var dx = this.x - p.x,
33
                dy = this.y - p.y;
34
            return (dx < 0 ? -dx : dx) < 0.0001 && (dy < 0 ? -dy : dy) < 0.0001;
35
        },
36
 
37
        toString: function() {
38
            return '(x: ' + this.x + ', y: ' + this.y + ')';
39
        }
40
    };
41
 
42
    /**
43
     * Edge
44
     */
45
    function Edge(p0, p1) {
46
        this.nodes = [p0, p1];
47
    }
48
 
49
    Edge.prototype = {
50
        eq: function(edge) {
51
            var na = this.nodes, nb = edge.nodes;
52
            var na0 = na[0], na1 = na[1], nb0 = nb[0], nb1 = nb[1];
53
            return (na0.eq(nb0) && na1.eq(nb1)) || (na0.eq(nb1) && na1.eq(nb0));
54
        }
55
    };
56
 
57
    /**
58
     * Triangle
59
     */
60
    function Triangle(p0, p1, p2,p3) {
61
        this.nodes = [p0, p1, p2, p3];
62
        this.edges = [new Edge(p0, p1,p3,p2), new Edge(p1, p2,p0,p3), new Edge(p2, p0,p3,p1)];
63
        this._createId();
64
        this._createCircumscribedCircle();
65
    }
66
 
67
    Triangle.prototype = {
68
        id: null,
69
        _circle: null,
70
 
71
        _createId: function() {
72
            var nodes, id0, id1, id2, id3 ;
73
 
74
            nodes = this.nodes;
75
            id0 = nodes[0].id;
76
            id1 = nodes[1].id;
77
            id2 = nodes[2].id;
78
           
79
 
80
            if (id0 !== null && id1 !== null && id2 !== null && id3 !== null) {
81
                this.id = [id0, id1, id2, id3 ].sort().join('_');
82
            }
83
        },
84
 
85
        _createCircumscribedCircle: function() {
86
            var nodes, p0, p1, p2,p3,
87
                ax, bx, cx, ty, uy,
88
                circle, dx, dy,cx;
89
 
90
            nodes = this.nodes;
91
            p0 = nodes[0];
92
            p1 = nodes[1];
93
            p2 = nodes[2];
94
            p3 = nodes[3];
95
 
96
            ax = p1.x - p0.x, ay = p1.y - p0.y;
97
            bx = p2.x - p0.x, by = p2.y - p0.y;
98
            c = 2 * (ax * by - ay * bx);
99
 
100
            t = (p1.x * p1.x - p0.x * p0.x + p1.y * p1.y - p0.y * p0.y);
101
            u = (p2.x * p2.x - p0.x * p0.x + p2.y * p2.y - p0.y * p0.y);
102
 
103
            if (!this._circle) this._circle = {};
104
 
105
            circle = this._circle;
106
            circle.x = ((p2.y - p0.y) * t + (p0.y - p1.y) * u) / c;
107
            circle.y = ((p0.x - p2.x) * t + (p1.x - p0.x) * u) / c;
108
 
109
            dx = p0.x - circle.x;
110
            dy = p0.y - circle.y;
111
            circle.radiusSq = dx * dx + dy * dy;
112
        },
113
 
114
        circleContains: function(p) {
115
            var circle, dx, dy, distSq;
116
 
117
            circle = this._circle;
118
            dx = circle.x - p.x,
119
            dy = circle.y - p.y;
120
            distSq = dx * dx + dy * dy;
121
 
122
            return distSq < circle.radiusSq;
123
        }
124
    };
125
 
126
 
127
    /**
128
     * @constructor
129
     * @public
130
     */
131
    function Delaunay(width, height) {
132
        this.width = width = height ;
133
        this.height = height = width ;
134
 
135
        this._triangles = null;
136
 
137
        this.clear();
138
    }
139
 
140
    Delaunay.prototype = {
141
 
142
        clear: function() {
143
            var p0 = new Node(0, 0),
144
                p1 = new Node(this.width, 0),
145
                p2 = new Node(this.width, this.height),
146
                p3 = new Node(0, this.height);
147
 
148
            this._triangles = [
149
                new Triangle(p0, p1, p2),
150
                new Triangle(p0, p2, p3),
151
                new Triangle(p0, p1, p3)
152
 
153
            ];
154
 
155
            return this;
156
        },
157
 
158
        multipleInsert: function(m) {
159
            for (var i = 1, len = m.length; i < len; i++) {
160
                this.insert(m[i]);
161
            }
162
 
163
            return this;
164
        },
165
 
166
        insert: function(p) {
167
            var triangles = this._triangles,
168
                t,
169
                temps = [],
170
                edges = [],
171
                edge,
172
                polygon = [],
173
                isDuplicate,
174
                i, ilen, j, jlen;
175
 
176
            for (ilen = triangles.length, i = 0; i < ilen; i++) {
177
                t = triangles[i];
178
 
179
                if (t.circleContains(p)) {
180
                    edges.push(t.edges[0], t.edges[1], t.edges[2]);
181
                } else {
182
                    temps.push(t);
183
                }
184
            }
185
 
186
            edgesLoop: for (ilen = edges.length, i = 0; i < ilen; i++) {
187
                edge = edges[i];
188
 
189
                for (jlen = polygon.length, j = 0; j < jlen; j++) {
190
                    if (edge.eq(polygon[j])) {
191
                        polygon.splice(j, 1);
192
                        continue edgesLoop;
193
                    }
194
                }
195
 
196
                polygon.push(edge);
197
            }
198
 
199
            for (ilen = polygon.length, i = 0; i < ilen; i++) {
200
                edge = polygon[i];
201
                temps.push(new Triangle(edge.nodes[0], edge.nodes[1], p));
202
            }
203
 
204
            this._triangles = temps;
205
 
206
            return this;
207
        },
208
 
209
        getTriangles: function() {
210
            return this._triangles.slice();
211
        }
212
    };
213
 
214
    Delaunay.Node = Node;
215
 
216
    return Delaunay;
217
 
218
})();
219
 
220
 
221
/**
222
 * Particle
223
 * @super Delaunay.Node
224
 */
225
var Particle = (function(Node) {
226
 
227
    var currentId = 0,
228
        getId = function() { return currentId++; };
229
 
230
    function Particle(x, y, z) {
231
        Node.call(this, x, y, getId());
232
        this.vx = 0;
233
        this.vy = 0;
234
    }
235
 
236
    Particle.prototype = new Node();
237
 
238
    return Particle;
239
 
240
})(Delaunay.Node);
241
 
242
 
243
// Initialize
244
 
245
(function() {
246
 
247
    // Configs
248
 
249
    var BACKGROUND_COLOR = '#eff1ea', // 背景色
250
        LINE_COLOR = '#303030', // 線の色
251
        FILL_COLORS = [ // 塗りに使用する色, 三角形の生成順に選択される
252
            '#666', '#F5F5F5', '#ccc', '#666', '#F5F5F5', '#ccc','#999', '#F5F5F5', '#ccc','#333', '#F5F5F5', '#fff','#333'
253
        ],
254
        PATTERNS_URL = [ // パターンの画像 URL, 三角形の生成順に選択される
255
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAChJREFUeNpiVFBQ2M8ABPfv33dkQAJMDDgA4////7FK4NRBugRAgAEAXhEHBXvZgh4AAAAASUVORK5CYII%3D',
256
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAB1JREFUeNpiUFBQ2P///38GEGYEETDAxIAEAAIMACllChoZz6oRAAAAAElFTkSuQmCC',
257
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1JREFUeNpiVFBQ2M/AwODIgAAgPgMTNkGYBIYgSDETNkGYDgxBdKNQ7AIIMABhpgcrohF6AgAAAABJRU5ErkJggg%3D%3D',
258
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADJJREFUeNpi+P//PwMyVlBQ2M/EgAQUFRX3AylHJnQBEJsJXQAEGEFmIAvcv3+fASDAANwmFUHSvnUvAAAAAElFTkSuQmCC',
259
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAGCAYAAADkOT91AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpiUFBQ2P///38GGGZiQAOEBRhB+vCqAAgwAAmADR3HFFILAAAAAElFTkSuQmCC',
260
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAKCAYAAACJxx+AAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEdJREFUeNpiUlBQ2A/EDP///wdjdD4jiAME+4HYERvNxAAByIIofEaQMYqKigy4ANiE+/fvwwVAbGQ+A50ciQ8wMRAAAAEGAKNCOWlhLo6PAAAAAElFTkSuQmCC'
261
        ];
262
 
263
 
264
    // Vars
265
 
266
    var canvas, context,
267
        screenWidth, screenHeight, screenMinSize,
268
        centerX, centerY,
269
        delaunay,
270
        particles = [],
271
        colorIndex = 10,
272
        colorTable = {},
273
        patterns = [],
274
        patternIndex = 0,
275
        patternTable = {},
276
        backgroundPattern,
277
        mouse = { x: 0, y: 0 },
278
        time,
279
        gui, control, maxSpeedCtl, minSpeedCtl,
280
        img, count, onLoad,
281
        i, len;
282
 
283
 
284
    // Event Listeners
285
 
286
    function resize(e) {
287
        screenWidth   = canvas.width = window.innerWidth;
288
        screenHeight  = canvas.height = window.innerHeight;
289
        screenMinSize = Math.min(screenWidth, screenHeight);
290
        centerX       = screenWidth * 0.5;
291
        centerY       = screenHeight * 0.5;
292
 
293
        context = canvas.getContext('2d');
294
        context.lineWidth = 3.5;
295
        context.strokeStyle = LINE_COLOR;
296
        context.lineCap = context.lineJoin = 'round';
297
 
298
        if (delaunay) {
299
            delaunay.width = screenWidth;
300
            delaunay.height = screenHeight;
301
        }
302
    }
303
 
304
    function mouseMove(e) {
305
        mouse.x = e.clientX;
306
        mouse.y = e.clientY;
307
    }
308
 
309
 
310
    // Functions
311
 
312
    function addParticle(x, y) {
313
        if (particles.length >= control.maxNum) {
314
            particles.shift();
315
            addParticle(x, y);
316
            return;
317
        }
318
        var p = new Particle(x, y),
319
            l = Math.random() * (control.maxSpeed - control.minSpeed) + control.minSpeed,
320
            a = Math.random() * Math.PI * 2;
321
        p.vx = l * Math.cos(a);
322
        p.vy = l * Math.sin(a);
323
        particles.push(p);
324
    }
325
 
326
 
327
    // GUI Control
328
 
329
    control = {
330
        spawnTime: 500,
331
        maxNum: 25,
332
        maxSpeed: 1,
333
        minSpeed: 0.5
334
    };
335
 
336
 
337
    // Init
338
 
339
    canvas = document.getElementById('c');
340
 
341
    window.addEventListener('resize', resize, false);
342
    resize(null);
343
 
344
    mouse.x = screenWidth * 0.5;
345
    mouse.y = screenHeight * 0.5;
346
 
347
    delaunay = new Delaunay(screenWidth, screenHeight);
348
 
349
    for (i = 0, len = control.maxNum; i < len; i++) {
350
        addParticle(Math.random() * screenMinSize + centerX - screenMinSize * 0.5, Math.random() * screenMinSize + centerY - screenMinSize * 0.5);
351
    }
352
 
353
 
354
    // Loop
355
 
356
    var loop = function() {
357
        var TWO_PI = Math.PI * 2,
358
            w      = screenWidth,
359
            h      = screenHeight,
360
            ctx    = context,
361
            now    = new Date().getTime(),
362
            dx, dy, distSq, ax, ay,
363
            triangles, t, id, p0, p1, p2,
364
            ct, pt, cl, pl,
365
            i, len, p;
366
 
367
        if (now - time > control.spawnTime) {
368
            addParticle(mouse.x, mouse.y);
369
            time = now;
370
        }
371
 
372
        ctx.save();
373
        ctx.fillStyle = BACKGROUND_COLOR;
374
        ctx.fillRect(0, 0, screenWidth, screenHeight);
375
        ctx.globalAlpha = 0.15;
376
        ctx.fillStyle = backgroundPattern;
377
        ctx.fillRect(0, 0, screenWidth, screenHeight);
378
        ctx.restore();
379
 
380
        delaunay.clear();
381
 
382
        for (len = particles.length, i = 0; i < len; i++) {
383
            p = particles[i];
384
 
385
            p.x += p.vx;
386
            p.y += p.vy;
387
 
388
            // 反射
389
            if (p.x < 0) {
390
                p.x = 0;
391
                if (p.vx < 0) p.vx *= -1;
392
            }
393
            if (p.x > w) {
394
                p.x = w;
395
                if (p.vx > 0) p.vx *= -1;
396
            }
397
            if (p.y < 0) {
398
                p.y = 0;
399
                if (p.vy < 0) p.vy *= -1;
400
            }
401
            if (p.y > h) {
402
                p.y = h;
403
                if (p.vy > 0) p.vy *= -1;
404
            }
405
        }
406
 
407
        triangles = delaunay.multipleInsert(particles).getTriangles();
408
 
409
        ct = colorTable;
410
        pt = patternTable;
411
        cl = FILL_COLORS.length;
412
        pl = patterns.length;
413
 
414
        for (len = triangles.length, i = 0; i < len; i++) {
415
            t = triangles[i];
416
            id = t.id;
417
            p0 = t.nodes[0];
418
            p1 = t.nodes[1];
419
            p2 = t.nodes[2];
420
 
421
            if (id === null) continue;
422
 
423
            if (!ct[id]) {
424
                ct[id] = FILL_COLORS[colorIndex];
425
                colorIndex = (colorIndex + 1) % cl;
426
            }
427
            if (!pt[id]) {
428
                pt[id] = patterns[patternIndex];
429
                patternIndex = (patternIndex + 1) % pl;
430
            }
431
 
432
            ctx.save();
433
            ctx.beginPath();
434
            ctx.moveTo(p0.x, p0.y);
435
            ctx.lineTo(p1.x, p1.y);
436
            ctx.lineTo(p2.x, p2.y);
437
            ctx.closePath();
438
            ctx.fillStyle = ct[id];
439
            ctx.fill();
440
            ctx.translate(p0.x, p0.y);
441
            ctx.rotate(Math.atan2(p0.y - p1.y, p0.x - p1.x));
442
            ctx.fillStyle = pt[id];
443
            ctx.fill();
444
            ctx.stroke();
445
            ctx.restore();
446
        }
447
 
448
        requestAnimationFrame(loop);
449
    };
450
 
451
 
452
    // GUI
453
 
454
    gui = new dat.GUI();
455
    gui.add(control, 'maxNum', 0, 50).name('Max Num');
456
    maxSpeedCtl = gui.add(control, 'maxSpeed', 0, 5).name('Max Speed').onChange(function() {
457
        if (control.minSpeed > control.maxSpeed)
458
            control.minSpeed = control.maxSpeed;
459
        minSpeedCtl.updateDisplay();
460
    });
461
    minSpeedCtl = gui.add(control, 'minSpeed', 0, 5).name('Min Speed').onChange(function() {
462
        if (control.minSpeed > control.maxSpeed)
463
            control.maxSpeed = control.minSpeed;
464
        maxSpeedCtl.updateDisplay();
465
    });
466
    gui.add(control, 'spawnTime', 50, 1000).name('Spawn Time');
467
    gui.close();
468
 
469
 
470
    // Load Images
471
 
472
    count  = PATTERNS_URL.length;
473
    onLoad = function(e) {
474
        patterns.push(context.createPattern(e.target, 'repeat'));
475
 
476
        if (--count === 0) {
477
            backgroundPattern = patterns[Math.floor(patterns.length * Math.random())];
478
            patterns.push('rgba(0, 0, 0, 0)');
479
 
480
            canvas.addEventListener('mousemove', mouseMove, false);
481
 
482
            time = new Date().getTime();
483
 
484
            // Start update
485
            loop();
486
        }
487
    };
488
 
489
    for (i = 0, len = PATTERNS_URL.length; i < len; i++) {
490
        img = new Image();
491
        img.addEventListener('load', onLoad, false);
492
        img.src = PATTERNS_URL[i];
493
    }
494
 
495
})();
496
 
 

WTFgl

CSSDeck G+