虾塘-养鱼养虾项目
使用 canvas 实现 鱼和虾游泳
在线访问 https://docs.ffffee.com/examples/pond/index.html
源码
index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>虾塘</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="pond" width="800" height="600"></canvas>
<script src="pond.js"></script>
<script>
function setCanvasSize() {
const pond = document.querySelector("#pond");
pond.style.width = window.innerWidth + "px";
pond.style.height = window.innerHeight + "px";
pond.width = window.innerWidth;
pond.height = window.innerHeight;
}
setCanvasSize();
window.addEventListener("resize", setCanvasSize);
</script>
</body>
</html>
pond.js
js
var canvas = document.getElementById("pond");
var ctx = canvas.getContext("2d");
var fishTypes = [
{ name: "金鱼", color: "#FFD700" },
{ name: "鲤鱼", color: "#FF4500" },
{ name: "鲨鱼", color: "#708090" },
];
var fishes = [];
var shrimps = [];
for (var i = 0; i < 15; i++) {
var fishType = fishTypes[Math.floor(Math.random() * fishTypes.length)];
fishes.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
speed: 1 + Math.random() * 2,
direction: Math.random() * Math.PI * 2,
hunger: Math.random() * 50,
mood: "开心",
type: fishType,
size: 1,
});
shrimps.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
speed: 1 + Math.random() * 2,
direction: Math.random() * Math.PI * 2,
size: 1,
});
}
var food = [];
canvas.addEventListener("click", function (e) {
var rect = canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
food.push({ x: x, y: y, size: 10 });
});
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPond();
food.forEach(function (f, index) {
drawFood(f.x, f.y, f.size);
if (f.size <= 0) {
food.splice(index, 1);
}
});
fishes.forEach(function (fish, index) {
drawFish(fish.x, fish.y, fish.type.color, fish.size);
if (food.length > 0) {
var closestFood = food[0];
var minDist = distance(fish.x, fish.y, closestFood.x, closestFood.y);
for (var i = 1; i < food.length; i++) {
var dist = distance(fish.x, fish.y, food[i].x, food[i].y);
if (dist < minDist) {
closestFood = food[i];
minDist = dist;
}
}
fish.direction = Math.atan2(
closestFood.y - fish.y,
closestFood.x - fish.x
);
if (minDist < 5) {
closestFood.size -= 1;
fish.hunger -= 10;
fish.size += 0.1; // 鱼长大
if (fish.hunger < 0) fish.hunger = 0;
fish.mood = "开心"; // 鱼吃到食物,心情变好
}
}
fish.x += Math.cos(fish.direction) * fish.speed;
fish.y += Math.sin(fish.direction) * fish.speed;
if (
fish.x < 0 ||
fish.x > canvas.width ||
fish.y < 0 ||
fish.y > canvas.height
) {
fish.direction += Math.PI;
}
drawFish(fish.x, fish.y, fish.type.color);
drawFishInfo(fish.x, fish.y, fish.type.name, fish.hunger, fish.mood);
// 如果鱼饥饿,心情变坏
if (fish.hunger > 80) {
fish.mood = "饿";
}
// 如果鱼长时间未吃到食物,心情变沮丧
if (fish.hunger > 90) {
fish.mood = "沮丧";
}
// 每帧鱼的饥饿度增加
fish.hunger += 0.1;
});
shrimps.forEach(function (shrimp) {
drawShrimp(shrimp.x, shrimp.y, shrimp.size);
shrimp.x += Math.cos(shrimp.direction) * shrimp.speed;
shrimp.y += Math.sin(shrimp.direction) * shrimp.speed;
if (
shrimp.x < 0 ||
shrimp.x > canvas.width ||
shrimp.y < 0 ||
shrimp.y > canvas.height
) {
shrimp.direction += Math.PI;
}
drawShrimp(shrimp.x, shrimp.y);
});
requestAnimationFrame(update);
}
function drawPond() {
// 画塘
ctx.fillStyle = "#00aaff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function drawFish(x, y, color, size) {
ctx.fillStyle = color;
// 画鱼的身体
ctx.beginPath();
ctx.ellipse(x, y, size * 15, size * 5, 0, 0, Math.PI * 2);
ctx.fill();
// 画鱼的尾巴
ctx.beginPath();
ctx.moveTo(x + size * 15, y);
ctx.lineTo(x + size * 25, y + size * 5);
ctx.lineTo(x + size * 25, y - size * 5);
ctx.closePath();
ctx.fill();
// 画鱼的眼睛
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(x + size * 2, y - size * 2, size * 2, 0, Math.PI * 2);
ctx.fill();
}
function drawFishInfo(x, y, name, hunger, mood) {
ctx.fillStyle = "black";
ctx.fillText("品种: " + name, x, y - 40);
ctx.fillText("饥饿度: " + Math.floor(hunger), x, y - 30);
ctx.fillText("心情: " + mood, x, y - 20);
}
function drawShrimp(x, y, size) {
ctx.fillStyle = "#FF4500";
// 画虾的身体
ctx.beginPath();
ctx.ellipse(x, y, size * 6, size * 3, 0, 0, Math.PI * 2);
ctx.fill();
// 画虾的尾巴
ctx.beginPath();
ctx.moveTo(x - size * 6, y);
ctx.lineTo(x - size * 10, y + size * 4);
ctx.lineTo(x - size * 10, y - size * 4);
ctx.closePath();
ctx.fill();
// 画虾的触须
ctx.strokeStyle = "#FF4500";
ctx.beginPath();
ctx.moveTo(x + size * 6, y);
ctx.lineTo(x + size * 12, y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x + size * 6, y);
ctx.lineTo(x + size * 10, y + size * 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x + size * 6, y);
ctx.lineTo(x + size * 10, y - size * 2);
ctx.stroke();
}
function drawFood(x, y, size) {
if (size <= 0) return; // 如果大小为0或负,不画这块食物
ctx.fillStyle = "#8B4513";
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
function distance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
update();