Web網頁開發——水果忍者

1.介紹

復刻經典小游戲——水果忍者

2.預覽

在這里插入圖片描述

3.代碼

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Fruit Ninja Web Demo</title><style>body {margin: 0;padding: 0;overflow: hidden;background-color: #333;font-family: Arial, sans-serif;touch-action: none;}#gameCanvas {background-color: #87CEEB;display: block;margin: 0 auto;max-width: 100%;max-height: 100vh;}#gameContainer {position: relative;width: 100%;height: 100vh;display: flex;flex-direction: column;align-items: center;justify-content: center;}#startScreen, #gameOverScreen {position: absolute;top: 0;left: 0;width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: rgba(0, 0, 0, 0.7);color: white;z-index: 10;}#gameOverScreen {display: none;}button {background-color: #4CAF50;border: none;color: white;padding: 15px 32px;text-align: center;text-decoration: none;display: inline-block;font-size: 16px;margin: 4px 2px;cursor: pointer;border-radius: 5px;}.scoreBoard {position: absolute;top: 10px;left: 10px;color: white;font-size: 24px;z-index: 5;text-shadow: 2px 2px 4px black;}.comboText {position: absolute;color: yellow;font-size: 36px;font-weight: bold;text-shadow: 2px 2px 4px black;opacity: 1;transition: opacity 1s, transform 1s;z-index: 5;}</style>
</head>
<body><div id="gameContainer"><div id="startScreen"><h1>Fruit Ninja Web Demo</h1><p>Slice fruits with your mouse or finger to score points!</p><p>Avoid bombs or you'll lose!</p><button id="startButton">Start Game</button></div><canvas id="gameCanvas"></canvas><div id="gameOverScreen"><h1>Game Over</h1><p>Your Score: <span id="finalScore">0</span></p><button id="restartButton">Play Again</button></div><div class="scoreBoard">Score: <span id="scoreDisplay">0</span></div></div><script>// Game variablesconst canvas = document.getElementById('gameCanvas');const ctx = canvas.getContext('2d');const startScreen = document.getElementById('startScreen');const gameOverScreen = document.getElementById('gameOverScreen');const startButton = document.getElementById('startButton');const restartButton = document.getElementById('restartButton');const scoreDisplay = document.getElementById('scoreDisplay');const finalScore = document.getElementById('finalScore');let score = 0;let gameActive = false;let gameObjects = [];let sliceTrail = [];let sliceActive = false;let lastTimestamp = 0;let spawnTimer = 0;let comboCount = 0;let comboTimer = 0;// Resize canvasfunction resizeCanvas() {canvas.width = window.innerWidth;canvas.height = window.innerHeight;}// Initialize gamefunction initGame() {resizeCanvas();score = 0;gameObjects = [];sliceTrail = [];sliceActive = false;scoreDisplay.textContent = score;gameActive = true;lastTimestamp = 0;spawnTimer = 0;// Start animationrequestAnimationFrame(gameLoop);}// Game objects classesclass GameObject {constructor(x, y, type) {this.x = x;this.y = y;this.type = type;this.sliced = false;this.velocityX = Math.random() * 8 - 4;this.velocityY = -15 - Math.random() * 5;this.gravity = 0.5;this.rotation = 0;this.rotationSpeed = Math.random() * 0.1 - 0.05;this.size = 40 + Math.random() * 20;if (this.type === 'banana') {this.color = '#FFD700';} else if (this.type === 'bomb') {this.color = '#333';this.size = 30 + Math.random() * 10;} else if (this.type === 'pomegranate') {this.color = '#FF4500';this.size = 50 + Math.random() * 10;} else if (this.type === 'watermelon') {this.color = '#32CD32';this.size = 60 + Math.random() * 10;} else {// Default applethis.color = '#FF0000';}this.slicedColor1 = this.color;this.slicedColor2 = this.type === 'watermelon' ? '#FF6347' : this.color;// For slice animationthis.sliceAngle = 0;this.slicePart1 = { x: 0, y: 0, vx: 0, vy: 0, rotation: 0 };this.slicePart2 = { x: 0, y: 0, vx: 0, vy: 0, rotation: 0 };}update() {if (this.sliced) {// Update sliced partsthis.slicePart1.x += this.slicePart1.vx;this.slicePart1.y += this.slicePart1.vy;this.slicePart1.vy += this.gravity;this.slicePart1.rotation += 0.05;this.slicePart2.x += this.slicePart2.vx;this.slicePart2.y += this.slicePart2.vy;this.slicePart2.vy += this.gravity;this.slicePart2.rotation += 0.05;return this.slicePart1.y < canvas.height && this.slicePart2.y < canvas.height;} else {// Update normal objectthis.x += this.velocityX;this.y += this.velocityY;this.velocityY += this.gravity;this.rotation += this.rotationSpeed;return this.y < canvas.height + 100;}}draw() {ctx.save();if (this.sliced) {// Draw first slice partctx.save();ctx.translate(this.slicePart1.x, this.slicePart1.y);ctx.rotate(this.slicePart1.rotation);ctx.beginPath();ctx.arc(0, 0, this.size / 2, 0, Math.PI, false);ctx.fillStyle = this.slicedColor1;ctx.fill();if (this.type === 'watermelon') {ctx.beginPath();ctx.arc(0, 0, this.size / 2 - 5, 0, Math.PI, false);ctx.fillStyle = this.slicedColor2;ctx.fill();}ctx.restore();// Draw second slice partctx.save();ctx.translate(this.slicePart2.x, this.slicePart2.y);ctx.rotate(this.slicePart2.rotation);ctx.beginPath();ctx.arc(0, 0, this.size / 2, Math.PI, 2 * Math.PI, false);ctx.fillStyle = this.slicedColor1;ctx.fill();if (this.type === 'watermelon') {ctx.beginPath();ctx.arc(0, 0, this.size / 2 - 5, Math.PI, 2 * Math.PI, false);ctx.fillStyle = this.slicedColor2;ctx.fill();}ctx.restore();} else {// Draw normal objectctx.translate(this.x, this.y);ctx.rotate(this.rotation);if (this.type === 'bomb') {// Draw bombctx.beginPath();ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);ctx.fillStyle = this.color;ctx.fill();// Draw fusectx.beginPath();ctx.moveTo(0, -this.size / 2);ctx.quadraticCurveTo(10, -this.size / 2 - 15, 20, -this.size / 2 - 10);ctx.lineWidth = 3;ctx.strokeStyle = '#8B4513';ctx.stroke();} else if (this.type === 'banana') {// Draw bananactx.beginPath();ctx.arc(0, 0, this.size / 2, 0.3 * Math.PI, 1.7 * Math.PI);ctx.lineWidth = this.size / 2;ctx.strokeStyle = this.color;ctx.stroke();} else if (this.type === 'watermelon') {// Draw watermelonctx.beginPath();ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);ctx.fillStyle = '#32CD32';ctx.fill();// Inner partctx.beginPath();ctx.arc(0, 0, this.size / 2 - 5, 0, 2 * Math.PI);ctx.fillStyle = '#FF6347';ctx.fill();// Seedsctx.fillStyle = 'black';for (let i = 0; i < 8; i++) {const angle = i * (Math.PI / 4);const distance = this.size / 4;ctx.beginPath();ctx.ellipse(Math.cos(angle) * distance,Math.sin(angle) * distance,3, 5, angle, 0, 2 * Math.PI);ctx.fill();}} else if (this.type === 'pomegranate') {// Draw pomegranatectx.beginPath();ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);ctx.fillStyle = this.color;ctx.fill();// Crownctx.beginPath();ctx.moveTo(-10, -this.size / 2);ctx.lineTo(10, -this.size / 2);ctx.lineTo(0, -this.size / 2 - 10);ctx.closePath();ctx.fillStyle = '#8B4513';ctx.fill();} else {// Draw applectx.beginPath();ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);ctx.fillStyle = this.color;ctx.fill();// Stemctx.beginPath();ctx.moveTo(0, -this.size / 2);ctx.lineTo(0, -this.size / 2 - 7);ctx.lineWidth = 3;ctx.strokeStyle = '#8B4513';ctx.stroke();}}ctx.restore();}checkSlice(slicePath) {if (this.sliced) return false;// Check if the slice path intersects with the objectfor (let i = 1; i < slicePath.length; i++) {const x1 = slicePath[i-1].x;const y1 = slicePath[i-1].y;const x2 = slicePath[i].x;const y2 = slicePath[i].y;// Calculate distance from line segment to center of objectconst distance = distToSegment(this.x, this.y, x1, y1, x2, y2);if (distance < this.size / 2) {// Calculate slice anglethis.sliceAngle = Math.atan2(y2 - y1, x2 - x1);// Set the slice partsthis.slicePart1.x = this.x;this.slicePart1.y = this.y;this.slicePart1.vx = this.velocityX - 1 + Math.random() * 2;this.slicePart1.vy = this.velocityY - 2;this.slicePart2.x = this.x;this.slicePart2.y = this.y;this.slicePart2.vx = this.velocityX + 1 + Math.random() * 2;this.slicePart2.vy = this.velocityY - 2;this.sliced = true;// Handle special fruitsif (this.type === 'bomb') {return 'bomb';} else if (this.type === 'banana') {return 'banana';} else if (this.type === 'pomegranate') {return 'pomegranate';} else {return 'fruit';}}}return false;}}// Helper function to calculate distance from point to line segmentfunction sqr(x) { return x * x; }function dist2(v, w) { return sqr(v.x - w.x) + sqr(v.y - w.y); }function distToSegmentSquared(p, v, w) {const l2 = dist2(v, w);if (l2 === 0) return dist2(p, v);let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;t = Math.max(0, Math.min(1, t));return dist2(p, {x: v.x + t * (w.x - v.x),y: v.y + t * (w.y - v.y)});}function distToSegment(px, py, x1, y1, x2, y2) {return Math.sqrt(distToSegmentSquared({x: px, y: py}, {x: x1, y: y1}, {x: x2, y: y2}));}// Spawn new game objectsfunction spawnObjects() {const types = ['apple', 'watermelon', 'banana', 'pomegranate'];const x = Math.random() * canvas.width;const y = canvas.height + 20;// 20% chance to spawn a bombif (Math.random() < 0.2) {gameObjects.push(new GameObject(x, y, 'bomb'));} else {const type = types[Math.floor(Math.random() * types.length)];gameObjects.push(new GameObject(x, y, type));}}// Show combo text animationfunction showComboText(count) {const comboText = document.createElement('div');comboText.className = 'comboText';comboText.textContent = `COMBO x${count}!`;comboText.style.left = `${canvas.width / 2 - 100}px`;comboText.style.top = `${canvas.height / 2 - 50}px`;document.getElementById('gameContainer').appendChild(comboText);setTimeout(() => {comboText.style.opacity = '0';comboText.style.transform = 'translateY(-50px)';setTimeout(() => {comboText.remove();}, 1000);}, 10);}// Spawn small fruits (for pomegranate effect)function spawnSmallFruits(x, y) {const count = 5 + Math.floor(Math.random() * 5);for (let i = 0; i < count; i++) {const fruit = new GameObject(x, y, 'apple');fruit.size = 15 + Math.random() * 10;fruit.velocityX = Math.random() * 10 - 5;fruit.velocityY = -10 - Math.random() * 5;gameObjects.push(fruit);}}// Game loopfunction gameLoop(timestamp) {if (!gameActive) return;// Calculate delta timeconst deltaTime = timestamp - lastTimestamp;lastTimestamp = timestamp;// Clear canvasctx.clearRect(0, 0, canvas.width, canvas.height);// Spawn objectsspawnTimer += deltaTime;if (spawnTimer > 1000) {spawnObjects();spawnTimer = 0;}// Draw slice trailif (sliceActive && sliceTrail.length > 1) {ctx.beginPath();ctx.moveTo(sliceTrail[0].x, sliceTrail[0].y);for (let i = 1; i < sliceTrail.length; i++) {ctx.lineTo(sliceTrail[i].x, sliceTrail[i].y);}ctx.strokeStyle = 'white';ctx.lineWidth = 5;ctx.lineCap = 'round';ctx.lineJoin = 'round';ctx.stroke();// Trail glow effectctx.shadowColor = '#FFF';ctx.shadowBlur = 15;ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';ctx.lineWidth = 15;ctx.stroke();ctx.shadowBlur = 0;}// Update and draw game objectslet fruitSlicedThisFrame = 0;for (let i = gameObjects.length - 1; i >= 0; i--) {const obj = gameObjects[i];// Check for slicingif (sliceActive && sliceTrail.length > 3 && !obj.sliced) {const sliceResult = obj.checkSlice(sliceTrail);if (sliceResult) {if (sliceResult === 'bomb') {// Game over if bomb is slicedgameActive = false;finalScore.textContent = score;gameOverScreen.style.display = 'flex';return;} else {fruitSlicedThisFrame++;if (sliceResult === 'fruit') {score += 10;} else if (sliceResult === 'banana') {// Banana gives double points for a short timescore += 20;} else if (sliceResult === 'pomegranate') {score += 30;// Spawn small fruitsspawnSmallFruits(obj.x, obj.y);}scoreDisplay.textContent = score;}}}const isVisible = obj.update();if (isVisible) {obj.draw();} else {gameObjects.splice(i, 1);}}// Handle comboif (fruitSlicedThisFrame > 1) {comboCount = fruitSlicedThisFrame;comboTimer = 0;// Add combo bonusconst comboBonus = comboCount * 5;score += comboBonus;scoreDisplay.textContent = score;// Show combo textshowComboText(comboCount);} else if (fruitSlicedThisFrame === 1) {comboTimer += deltaTime;if (comboTimer > 500) {comboCount = 0;}}// Reduce slice trail graduallyif (sliceTrail.length > 0 && !sliceActive) {sliceTrail.shift();}// Continue animationrequestAnimationFrame(gameLoop);}// Event listeners for mouse/touchcanvas.addEventListener('mousedown', (e) => {sliceActive = true;sliceTrail = [];sliceTrail.push({x: e.clientX,y: e.clientY});});canvas.addEventListener('mousemove', (e) => {if (sliceActive) {sliceTrail.push({x: e.clientX,y: e.clientY});// Limit trail lengthif (sliceTrail.length > 20) {sliceTrail.shift();}}});canvas.addEventListener('mouseup', () => {sliceActive = false;});canvas.addEventListener('mouseleave', () => {sliceActive = false;});// Touch eventscanvas.addEventListener('touchstart', (e) => {e.preventDefault();sliceActive = true;sliceTrail = [];sliceTrail.push({x: e.touches[0].clientX,y: e.touches[0].clientY});});canvas.addEventListener('touchmove', (e) => {e.preventDefault();if (sliceActive) {sliceTrail.push({x: e.touches[0].clientX,y: e.touches[0].clientY});// Limit trail lengthif (sliceTrail.length > 20) {sliceTrail.shift();}}});canvas.addEventListener('touchend', (e) => {e.preventDefault();sliceActive = false;});// Button event listenersstartButton.addEventListener('click', () => {startScreen.style.display = 'none';initGame();});restartButton.addEventListener('click', () => {gameOverScreen.style.display = 'none';initGame();});// Handle window resizewindow.addEventListener('resize', resizeCanvas);// Initial setupresizeCanvas();</script>
</body>
</html>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/71691.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/71691.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/71691.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Flink銀行反欺詐系統設計方案】6.用戶畫像數據與反欺詐系統的關聯思路

【Flink銀行反欺詐系統設計方案】6.用戶畫像數據與反欺詐系統的關聯思路 概要1. 用戶畫像數據與反欺詐系統的關聯思路1.1 用戶畫像數據內容1.2 數據賦能反欺詐的核心邏輯 2. 用戶畫像賦能反欺詐的3個案例2.1 案例1&#xff1a;消費習慣異常檢測2.2 案例2&#xff1a;設備/地理位…

如何在unity中完整錄制一段動畫

在動畫制作中&#xff0c;需要對接音頻部門進行音效的制作。通常需要完整且無多余幀數的動畫視頻作為時間和幀數對幀參考&#xff0c;這時候手動錄屏就會顯得不夠精確&#xff0c;這里分享一個插件錄制方法&#xff0c;可以自定義錄制起始位置&#xff0c;0幀起手完整錄制。 錄…

Jetson Orin 安裝 onnxruntime

Jetson Orin 安裝 onnxruntime onnxruntime在Jetson上安裝只需注意三件事&#xff1a; 版本&#xff01; 版本&#xff01; 還是TMD版本&#xff01; 本機環境 Jectpack : 5.1.2CUDA : 11.4cuDNN &#xff1a;8.6.0 版本說明 關于onnxruntime的版本適配不同的官方有不同的…

Manus AI : Agent 元年開啟.pdf

Manus AI : Agent 元年開啟.pdf 是由華泰證券出品的一份調研報告&#xff0c;共計23頁。報告詳細介紹了Manus AI 及 Agent&#xff0c;主要包括Manus AI 的功能、優勢、技術能力&#xff0c;Agent 的概念、架構、應用場景&#xff0c;以及 AI Agent 的類型和相關案例&#xff0…

【為什么會有 map、weakmap 類型?】

為什么會有 map、weakmap 類型? 傳統對象的局限性催生 Map?1. 鍵類型單一性?2. 有序性與迭代支持?3. 性能優化場景? 內存管理需求催生 WeakMap?1.弱引用機制?2. 私有數據存儲?3. 規避循環引用問題? 總結 傳統對象的局限性催生 Map? 1. 鍵類型單一性? 傳統對象&…

SpringSecurity認證授權完整流程

SpringSecurity認證流程&#xff1a;loadUserByUsername&#xff08;&#xff09;方法內部實現。 實現步驟&#xff1a; 構建一個自定義的service接口&#xff0c;實現SpringSecurity的UserDetailService接口。建一個service實現類&#xff0c;實現此loadUserByUsername方法。…

本地部署DeepSeek R1大數據模型知識庫

DeepSeek-V3 的綜合能力 DeepSeek-V3 在推理速度上相較歷史模型有了大幅提升。在目前大模型主流榜單中&#xff0c;DeepSeek-V3 在開源模型中位列榜首&#xff0c;與世界上最先進OpenAI 閉源模型不分伯仲。 1、下載Ollama運行大數據庫 Ollama支持 Llama 3.3, DeepSeek-R1, Phi-…

云端秘境:EC2的奇幻之旅

在一個神秘的云端世界里&#xff0c;流傳著一個傳說——只要掌握了 EC2&#xff08;Elastic Compute Cloud&#xff09; 的奧秘&#xff0c;就能召喚出強大的騎士軍團&#xff0c;在云端之上建造屬于自己的帝國。年輕的程序法師 艾倫&#xff08;Allen&#xff09;&#xff0c;…

【javaEE】多線程(基礎)

1.????前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 親愛的朋友們&#x1f44b;&#x1f44b;&#xff0c;這里是E綿綿呀????。 如果你喜歡這篇文章&#xff0c;請別吝嗇你的點贊????和收藏&#x1f4d6;&#x1f4d6;。如果你對我的…

一周學會Flask3 Python Web開發-WTForms表單驗證

鋒哥原創的Flask3 Python Web開發 Flask3視頻教程&#xff1a; 2025版 Flask3 Python web開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 我們可以通過WTForms表單類屬性的validators屬性來實現表單驗證。 常用的WTForms驗證器 驗證器說明DataRequired(messageNo…

STM32標準庫代碼詳解之GPIO

GPIO的初始化代碼如下&#xff1a; /*開啟時鐘*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘&#xff0c;使用外設必須開啟/*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; //定義結構體變量 GPIO_InitStructure.GPIO_Mode GPIO_Mo…

華為歐拉系統 Tomcat 安裝詳解

1. 安裝或確認安裝 Java Tomcat 需要 Java 環境(JDK 或 JRE)才能運行。如果系統尚未安裝 Java,可以使用以下命令安裝 OpenJDK: # 更新軟件包索引 yum update -y# 安裝 OpenJDK 21(可根據需求安裝其他版本,如 8、11、17 等) yum install -y java-21-openjdk java-21-op…

第六課:數據庫集成:MongoDB與Mongoose技術應用

本文詳細介紹了如何在Node.js應用程序中集成MongoDB數據庫&#xff0c;并使用Mongoose庫進行數據操作。我們將涵蓋MongoDB在Ubuntu 20系統中的安裝、Bash命令的CRUD操作、Mongoose數據建模&#xff08;Schema/Model&#xff09;、關聯查詢與聚合管道&#xff0c;以及實戰案例—…

大數據學習(56)-Impala

&&大數據學習&& &#x1f525;系列專欄&#xff1a; &#x1f451;哲學語錄: 承認自己的無知&#xff0c;乃是開啟智慧的大門 &#x1f496;如果覺得博主的文章還不錯的話&#xff0c;請點贊&#x1f44d;收藏??留言&#x1f4dd;支持一下博主哦&#x1f91…

【C#】async與await介紹

1. 實例1 1.1 代碼 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp1 {class Program{static void Main(string[] args){Method1();Method2();Console.ReadKey();}public static…

【大模型基礎_毛玉仁】1.1 基于統計方法的語言模型

【大模型基礎_毛玉仁】1.1 基于統計方法的語言模型 1.語言模型基礎1.1 基于統計方法的語言模型1.1.1 n-grams 語言模型1.1.2 n-grams 的統計學原理 1.語言模型基礎 語言是概率的。語言模型&#xff08;LanguageModels, LMs&#xff09;旨在準確預測語言符號的概率。 將按照語…

JavaScript(最后一個元素的索引就是數組的長度減 1)array.length - 1

在不同的編程語言中&#xff0c;表示數組中最后一個元素的方法略有不同&#xff0c;但基本思路都是利用數組的長度或索引來實現。 以下是一些常見編程語言中獲取數組最后一個元素的方法&#xff1a; 1. JavaScript: 使用 array.length - 1 索引: 這是最常見和傳統的方法。Java…

SpringCloud中使用服務名調用場景總結

一 API Gateway 在 Spring Cloud API Gateway 中&#xff0c;通過使用 lb:// 前綴可以實現基于服務名的負載均衡路由。以下是具體的配置和使用方法&#xff1a; 1. 配置服務注冊與發現 確保你的服務已經注冊到服務注冊中心&#xff08;如 Nacos 或 Eureka&#xff09;。API …

7V 至 30V 的超寬 VIN 輸入范圍,轉換效率高達 96%的WD5030

WD5030 具備 7V 至 30V 的超寬 VIN 輸入范圍&#xff0c;這一特性使其能夠適應多種不同電壓等級的供電環境&#xff0c;無論是在工業設備中常見的較高電壓輸入&#xff0c;還是在一些便攜式設備經過初步升壓后的電壓&#xff0c;WD5030 都能輕松應對&#xff0c;極大地拓展了應…

深度學習模型Transformer核心組件—自注意力機制

第一章&#xff1a;人工智能之不同數據類型及其特點梳理 第二章&#xff1a;自然語言處理(NLP)&#xff1a;文本向量化從文字到數字的原理 第三章&#xff1a;循環神經網絡RNN&#xff1a;理解 RNN的工作機制與應用場景(附代碼) 第四章&#xff1a;循環神經網絡RNN、LSTM以及GR…