Skip to content

Instantly share code, notes, and snippets.

@MacroMachines
Created December 26, 2017 14:46
Show Gist options
  • Select an option

  • Save MacroMachines/353f818a5cd21dbfc39ce849d58c3cc0 to your computer and use it in GitHub Desktop.

Select an option

Save MacroMachines/353f818a5cd21dbfc39ce849d58c3cc0 to your computer and use it in GitHub Desktop.
Molecules
<body></body>
/*
* Copyright MIT © <2013> <Francesco Trillini>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var Molecules = {};
;(function(Molecules, undefined) {
var self = window.Molecules || {}, canvas, context, mouse = { x: 0, y: 0}, molecules = [], MAX_MOLECULES = ~~((innerWidth + innerHeight) / 30), FPS = 60;
/*
* Colors.
*/
var RGB = {
light: {
r: 0,
g: 0,
b: 0
},
transparent: {
r: 0,
g: 0,
b: 0
}
};
// Dat GUI default values
var attraction = 0.0008, friction = 0.2, light = true, transparent = false, color = '#ffffff';
/*
* Settings.
*/
var Settings = function() {
this.attraction = 0.0008;
this.friction = 0.2;
this.light = true,
this.transparent = false;
this.color = '#ffffff';
this.changeAttraction = function(value) {
attraction = value;
};
this.changeFriction = function(value) {
friction = value;
};
this.enableLight = function(value) {
light = value;
};
this.enableTransparency = function(value) {
transparent = value;
};
this.changeColor = function(value) {
color = value;
};
this.deleteAll = function(value) {
molecules = [];
};
};
/*
* Init.
*/
self.init = function() {
var settings = new Settings();
var GUI = new dat.GUI();
// Dat GUI main
GUI.add(settings, 'attraction').min(0.0005).max(0.005).onChange(settings.changeAttraction);
GUI.add(settings, 'friction').min(0.1).max(0.9).onChange(settings.changeFriction);
GUI.add(settings, 'light').onChange(settings.enableLight);
GUI.add(settings, 'transparent').onChange(settings.enableTransparency);
GUI.addColor(settings, 'color').onChange(settings.changeColor);
GUI.add(settings, 'deleteAll');
var body = document.querySelector('body');
canvas = document.createElement('canvas');
canvas.width = innerWidth;
canvas.height = innerHeight;
canvas.style.position = 'absolute';
canvas.style.top = 0;
canvas.style.bottom = 0;
canvas.style.left = 0;
canvas.style.right = 0;
canvas.style.zIndex = -1;
canvas.style.cursor = 'pointer';
canvas.style.background = '-webkit-radial-gradient(#0067c7, #002649)';
canvas.style.background = '-moz-radial-gradient(#0067c7, #002649)';
canvas.style.background = '-ms-radial-gradient(#0067c7, #002649)';
canvas.style.background = '-o-radial-gradient(#0067c7, #002649)';
canvas.style.background = 'radial-gradient(#0067c7, #002649)';
body.appendChild(canvas);
// Browser supports canvas?
if(!!(self.gotSupport())) {
context = canvas.getContext('2d');
// Events
if('ontouchstart' in window)
canvas.addEventListener('touchstart', self.onTouchStart, false);
else
canvas.addEventListener('mousedown', self.onMouseDown, false);
// On resize event
window.onresize = onResize;
self.createMolecules();
}
else {
console.error("Sorry, your browser doesn't support canvas.");
}
};
/*
* Check if browser supports canvas element.
*/
self.gotSupport = function() {
return canvas.getContext && canvas.getContext('2d');
};
/*
* On resize window event.
*/
function onResize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
/*
* Mouse down event.
*/
self.onMouseDown = function(event) {
event.preventDefault();
mouse.x = event.pageX - canvas.offsetLeft;
mouse.y = event.pageY - canvas.offsetTop;
self.addMolecule();
};
/*
* Touch start event.
*/
self.onTouchStart = function(event) {
event.preventDefault();
// Multitouch list
var touches = event.changedTouches;
for(var touch = 0; touch < touches.length; touch++) {
mouse.x = touches[touch].pageX - canvas.offsetLeft;
mouse.y = touches[touch].pageY - canvas.offsetTop;
self.addMolecule();
}
};
/*
* Clear the screen.
*/
self.clear = function() {
context.clearRect(0, 0, canvas.width, canvas.height);
// Toggle the light
if(!light) {
RGB.light.r += (0 - RGB.light.r) * 0.1;
RGB.light.g += (0 - RGB.light.g) * 0.1;
RGB.light.b += (0 - RGB.light.b) * 0.1;
}
else if(light) {
RGB.light.r += (255 - RGB.light.r) * 0.1;
RGB.light.g += (255 - RGB.light.g) * 0.1;
RGB.light.b += (255 - RGB.light.b) * 0.1;
}
};
/*
* Populate molecules.
*/
self.createMolecules = function() {
for(var quantity = 0, len = MAX_MOLECULES; quantity < len; quantity++) {
var radius = 2 + ~~(Math.random() * 8);
molecules.push({
x: 20 + Math.random() * (innerWidth - 40) | 0,
y: 20 + Math.random() * (innerHeight - 40) | 0,
vx: - 1 + Math.random() * 2,
vy: - 1 + Math.random() * 2,
minRadius: radius,
maxRadius: radius
});
}
self.merge();
};
/*
* Add a new molecule.
*/
self.addMolecule = function() {
var angle, radius;
angle = Math.random() * Math.PI * 2;
radius = 2 + ~~(Math.random() * 8);
molecules.push({
x: mouse.x,
y: mouse.y,
vx: 2 + Math.random() * 6 * Math.cos(angle),
vy: 2 + Math.random() * 6 * Math.sin(angle),
minRadius: radius,
maxRadius: 0
});
};
/*
* Let's merge our molecules! :D
*/
self.merge = function() {
self.clear();
molecules.forEach(function(molecule, index) {
// Add velocity
molecule.x += molecule.vx;
molecule.y += molecule.vy;
// Reach the towards radius
molecule.maxRadius += (molecule.minRadius - molecule.maxRadius) * 0.1;
// Width bounds
if((molecule.x >= canvas.width - molecule.maxRadius * 4 && molecule.vx > 0) || (molecule.x <= molecule.maxRadius * 4 && molecule.vx < 0)) {
molecule.vx *= -1;
molecule.vx *= friction;
}
// Height bounds
if((molecule.y >= canvas.height - molecule.maxRadius * 4 && molecule.vy > 0) || (molecule.y <= molecule.maxRadius * 4 && molecule.vy < 0)) {
molecule.vy *= -1;
molecule.vy *= friction;
}
// Checks a neighborhood bludger
for(var nextMolecule = index + 1; nextMolecule < molecules.length; nextMolecule++) {
var otherMolecule = molecules[nextMolecule];
// Oh we've found one!
if(self.distanceTo(molecule, otherMolecule) < 100) {
context.save();
context.beginPath();
context.globalCompositeOperation = 'destination-over';
context.globalAlpha = 1 - self.distanceTo(molecule, otherMolecule) / 100;
context.lineWidth = 1;
context.strokeStyle = color;
context.moveTo(molecule.x, molecule.y);
context.lineTo(otherMolecule.x, otherMolecule.y);
context.stroke();
context.closePath();
context.restore();
// Spring force
var attractionX = (molecule.x - otherMolecule.x) * attraction;
var attractionY = (molecule.y - otherMolecule.y) * attraction;
molecule.vx -= attractionX;
molecule.vy -= attractionY;
otherMolecule.vx += attractionX;
otherMolecule.vy += attractionY;
}
}
// Drawing stuff
if(!transparent) {
context.save();
context.globalCompositeOperation = 'lighter';
context.translate(molecule.x, molecule.y);
// Radial shadow
var gradient = context.createRadialGradient(0, 0, 0, 0, 0, molecule.maxRadius * 2);
gradient.addColorStop(0, color);
gradient.addColorStop(1, 'rgba' + '(' + Math.round(RGB.light.r) + ', ' + Math.round(RGB.light.g) + ', ' + Math.round(RGB.light.b) + ', ' + '0.2' + ')');
context.fillStyle = gradient;
context.beginPath();
context.arc(0, 0, molecule.maxRadius * 4, 0, Math.PI * 2);
context.closePath();
context.fill();
context.restore();
}
});
requestAnimFrame(self.merge);
};
/*
* Distance between two points.
*/
self.distanceTo = function(pointA, pointB) {
var dx = Math.abs(pointA.x - pointB.x);
var dy = Math.abs(pointA.y - pointB.y);
return Math.sqrt(dx * dx + dy * dy);
};
/*
* Request new frame by Paul Irish.
* 60 FPS.
*/
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / FPS);
};
})();
window.addEventListener ? window.addEventListener('load', self.init, false) : window.onload = self.init;
})(Molecules);
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
body {
margin: 0px;
overflow: hidden;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment