Line 430: | Line 430: | ||
<script> | <script> | ||
− | / | + | var PI2 = Math.PI * 2; |
+ | var HALF_PI = Math.PI / 2; | ||
− | var | + | var isTouch = 'ontouchstart' in window; |
− | var | + | var isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/); |
− | + | function Canvas(options) { | |
+ | options = _.clone(options || {}); | ||
+ | this.options = _.defaults(options, this.options); | ||
+ | |||
+ | this.el = this.options.el; | ||
+ | this.ctx = this.el.getContext('2d'); | ||
+ | |||
+ | this.dpr = window.devicePixelRatio || 1; | ||
+ | |||
+ | this.updateDimensions(); | ||
+ | window.addEventListener('resize', this.updateDimensions.bind(this), false); | ||
+ | this.resetTarget(); | ||
+ | |||
+ | if(isTouch){ | ||
+ | // touch | ||
+ | this.el.addEventListener('touchstart', this.touchMove.bind(this), false); | ||
+ | this.el.addEventListener('touchmove', this.touchMove.bind(this), false); | ||
+ | // this.el.addEventListener('touchend', this.resetTarget.bind(this), false); | ||
+ | } else { | ||
+ | // Mouse | ||
+ | window.addEventListener('mousemove', this.mouseMove.bind(this), false); | ||
+ | window.addEventListener('mouseout', this.resetTarget.bind(this), false); | ||
+ | } | ||
+ | |||
+ | this.setupParticles(); | ||
− | + | this.loop(); | |
− | + | } | |
− | + | ||
− | + | Canvas.prototype.updateDimensions = function() { | |
+ | this.width = this.el.width = _.result(this.options, 'width') * this.dpr; | ||
+ | this.height = this.el.height = _.result(this.options, 'height') * this.dpr; | ||
+ | this.el.style.width = _.result(this.options, 'width') + 'px'; | ||
+ | this.el.style.height = _.result(this.options, 'height') + 'px'; | ||
+ | } | ||
− | + | // Update the orb target | |
− | + | Canvas.prototype.mouseMove = function(event) { | |
− | + | this.target = new Vector(event.clientX * this.dpr, event.clientY* this.dpr); | |
+ | } | ||
− | + | // Reset to center when we mouse out | |
− | + | Canvas.prototype.resetTarget = function() { | |
− | + | this.target = new Vector(this.width / 2, this.height /2); | |
+ | } | ||
− | function | + | // Touch Eent |
+ | Canvas.prototype.touchMove = function(event) { | ||
+ | if(event.touches.length === 1) { event.preventDefault(); } | ||
− | + | this.target = new Vector(event.touches[0].pageX * this.dpr, event.touches[0].pageY * this.dpr); | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
} | } | ||
− | + | // Defaults | |
− | + | Canvas.prototype.options = { | |
− | + | count: 20, | |
− | + | speed: 0.5, | |
− | + | width: 400, | |
− | + | height: 400, | |
− | + | size: 10, | |
− | + | radius: 5, | |
− | + | background: '29, 22, 52', | |
− | + | maxDistance: 100 | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
} | } | ||
− | function | + | Canvas.prototype.setupParticles = function() { |
− | + | this.particles = []; | |
− | + | var index = -1; | |
− | + | var between = PI2 / this.options.count; | |
+ | while(++index < this.options.count) { | ||
+ | var x; | ||
+ | var y; | ||
+ | var angle; | ||
+ | var max = Math.max(this.width, this.height); | ||
+ | |||
+ | angle = (index + 1) * between; | ||
+ | |||
+ | x = Math.cos(angle) * max; | ||
+ | x += this.width / 2; | ||
− | + | y = Math.sin(angle) * max; | |
− | + | y += this.height / 2; | |
+ | |||
+ | var particle = new Particle({ | ||
+ | x: x, | ||
+ | y: y, | ||
+ | radius: this.options.radius, | ||
+ | size: this.options.size, | ||
+ | angle: angle, | ||
+ | color: this.options.color | ||
+ | }); | ||
+ | |||
+ | this.particles.push(particle); | ||
+ | } | ||
} | } | ||
− | function | + | Canvas.prototype.findClosest = function() { |
− | + | var index = -1; | |
− | + | var pointsLength = this.particles.length; | |
− | + | while(++index < pointsLength) { | |
− | + | var closestIndex = -1; | |
− | + | this.particles[index].closest = []; | |
− | + | ||
− | + | while(++closestIndex < pointsLength) { | |
− | + | var closest = this.particles[closestIndex]; | |
+ | var distance = this.particles[index].position.distanceTo(closest.position); | ||
+ | if(distance < this.options.maxDistance) { | ||
+ | var vector = new Vector(closest.position.x, closest.position.y); | ||
+ | vector.opacity = 1 - (distance / this.options.maxDistance); | ||
+ | vector.distance = distance; | ||
+ | this.particles[index].closest.push(vector); | ||
+ | } | ||
} | } | ||
+ | } | ||
} | } | ||
− | function | + | Canvas.prototype.loop = function() { |
− | + | // this.clear(); | |
− | + | if(isTouch || isSafari) { | |
+ | this.ghost(); | ||
+ | } else { | ||
+ | this.ghostGradient(); | ||
+ | } | ||
+ | if(this.options.maxDistance > 0) { | ||
+ | this.findClosest(); | ||
+ | } | ||
+ | this.draw(); | ||
+ | |||
+ | window.requestAnimationFrame(_.bind(this.loop, this)); | ||
+ | } | ||
− | + | Canvas.prototype.clear = function() { | |
− | + | this.ctx.clearRect(0, 0 , this.width, this.height); | |
− | + | ||
} | } | ||
− | function | + | Canvas.prototype.ghost = function() { |
− | + | this.ctx.globalCompositeOperation = "source-over"; | |
− | + | this.ctx.rect(0, 0 , this.width, this.height); | |
+ | if(typeof this.options.background === 'string') { | ||
+ | this.ctx.fillStyle = "rgb(" + this.options.background + ")"; | ||
+ | } else { | ||
+ | this.ctx.fillStyle = "rgb(" + this.options.background[0] + ")"; | ||
+ | } | ||
− | + | this.ctx.fill(); | |
− | + | ||
} | } | ||
− | function | + | Canvas.prototype.ghostGradient = function() { |
+ | var gradient; | ||
+ | |||
+ | if(typeof this.options.background === 'string') { | ||
+ | this.ctx.fillStyle = 'rgb(' + this.options.background + ')'; | ||
+ | } else { | ||
+ | var gradient = this.ctx.createRadialGradient(this.width/2, this.height/2, 0, this.width/2, this.height/2, Math.max(this.width, this.height)/2); | ||
− | + | var length = this.options.background.length; | |
− | + | for(var i = 0; i < length; i++){ | |
− | + | gradient.addColorStop((i+1) / length, 'rgb(' + this.options.background[i] + ')'); | |
− | + | ||
− | + | ||
} | } | ||
+ | this.ctx.fillStyle = gradient; | ||
+ | } | ||
+ | |||
+ | this.ctx.globalOpacity = 0.1; | ||
+ | this.ctx.globalCompositeOperation = "darken"; | ||
+ | this.ctx.fillRect(0, 0 , this.width, this.height); | ||
+ | } | ||
+ | |||
+ | // Draw | ||
+ | Canvas.prototype.draw = function() { | ||
+ | var index = -1; | ||
+ | var length = this.particles.length; | ||
+ | while(++index < length) { | ||
+ | var point = this.particles[index]; | ||
+ | var color = point.color || this.options.color; | ||
+ | point.update(this.target, index); | ||
− | + | this.ctx.globalAlpha = 0.3; | |
+ | this.ctx.globalCompositeOperation = "lighten"; | ||
+ | this.ctx.fillStyle = 'rgb(' + color + ')'; | ||
+ | this.ctx.beginPath(); | ||
+ | this.ctx.arc(point.position.x, point.position.y, point.size, 0, PI2, false); | ||
+ | this.ctx.closePath(); | ||
+ | this.ctx.fill(); | ||
− | + | if(this.options.maxDistance > 0) { | |
− | + | this.drawLines(point, color); | |
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Draw connecting lines | ||
+ | Canvas.prototype.drawLines = function (point, color) { | ||
+ | color = color || this.options.color; | ||
+ | var index = -1; | ||
+ | var length = point.closest.length; | ||
+ | this.ctx.globalAlpha = 0.2; | ||
+ | this.ctx.globalCompositeOperation = "screen"; | ||
+ | this.ctx.lineCap = 'round'; | ||
+ | while(++index < length) { | ||
+ | this.ctx.lineWidth = (point.size * 2) * point.closest[index].opacity; | ||
+ | this.ctx.strokeStyle = 'rgba(' + color + ', ' + point.closest[index].opacity + ')'; | ||
+ | this.ctx.beginPath(); | ||
+ | this.ctx.moveTo(point.position.x, point.position.y); | ||
+ | this.ctx.lineTo(point.closest[index].x, point.closest[index].y); | ||
+ | this.ctx.stroke(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function Particle(options) { | ||
+ | options = _.clone(options || {}); | ||
+ | this.options = _.defaults(options, this.options); | ||
+ | |||
+ | this.position = this.shift = new Vector(this.options.x, this.options.y); | ||
+ | |||
+ | this.speed = this.options.speed || 0.01 + Math.random() * 0.04; | ||
+ | |||
+ | this.angle = this.options.angle || 0; | ||
− | + | if(this.options.color) { | |
− | + | var color = this.options.color.split(','); | |
− | + | var colorIndex = -1; | |
− | + | while(++colorIndex < 3) { | |
− | + | color[colorIndex] = Math.round(parseInt(color[colorIndex], 10) + (Math.random()*100)-50); | |
− | + | ||
− | + | // Clamp | |
− | + | color[colorIndex] = Math.min(color[colorIndex], 255); | |
− | + | color[colorIndex] = Math.max(color[colorIndex], 0); | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
} | } | ||
+ | this.color = color.join(', '); | ||
+ | } | ||
+ | |||
+ | // Size | ||
+ | this.options.size = this.options.size || 7; | ||
+ | this.size = 1 + Math.random() * this.options.size; | ||
+ | this.targetSize = this.options.targetSize || this.options.size; | ||
+ | |||
+ | this.orbit = this.options.radius * 0.5 + (this.options.radius * 0.5 * Math.random()); | ||
} | } | ||
− | + | Particle.prototype.update = function(target, index) { | |
+ | this.angle += this.speed; | ||
+ | |||
+ | this.shift.x += (target.x - this.shift.x) * this.speed; | ||
+ | this.shift.y += (target.y - this.shift.y) * this.speed; | ||
+ | |||
+ | this.position.x = this.shift.x + Math.cos(index + this.angle) * this.orbit; | ||
+ | this.position.y = this.shift.y + Math.sin(index + this.angle) * this.orbit; | ||
+ | |||
+ | if(!isSafari) { | ||
+ | this.size += (this.targetSize - this.size) * 0.03; | ||
+ | |||
+ | if(Math.round(this.size) === Math.round(this.targetSize)) { | ||
+ | this.targetSize = 1 + Math.random() * this.options.size; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function Vector(x, y) { | ||
+ | this.x = x || 0; | ||
+ | this.y = y || 0; | ||
+ | } | ||
+ | |||
+ | Vector.prototype.distanceTo = function(vector, abs) { | ||
+ | var distance = Math.sqrt(Math.pow(this.x - vector.x, 2) + Math.pow(this.y - vector.y, 2)); | ||
+ | return abs || false ? Math.abs(distance) : distance; | ||
+ | }; | ||
+ | |||
+ | new Canvas({ | ||
+ | el: document.getElementById('canvas'), | ||
+ | |||
+ | count: 25, | ||
+ | speed: 0.3, | ||
+ | radius: 6, | ||
+ | width: function() { return window.innerWidth; }, | ||
+ | height: function() { return window.innerHeight; }, | ||
+ | size: 15, | ||
+ | color: '30, 180, 1', | ||
+ | maxDistance: 100, | ||
+ | background: ['1, 62, 66', '1, 40, 60'] | ||
+ | }); | ||
</script> | </script> | ||
Revision as of 13:52, 25 September 2018
Welcome to Alternative Roots
Alternative Roots
Newcastle iGEM
Alternative Roots
PROJECT OVERVIEW
Our Sponsors
Newcastle iGEM is proud to be sponsored by:
Contact Us
Reach out for a collaboration or just say hello
Get Involved
We are always happy to hear from individuals, groups or organisations that would like to support our project. If you wish to discuss a collaboration, sponsorship or if you just want to learn more about the project, please get in touch using the contact details on the right. Alternatively, if you would like to donate a small amount to fund our project, you can do so through Experiment.com - an online platform for discovering, funding, and sharing scientific research.
Any support is greatly appreciated and funds raised will be used in all areas of the iGEM project - such as lab equipment, human practices research, merchandise and travel to jamboree in Boston, USA.
Contact Info
Where to Find Us
Devonshire Building
Newcastle University
Newcastle upon Tyne
NE1 7RU
Email Us At
igem.team@newcastle.ac.uk