mirror of
https://github.com/seigler/dash-website
synced 2025-07-27 07:16:10 +00:00
(Feature) Boids: Add boids animation to Particiapte header
This commit is contained in:
parent
d2eea20612
commit
fd0251c24d
3 changed files with 312 additions and 5 deletions
|
@ -1,11 +1,14 @@
|
|||
<div class="hero" id="hero">
|
||||
<div id="background-video">
|
||||
<div class="hero hero--space" id="hero">
|
||||
<!-- <div id="background-video">
|
||||
<video loop autoplay width="100%" height="auto" poster="{{ base }}/assets/img/hero/{% t pages.participate.hero-image %}" class="background-video">
|
||||
<!-- <source src="assets/videos/Home-BG.m4v" type="video/mp4">
|
||||
<source src="assets/videos/Home-BG.m4v" type="video/mp4">
|
||||
<source src="assets/videos/Home-BG.webm" type="video/webm">
|
||||
<source src="assets/videos/Home-BG" type="video/ogg"> -->
|
||||
<source src="assets/videos/Home-BG" type="video/ogg">
|
||||
</video>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Boids -->
|
||||
<canvas id="canvas"></canvas>
|
||||
|
||||
<!-- Navigation -->
|
||||
{% include nav-desktop.html %}
|
||||
|
@ -21,4 +24,7 @@
|
|||
<a href="#participate-budgets" class="btn-blue-solid">{% t pages.participate.hero-funding-btn %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hero stripe -->
|
||||
<div class="hero__stripe"></div>
|
||||
</div>
|
||||
|
|
286
src/js/boids.js
Normal file
286
src/js/boids.js
Normal file
|
@ -0,0 +1,286 @@
|
|||
// Taken from http://codepen.io/kenjiSpecial
|
||||
// window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
// --------------------------
|
||||
(function () {
|
||||
var width, height,
|
||||
prevTime,
|
||||
fps = 30,
|
||||
now, then = Date.now(),
|
||||
interval = 1000/fps,
|
||||
delta,
|
||||
ctx, canvas = document.getElementById('canvas');
|
||||
|
||||
if (canvas) {
|
||||
|
||||
ctx = canvas.getContext('2d');
|
||||
|
||||
width = canvas.offsetWidth;
|
||||
height = canvas.offsetHeight;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// ================
|
||||
var Boid = function(ctx){
|
||||
this.ctx = ctx;
|
||||
|
||||
this.x = width * Math.random();
|
||||
this.y = height * Math.random();
|
||||
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
};
|
||||
|
||||
Boid.prototype = {
|
||||
rad : 2,
|
||||
col : '#fff',
|
||||
|
||||
update : function(){
|
||||
|
||||
},
|
||||
|
||||
draw : function(){
|
||||
this.ctx.fillStyle = this.col;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc( this.x, this.y, this.rad, 0, 2 * Math.PI, false );
|
||||
this.ctx.fill();
|
||||
this.ctx.closePath();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// ================
|
||||
var boids = [];
|
||||
var randomAclX = [];
|
||||
var randomAclY = [];
|
||||
var isRandom = false;
|
||||
|
||||
var NUM_BOIDS = ~~(width * height * 0.00009);
|
||||
var NUM_BOIDS_EXC = NUM_BOIDS - 1;
|
||||
var BOID_SIDE = 80;
|
||||
var MAX_SPEED = 0.05;
|
||||
var MAX_DIStANCE = 150;
|
||||
|
||||
window.onresize = function (event) {
|
||||
|
||||
width = canvas.offsetWidth;
|
||||
height = canvas.offsetHeight;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// balance boid number, so we have an avg density across all screen sizes
|
||||
NUM_BOIDS = ~~(width * height * 0.00009);
|
||||
|
||||
init();
|
||||
|
||||
// TODO: use this over init() but redistribute boids fluidly
|
||||
// if (boids.length > NUM_BOIDS) {
|
||||
// boids.splice(NUM_BOIDS - boids.length);
|
||||
// } else {
|
||||
// for(var i = 0; i < NUM_BOIDS - boids.length; i++){
|
||||
// var boid = new Boid(ctx);
|
||||
// boids.push(boid);
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
init();
|
||||
draw();
|
||||
|
||||
function draw() {
|
||||
|
||||
now = Date.now();
|
||||
delta = now - then;
|
||||
|
||||
if (delta > interval) {
|
||||
// update time stuffs
|
||||
|
||||
// Just `then = now` is not enough.
|
||||
// Lets say we set fps at 10 which means
|
||||
// each frame must take 100ms
|
||||
// Now frame executes in 16ms (60fps) so
|
||||
// the loop iterates 7 times (16*7 = 112ms) until
|
||||
// delta > interval === true
|
||||
// Eventually this lowers down the FPS as
|
||||
// 112*10 = 1120ms (NOT 1000ms).
|
||||
// So we have to get rid of that extra 12ms
|
||||
// by subtracting delta (112) % interval (100).
|
||||
// Hope that makes sense.
|
||||
|
||||
then = now - (delta % interval);
|
||||
|
||||
// Execute draw code
|
||||
loop();
|
||||
}
|
||||
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function init(){
|
||||
|
||||
boids = [];
|
||||
|
||||
for(var i = 0; i < NUM_BOIDS; i++){
|
||||
var boid = new Boid(ctx);
|
||||
|
||||
boids.push(boid);
|
||||
}
|
||||
|
||||
prevTime = +new Date;
|
||||
setTimeout(onRandomChange, 200);
|
||||
}
|
||||
|
||||
function onRandomChange(){
|
||||
isRandom = true;
|
||||
|
||||
if(isRandom){
|
||||
for(var i = 0; i < NUM_BOIDS; i++){
|
||||
var aclX = 10 * (Math.random() - .5);
|
||||
var aclY = 10 * (Math.random() - .5);
|
||||
|
||||
randomAclX[i] = aclX;
|
||||
randomAclY[i] = aclY;
|
||||
}
|
||||
|
||||
setTimeout(onRandomChange, 10000);
|
||||
}else{
|
||||
setTimeout(onRandomChange, 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function loop(){
|
||||
var curTime = +new Date;
|
||||
var duration = (curTime - prevTime)/1000;
|
||||
prevTime = curTime;
|
||||
|
||||
// ctx.fillStyle = '#fff';
|
||||
// ctx.fillRect(0, 0, width, height);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
for(var i = 0; i < boids.length; i++){
|
||||
var b = boids[i];
|
||||
if(!isRandom){
|
||||
rule1(i);
|
||||
rule2(i);
|
||||
rule3(i);
|
||||
}else{
|
||||
b.vx += randomAclX[i]
|
||||
b.vy += randomAclY[i];
|
||||
}
|
||||
|
||||
var speed = Math.sqrt(b.vx * b.vx + b.vy * b.vy);
|
||||
if(speed >= MAX_SPEED) {
|
||||
var r = MAX_SPEED / speed;
|
||||
b.vx *= r;
|
||||
b.vy *= r;
|
||||
}
|
||||
|
||||
if(!isRandom){
|
||||
if(b.x < 0 && b.vx < 0 || b.x > width && b.vx > 0) b.vx *= -1;
|
||||
if(b.y < 0 && b.vy < 0 || b.y > height && b.vy > 0) b.vy *= -1;
|
||||
}
|
||||
|
||||
|
||||
b.x += b.vx;
|
||||
b.y += b.vy;
|
||||
|
||||
if(isRandom){
|
||||
if(b.x < 0) b.x += width;
|
||||
if(b.x > width) b.x -= width;
|
||||
|
||||
if(b.y < 0)b.y += height;
|
||||
if(b.y > height)b.y -= height;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for(var i = 0; i < boids.length; i++){
|
||||
var boidA = boids[i];
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, .3)';
|
||||
ctx.arc(boidA.x , boidA.y, boidA.rad, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
|
||||
// ctx.beginPath();
|
||||
// ctx.fillStyle = 'rgba(255, 255, 255, .1)';
|
||||
// ctx.arc(boidA.x , boidA.y, 6, 0, 2 * Math.PI);
|
||||
// ctx.fill();
|
||||
// ctx.closePath();
|
||||
|
||||
for(var j = i; j < boids.length; j++){
|
||||
var boidB = boids[j];
|
||||
var dx = boidA.x - boidB.x;
|
||||
var dy = boidA.y - boidB.y;
|
||||
|
||||
var dis = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if(dis < MAX_DIStANCE){
|
||||
var alpha = (MAX_DIStANCE - dis) / MAX_DIStANCE;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, ' + alpha + ')';
|
||||
ctx.moveTo(boidA.x, boidA.y);
|
||||
ctx.lineTo(boidB.x, boidB.y);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.closePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rule1(index){
|
||||
var c = {x: 0, y: 0};
|
||||
|
||||
for(var i = 0; i < boids.length; i++){
|
||||
|
||||
if(i != index){
|
||||
c.x += boids[i].x;
|
||||
c.y += boids[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
c.x = c.x / NUM_BOIDS_EXC;
|
||||
c.y = c.y / NUM_BOIDS_EXC;
|
||||
|
||||
boids[index].vx += (c.x - boids[index].x)/100;
|
||||
boids[index].vy += (c.y - boids[index].y)/100;
|
||||
}
|
||||
|
||||
function rule2(index){
|
||||
for( var i = 0; i < boids.length; i++){
|
||||
var d = getDistance(boids[i], boids[index]);
|
||||
if(d < BOID_SIDE){
|
||||
boids[index].vx -= boids[i].x - boids[index].x;
|
||||
boids[index].vy -= boids[i].y - boids[index].y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rule3(index){
|
||||
var pv = {x: 0, y: 0};
|
||||
for(var i = 0; i < boids.length; i++){
|
||||
if(i != index){
|
||||
pv.x += boids[i].vx;
|
||||
pv.y += boids[i].vy;
|
||||
}
|
||||
}
|
||||
|
||||
pv.x /= NUM_BOIDS_EXC;
|
||||
pv.y /= NUM_BOIDS_EXC;
|
||||
|
||||
boids[index].vx += (pv.x - boids[index].vx) / 8;
|
||||
boids[index].vy += (pv.y - boids[index].vy) / 8;
|
||||
}
|
||||
|
||||
function getDistance(p1, p2){
|
||||
var dx = p1.x - p2.x;
|
||||
var dy = p1.y -p2.y;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
}
|
||||
}());
|
|
@ -5,6 +5,17 @@
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
min-height: 600px;
|
||||
|
@ -93,4 +104,8 @@
|
|||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--space {
|
||||
background: linear-gradient(to top right,#4cddff,#3b9aca 10%,#2d6ca5 20%,#2a5291 27%,#283d81 35%,#222654 50%,#1e1635 63%,#0c0010 93%,#000);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue