Created
August 1, 2015 23:00
Revisions
-
vijayrajanna created this gist
Aug 1, 2015 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,9 @@ Gravity Points -------------- http://jsdo.it/akm2/5SPA Forked from [AKM2](http://codepen.io/akm2/)'s Pen [Gravity Points](http://codepen.io/akm2/pen/rHIsa/). A [Pen](http://codepen.io/vijayrajanna/pen/RPEZEJ) by [Vijay Rajanna](http://codepen.io/vijayrajanna) on [CodePen](http://codepen.io/). [License](http://codepen.io/vijayrajanna/pen/RPEZEJ/license). This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,2 @@ <canvas id="c"></canvas> <div class="info">Click to add gravity point.</div> This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,531 @@ /** * requestAnimationFrame */ window.requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })(); /** * Vector */ function Vector(x, y) { this.x = x || 0; this.y = y || 0; } Vector.add = function(a, b) { return new Vector(a.x + b.x, a.y + b.y); }; Vector.sub = function(a, b) { return new Vector(a.x - b.x, a.y - b.y); }; Vector.scale = function(v, s) { return v.clone().scale(s); }; Vector.random = function() { return new Vector( Math.random() * 2 - 1, Math.random() * 2 - 1 ); }; Vector.prototype = { set: function(x, y) { if (typeof x === 'object') { y = x.y; x = x.x; } this.x = x || 0; this.y = y || 0; return this; }, add: function(v) { this.x += v.x; this.y += v.y; return this; }, sub: function(v) { this.x -= v.x; this.y -= v.y; return this; }, scale: function(s) { this.x *= s; this.y *= s; return this; }, length: function() { return Math.sqrt(this.x * this.x + this.y * this.y); }, lengthSq: function() { return this.x * this.x + this.y * this.y; }, normalize: function() { var m = Math.sqrt(this.x * this.x + this.y * this.y); if (m) { this.x /= m; this.y /= m; } return this; }, angle: function() { return Math.atan2(this.y, this.x); }, angleTo: function(v) { var dx = v.x - this.x, dy = v.y - this.y; return Math.atan2(dy, dx); }, distanceTo: function(v) { var dx = v.x - this.x, dy = v.y - this.y; return Math.sqrt(dx * dx + dy * dy); }, distanceToSq: function(v) { var dx = v.x - this.x, dy = v.y - this.y; return dx * dx + dy * dy; }, lerp: function(v, t) { this.x += (v.x - this.x) * t; this.y += (v.y - this.y) * t; return this; }, clone: function() { return new Vector(this.x, this.y); }, toString: function() { return '(x:' + this.x + ', y:' + this.y + ')'; } }; /** * GravityPoint */ function GravityPoint(x, y, radius, targets) { Vector.call(this, x, y); this.radius = radius; this.currentRadius = radius * 0.5; this._targets = { particles: targets.particles || [], gravities: targets.gravities || [] }; this._speed = new Vector(); } GravityPoint.RADIUS_LIMIT = 65; GravityPoint.interferenceToPoint = true; GravityPoint.prototype = (function(o) { var s = new Vector(0, 0), p; for (p in o) s[p] = o[p]; return s; })({ gravity: 0.05, isMouseOver: false, dragging: false, destroyed: false, _easeRadius: 0, _dragDistance: null, _collapsing: false, hitTest: function(p) { return this.distanceTo(p) < this.radius; }, startDrag: function(dragStartPoint) { this._dragDistance = Vector.sub(dragStartPoint, this); this.dragging = true; }, drag: function(dragToPoint) { this.x = dragToPoint.x - this._dragDistance.x; this.y = dragToPoint.y - this._dragDistance.y; }, endDrag: function() { this._dragDistance = null; this.dragging = false; }, addSpeed: function(d) { this._speed = this._speed.add(d); }, collapse: function(e) { this.currentRadius *= 1.75; this._collapsing = true; }, render: function(ctx) { if (this.destroyed) return; var particles = this._targets.particles, i, len; for (i = 0, len = particles.length; i < len; i++) { particles[i].addSpeed(Vector.sub(this, particles[i]).normalize().scale(this.gravity)); } this._easeRadius = (this._easeRadius + (this.radius - this.currentRadius) * 0.07) * 0.95; this.currentRadius += this._easeRadius; if (this.currentRadius < 0) this.currentRadius = 0; if (this._collapsing) { this.radius *= 0.75; if (this.currentRadius < 1) this.destroyed = true; this._draw(ctx); return; } var gravities = this._targets.gravities, g, absorp, area = this.radius * this.radius * Math.PI, garea; for (i = 0, len = gravities.length; i < len; i++) { g = gravities[i]; if (g === this || g.destroyed) continue; if ( (this.currentRadius >= g.radius || this.dragging) && this.distanceTo(g) < (this.currentRadius + g.radius) * 0.85 ) { g.destroyed = true; this.gravity += g.gravity; absorp = Vector.sub(g, this).scale(g.radius / this.radius * 0.5); this.addSpeed(absorp); garea = g.radius * g.radius * Math.PI; this.currentRadius = Math.sqrt((area + garea * 3) / Math.PI); this.radius = Math.sqrt((area + garea) / Math.PI); } g.addSpeed(Vector.sub(this, g).normalize().scale(this.gravity)); } if (GravityPoint.interferenceToPoint && !this.dragging) this.add(this._speed); this._speed = new Vector(); if (this.currentRadius > GravityPoint.RADIUS_LIMIT) this.collapse(); this._draw(ctx); }, _draw: function(ctx) { var grd, r; ctx.save(); grd = ctx.createRadialGradient(this.x, this.y, this.radius, this.x, this.y, this.radius * 5); grd.addColorStop(0, 'rgba(0, 0, 0, 0.1)'); grd.addColorStop(1, 'rgba(0, 0, 0, 0)'); ctx.beginPath(); ctx.arc(this.x, this.y, this.radius * 5, 0, Math.PI * 2, false); ctx.fillStyle = grd; ctx.fill(); r = Math.random() * this.currentRadius * 0.7 + this.currentRadius * 0.3; grd = ctx.createRadialGradient(this.x, this.y, r, this.x, this.y, this.currentRadius); grd.addColorStop(0, 'rgba(0, 0, 0, 1)'); grd.addColorStop(1, Math.random() < 0.2 ? 'rgba(255, 196, 0, 0.15)' : 'rgba(103, 181, 191, 0.75)'); ctx.beginPath(); ctx.arc(this.x, this.y, this.currentRadius, 0, Math.PI * 2, false); ctx.fillStyle = grd; ctx.fill(); ctx.restore(); } }); /** * Particle */ function Particle(x, y, radius) { Vector.call(this, x, y); this.radius = radius; this._latest = new Vector(); this._speed = new Vector(); } Particle.prototype = (function(o) { var s = new Vector(0, 0), p; for (p in o) s[p] = o[p]; return s; })({ addSpeed: function(d) { this._speed.add(d); }, update: function() { if (this._speed.length() > 12) this._speed.normalize().scale(12); this._latest.set(this); this.add(this._speed); } // render: function(ctx) { // if (this._speed.length() > 12) this._speed.normalize().scale(12); // this._latest.set(this); // this.add(this._speed); // ctx.save(); // ctx.fillStyle = ctx.strokeStyle = '#fff'; // ctx.lineCap = ctx.lineJoin = 'round'; // ctx.lineWidth = this.radius * 2; // ctx.beginPath(); // ctx.moveTo(this.x, this.y); // ctx.lineTo(this._latest.x, this._latest.y); // ctx.stroke(); // ctx.beginPath(); // ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false); // ctx.fill(); // ctx.restore(); // } }); // Initialize (function() { // Configs var BACKGROUND_COLOR = 'rgba(11, 51, 56, 1)', PARTICLE_RADIUS = 1, G_POINT_RADIUS = 10, G_POINT_RADIUS_LIMITS = 65; // Vars var canvas, context, bufferCvs, bufferCtx, screenWidth, screenHeight, mouse = new Vector(), gravities = [], particles = [], grad, gui, control; // Event Listeners function resize(e) { screenWidth = canvas.width = window.innerWidth; screenHeight = canvas.height = window.innerHeight; bufferCvs.width = screenWidth; bufferCvs.height = screenHeight; context = canvas.getContext('2d'); bufferCtx = bufferCvs.getContext('2d'); var cx = canvas.width * 0.5, cy = canvas.height * 0.5; grad = context.createRadialGradient(cx, cy, 0, cx, cy, Math.sqrt(cx * cx + cy * cy)); grad.addColorStop(0, 'rgba(0, 0, 0, 0)'); grad.addColorStop(1, 'rgba(0, 0, 0, 0.35)'); } function mouseMove(e) { mouse.set(e.clientX, e.clientY); var i, g, hit = false; for (i = gravities.length - 1; i >= 0; i--) { g = gravities[i]; if ((!hit && g.hitTest(mouse)) || g.dragging) g.isMouseOver = hit = true; else g.isMouseOver = false; } canvas.style.cursor = hit ? 'pointer' : 'default'; } function mouseDown(e) { for (var i = gravities.length - 1; i >= 0; i--) { if (gravities[i].isMouseOver) { gravities[i].startDrag(mouse); return; } } gravities.push(new GravityPoint(e.clientX, e.clientY, G_POINT_RADIUS, { particles: particles, gravities: gravities })); } function mouseUp(e) { for (var i = 0, len = gravities.length; i < len; i++) { if (gravities[i].dragging) { gravities[i].endDrag(); break; } } } function doubleClick(e) { for (var i = gravities.length - 1; i >= 0; i--) { if (gravities[i].isMouseOver) { gravities[i].collapse(); break; } } } // Functions function addParticle(num) { var i, p; for (i = 0; i < num; i++) { p = new Particle( Math.floor(Math.random() * screenWidth - PARTICLE_RADIUS * 2) + 1 + PARTICLE_RADIUS, Math.floor(Math.random() * screenHeight - PARTICLE_RADIUS * 2) + 1 + PARTICLE_RADIUS, PARTICLE_RADIUS ); p.addSpeed(Vector.random()); particles.push(p); } } function removeParticle(num) { if (particles.length < num) num = particles.length; for (var i = 0; i < num; i++) { particles.pop(); } } // GUI Control control = { particleNum: 50 }; // Init canvas = document.getElementById('c'); bufferCvs = document.createElement('canvas'); window.addEventListener('resize', resize, false); resize(null); addParticle(control.particleNum); canvas.addEventListener('mousemove', mouseMove, false); canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mouseup', mouseUp, false); canvas.addEventListener('dblclick', doubleClick, false); // GUI gui = new dat.GUI(); gui.add(control, 'particleNum', 0, 300).step(1).name('Particle Num').onChange(function() { var n = (control.particleNum | 0) - particles.length; if (n > 0) addParticle(n); else if (n < 0) removeParticle(-n); }); gui.add(GravityPoint, 'interferenceToPoint').name('Interference Between Point'); gui.close(); // Start Update var loop = function() { var i, len, g, p; context.save(); context.fillStyle = BACKGROUND_COLOR; context.fillRect(0, 0, screenWidth, screenHeight); context.fillStyle = grad; context.fillRect(0, 0, screenWidth, screenHeight); context.restore(); for (i = 0, len = gravities.length; i < len; i++) { g = gravities[i]; if (g.dragging) g.drag(mouse); g.render(context); if (g.destroyed) { gravities.splice(i, 1); len--; i--; } } bufferCtx.save(); bufferCtx.globalCompositeOperation = 'destination-out'; bufferCtx.globalAlpha = 0.35; bufferCtx.fillRect(0, 0, screenWidth, screenHeight); bufferCtx.restore(); // パーティクルをバッファに描画 // for (i = 0, len = particles.length; i < len; i++) { // particles[i].render(bufferCtx); // } len = particles.length; bufferCtx.save(); bufferCtx.fillStyle = bufferCtx.strokeStyle = '#fff'; bufferCtx.lineCap = bufferCtx.lineJoin = 'round'; bufferCtx.lineWidth = PARTICLE_RADIUS * 2; bufferCtx.beginPath(); for (i = 0; i < len; i++) { p = particles[i]; p.update(); bufferCtx.moveTo(p.x, p.y); bufferCtx.lineTo(p._latest.x, p._latest.y); } bufferCtx.stroke(); bufferCtx.beginPath(); for (i = 0; i < len; i++) { p = particles[i]; bufferCtx.moveTo(p.x, p.y); bufferCtx.arc(p.x, p.y, p.radius, 0, Math.PI * 2, false); } bufferCtx.fill(); bufferCtx.restore(); // バッファをキャンバスに描画 context.drawImage(bufferCvs, 0, 0); requestAnimationFrame(loop); }; loop(); })(); This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ <script src="http://dat-gui.googlecode.com/git/build/dat.gui.min.js"></script> This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,28 @@ body { font-family: Helvetica sans-serif; padding: 0; margin: 0; background-color: #222; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -o-user-select: none; -ms-user-select: none; user-select: none; } canvas { position: absolute; top: 0; left: 0; } .info { position: absolute; top: 0; left: 0; padding: 5px 15px; color: #eee; font-size: 13px; background-color: rgba(0, 0, 0, .5); }