天天看点

html5绘制小鱼,HTML5 Canvas 深海游弋的鱼群

JavaScript

语言:

JaveScriptBabelCoffeeScript

确定

var FOLLOW_DISTANCE = 100;

var Fish = function() {

// dx/yx is current speed, ox/oy is the previous one

this.ox = this.dx = Math.random() - 0.5;

this.oy = this.dy = Math.random() - 0.5;

this.x = canvas.width * Math.random();

this.y = canvas.height * Math.random();

// A couple of helper functions, the names should describe their purpose

Fish.prototype.angleToClosestFish = function(otherFish) {

otherFish = otherFish == null ? this.following : otherFish;

if (otherFish) {

return Math.atan2(otherFish.y - this.y, otherFish.x - this.x);

} else {

return Number.MAX_VALUE;

}

}

Fish.prototype.angleFromFishDirectionToClosestFish = function(otherFish) {

otherFish = otherFish == null ? this.following : otherFish;

if (otherFish) {

return Math.abs(deltaAngle(this.angle, this.angleToClosestFish(otherFish)));

} else {

return Number.MAX_VALUE;

}

}

Fish.prototype.angleDirectionDifference = function(otherFish) {

otherFish = otherFish == null ? this.following : otherFish;

if (otherFish) {

Math.abs(deltaAngle(this.angle, otherFish.angle));

} else {

return Number.MAX_VALUE;

}

}

// Update the fish "physics"

Fish.prototype.calc = function() {

this.ox = this.dx;

this.oy = this.dy;

var maxSpeed = 1;

//Do I need to find another fish buddy?

if (this.following == null || py(this.x - this.following.x, this.y - this.following.y) > FOLLOW_DISTANCE) {

this.following = null;

//attract closer to other fish - find closest

var closestDistance = Number.MAX_VALUE;

for (var i = 0; i < fishes.length; i++) {

var fish = fishes[i];

if (fish != this) {

var distance = py(this.x - fish.x, this.y - fish.y);

// Is it closer, within the max distance and within the sector that the fish can see?

if (distance < closestDistance && fish.following != this && distance < FOLLOW_DISTANCE && this.angleFromFishDirectionToClosestFish(fish) < Math.PI * 0.25) {

closestDistance = distance;

this.following = fish;

}

}

}

}

// Fish is following another

if (this.following != null) {

// Go closer to other fish

this.followingDistance = py(this.x - this.following.x, this.y - this.following.y);

this.distanceFactor = 1 - this.followingDistance / FOLLOW_DISTANCE;

// If going head on, just break a little before following

if (this.angleDirectionDifference() > (Math.PI * 0.9) && // On colliding angle?

this.angleFromFishDirectionToClosestFish() < (Math.PI * 0.2)) { // In colliding sector?

this.dx += this.following.x * 0.1;

this.dy += this.following.y * 0.1;

if (keyDown) affinityLine(this.following, this, "yellow");

} else if (this.followingDistance > FOLLOW_DISTANCE * 0.3) { // Dont go closer if close

this.dx += Math.cos(this.angleToClosestFish()) * (0.05 * this.distanceFactor);

this.dy += Math.sin(this.angleToClosestFish()) * (0.05 * this.distanceFactor);

if (keyDown) affinityLine(this.following, this, "red");

}

}

// Go closer to center, crashing into the canvas walls is just silly!

if (this.x < canvas.width * .1 || this.x > canvas.width * .9 || this.y < canvas.height * .2 || this.y > canvas.height * .8) {

this.dx += (canvas.width / 2 - this.x) / 5000;

this.dy += (canvas.height / 2 - this.y) / 5000;

}

// Poor little fishies are2 scared of your cursor

if (py(this.x - cursor.x, this.y - cursor.y) < FOLLOW_DISTANCE * 0.75) {

this.dx -= (cursor.x - this.x) / 500;

this.dy -= (cursor.y - this.y) / 500;

maxSpeed = 4;

if (keyDown) affinityLine(cursor, this, "green");

}

// Calculate heading from new speed

this.angle = Math.atan2(this.dy, this.dx);

// Grab the speed from the vectors, and normalize it

var speed = Math.max(0.1, Math.min(maxSpeed, py(this.dx, this.dy)));

// Recreate speed vector from recombining angle of direction with normalized speed

this.dx = Math.cos(this.angle) * (speed + speedBoost);

this.dy = Math.sin(this.angle) * (speed + speedBoost);

// Fish like to move it, move it!

this.x += this.dx;

this.y += this.dy;

}

}

var canvas = document.getElementById('fishtank');

var context = canvas.getContext('2d');

var fishes = [];

var speedBoostCountdown = 100,

speedBoost = 0,

SPEED_BOOST = 2;

var fishBitmap = new Image()

fishBitmap.onload = function() {

update();

};

fishBitmap.src = "/uploads/141102/fish.png";

//Draw Circle

function draw(f) {

var r = f.angle + Math.PI;

context.translate(f.x, f.y);

context.rotate(r);

var w = 20;

var acc = py(f.dx - f.ox, f.dy - f.oy) / 0.05;

// If a fish does a "flip", make it less wide

if (acc > 1) {

w = 10 + 10 / acc;

}

context.drawImage(fishBitmap, 0, 0, w, 6);

context.rotate(-r);

context.translate(-f.x, -f.y);

}

// Pythagoras shortcut

function py(a, b) {

return Math.sqrt(a * a + b * b);

}

//------------ USER INPUT START -------------

var cursor = {

x: 0,

y: 0

};

var cursorDown = false,

keyDown = false;

document.onmousemove = function(e) {

cursor.x = e.pageX - (window.innerWidth / 2 - canvas.width / 2);

cursor.y = e.pageY - (window.innerHeight / 2 - canvas.height / 2);

}

document.onmouseout = function(e) { //Out of screen is not a valid pos

cursor.y = cursor.x = Number.MAX_VALUE;

}

document.onmousedown = function() {

activateSpeedBoost();

cursorDown = true;

}

document.onmouseup = function() {

cursorDown = false;

}

document.onkeydown = function() {

keyDown = true;

}

document.onkeyup = function() {

keyDown = false;

}

//------------ USER INPUT STOP -------------

function deltaAngle(f, o) { //Find the shortest angle between two

var r = f - o;

return Math.atan2(Math.sin(r), Math.cos(r));

}

function affinityLine(f, o, c) { //Draw a line with pretty gradient

var grad = context.createLinearGradient(f.x, f.y, o.x, o.y);

grad.addColorStop(0, c);

grad.addColorStop(1, "black");

context.strokeStyle = grad;

context.beginPath();

context.moveTo(f.x, f.y);

context.lineTo(o.x, o.y);

context.stroke();

}

function activateSpeedBoost() {

speedBoostCountdown = 400 + Math.round(400 * Math.random());

speedBoost = SPEED_BOOST;

}

//Update and draw all of them

function update() {

if (fishes.length < 600) {

fishes.push(new Fish());

}

if (!cursorDown) {

//clear the canvas

canvas.width = canvas.width; //Try commenting this line :-)

//Update and draw fish

for (var i = 0; i < fishes.length; i++) {

var fish = fishes[i];

fish.calc();

draw(fish);

}

}

speedBoostCountdown--;

if (speedBoostCountdown < 0) {

activateSpeedBoost();

}

if (speedBoost > 0) {

speedBoost -= SPEED_BOOST / 80; //Reduce speed bost fast!

} else {

speedBoost = 0;

}

requestAnimationFrame(update);

}