演示地址(第一次打开有点卡哦)水果忍者小游戏
http://kiss-rebounds.gitee.io/fruitninja-game/
- 实现了得分和连击等信息的持久化储存
- 增加了happy模式,下方进度条达到百分之百后开启
- 两个带有buff的水果,分别是运动速度减缓和双倍得分
- 暂停功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="images/fruit/apple.png" />
<title>水果忍者小游戏</title>
<style>
* {
margin: 0px;
padding: 0px;
}
html,
body {
/* 使超出屏幕的部分隐藏 */
width: 100%;
height: 100%;
overflow: hidden;
}
body {
font-family: cursive;
background: url(images/background.jpg);
}
.trace {
width: 1vw;
height: 1vw;
position: absolute;
background: rgba(255, 255, 255);
border-radius: 5px;
z-index: 9;
animation: trace .8s forwards;
}
@keyframes trace {
0% {}
100% {
transform: scale(0);
}
}
.fruit {
position: absolute;
}
.fullFruit,
.buffFruit {
position: absolute;
z-index: 1;
top: 100vh;
}
.fruitRun {
position: absolute;
z-index: 1;
top: 95vh;
animation: fruitRun 3s linear forwards;
}
@keyframes fruitRun {
0% {}
50% {
top: 5vh;
}
100% {
top: 110vh;
transform: translateX(-200px) rotate(1000deg);
}
}
.fruitSplit {
position: absolute;
}
.fruitSplitRun {
position: absolute;
animation: fruitSplitRun 1s linear forwards;
}
@keyframes fruitSplitRun {
0% {}
100% {
top: 110vh;
transform: translateX(-100px) rotate(-666deg);
}
}
.score {
position: absolute;
z-index: 1;
top: 9.5vh;
left: 1.5vw;
font-size: 2vw;
color: aliceblue;
}
.score::before {
font-family: cursive;
content: '得分: ';
}
.boom {
width: 5vw;
height: auto;
position: absolute;
z-index: 9;
top: 95vh;
animation: boom 2.2s linear forwards;
}
@keyframes boom {
0% {}
50% {
top: 5vh;
}
100% {
top: 110vh;
transform: translateX(-100px) rotate(-666deg);
}
}
.play {
position: fixed;
top: -5px;
left: 0px;
z-index: 99;
display: block;
width: 100vw;
height: 110vh;
background-image: url(images/demo.gif);
background-repeat: no-repeat;
background-size: 100% 95%;
}
.cover {
position: fixed;
z-index: 99;
display: block;
width: 100vw;
height: 110vh;
background-color: aliceblue;
opacity: 0;
display: none;
/* 贝塞尔曲线-先快后慢 */
animation: cover 1s cubic-bezier(.22, 1.23, .97, .89) forwards;
}
@keyframes cover {
0% {}
100% {
opacity: 1;
}
}
.crossNumber {
position: fixed;
top: 1vh;
left: 0.3vw;
}
.crossNumber img {
/* 三个叉号的图片 */
width: 3vw;
height: auto;
}
.soundControl {
width: 3vw;
height: auto;
position: fixed;
z-index: 100;
top: 1vh;
right: 1vw;
cursor: pointer;
}
.viewHelp {
color: aliceblue;
position: fixed;
z-index: 101;
top: 2vh;
right: 5vw;
font-size: 2vw;
cursor: pointer;
}
.viewHelp:hover {
text-decoration: underline;
}
.help {
width: 60vw;
height: 60vh;
background: url(images/background.jpg) no-repeat;
background-size: cover;
opacity: 0.1;
border-radius: 30px;
box-shadow: 1px 1px 5px 5px rgb(170, 170, 170);
position: fixed;
z-index: 101;
top: 15vh;
left: calc((100vw - 60vw) / 2);
display: none;
animation: help .5s forwards;
}
@keyframes help {
0% {
transform: translateY(-200px) scale(2);
}
100% {
opacity: 0.9;
}
}
.closeHelp {
width: 4vw;
height: auto;
position: absolute;
top: 2vh;
right: 1vw;
cursor: pointer;
}
.scoreRecord {
width: 30%;
height: 18%;
background-color: rgb(255, 255, 255);
color: #2d1507;
border-radius: 20px;
position: absolute;
top: 26%;
right: 3%;
text-align: center;
line-height: 140%;
font-size: 4vw;
}
.scoreRecord::before {
content: '最高得分记录:';
font-size: 1.5vw;
position: absolute;
color: white;
bottom: 80%;
left: 0%;
text-align: left;
}
.batterRecord {
width: 22%;
height: 18%;
background-color: rgb(255, 255, 255);
color: #2d1507;
border-radius: 20px;
position: absolute;
top: 26%;
right: 41%;
text-align: center;
line-height: 140%;
font-size: 4vw;
}
.batterRecord::before {
content: '最高连击记录:';
font-size: 1.5vw;
position: absolute;
color: white;
bottom: 80%;
left: 0%;
text-align: left;
}
.helpContent {
width: 56%;
height: 32%;
background-color: aliceblue;
border-radius: 10px;
position: absolute;
top: 50%;
right: 3%;
padding: 2%;
font-size: 1.2vw;
}
.helpContent p {
margin-top: 4%;
}
.helpBG {
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0px;
left: 0px;
z-index: 99;
display: none;
}
.showScore {
position: absolute;
color: white;
font-size: 2vw;
animation: showScore 1.5s forwards;
}
@keyframes showScore {
0% {}
100% {
opacity: 0;
}
}
.cutFruitsInfo {
width: 30%;
height: 70%;
background-color: aliceblue;
border-radius: 20px;
position: absolute;
top: 20%;
left: 3%;
}
.cutFruitsInfo::before {
content: '你共切割了:';
font-size: 2vw;
position: absolute;
top: -15%;
left: 1%;
color: white;
}
.cutFruitsInfo .apple,
.cutFruitsInfo .banana,
.cutFruitsInfo .basaha,
.cutFruitsInfo .peach,
.cutFruitsInfo .sandia {
width: 45%;
height: 15%;
background-repeat: no-repeat;
background-size: contain;
position: absolute;
left: 5%;
line-height: 40px;
font-size: 2vw;
padding-left: 26%;
color: #482816;
font-weight: 900;
background-image: var(--fruitImg);
top: var(--top);
}
.cutFruitsInfo .banana {
padding-left: 50%;
}
.cutFruitsInfo .apple::after,
.cutFruitsInfo .banana::after,
.cutFruitsInfo .basaha::after,
.cutFruitsInfo .peach::after,
.cutFruitsInfo .sandia::after {
content: '';
position: absolute;
bottom: -5%;
left: 0;
width: 15vw;
height: 1px;
background-color: #4b2d1b;
}
.happyModelProgress {
position: fixed;
bottom: 5px;
left: calc((100vw - 80vw) / 2);
width: 80vw;
height: 2vh;
z-index: 1;
color: none;
background: none;
}
.happyModelProgress::-webkit-progress-bar {
border-radius: 100px;
background-color: white;
}
.happyModelProgress::-webkit-progress-value {
background-color: rgb(237, 91, 1);
border-radius: 100px;
}
.happyModelText {
width: 100%;
height: auto;
position: fixed;
top: 1%;
left: 0;
color: rgb(234, 115, 41);
animation: happyModelText .5s infinite;
text-align: center;
font-size: 4vw;
font-weight: 900;
display: none;
}
@keyframes happyModelText {
0% {}
50% {
filter: brightness(200%);
}
100% {}
}
.buffState {
position: fixed;
z-index: 1;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
display: none;
}
.showBatter {
position: fixed;
z-index: 1;
top: 15vh;
right: 1vw;
height: 6vh;
width: 8vw;
color: #f85300;
font-size: 4vw;
line-height: 6vh;
text-align: left;
font-family: FZShuTi;
font-weight: 900;
display: none;
}
.showBatter::before {
content: '连击';
position: absolute;
top: 0;
left: -110%;
width: 100%;
height: 100%;
}
.batterAddPoints{
position: fixed;
z-index: 1;
top: 14vh;
right: 5vw;
color: #f85300;
font-size: 4vw;
font-family: FZShuTi;
font-weight: 900;
animation: batterAddPoints 1.5s forwards;
}
@keyframes batterAddPoints{
0% {
}
30% {
transform: scale(2);
top: 14vh;
right: 5vw;
}
99% {
opacity: 1;
}
100% {
top: 6vh;
right: 90vw;
opacity: 0;
}
}
</style>
</head>
<body>
<!-- <div class="batterAddPoints"></div> -->
<div class="play"></div>
<div class="buffState"></div>
<div class="showBatter"></div>
<div class="crossNumber">
<img src="images/x.png">
<img src="images/xx.png">
<img src="images/xxx.png">
</div>
<div class="score"></div>
<div class="cover"></div>
<audio autoplay class="splatter"></audio>
<audio autoplay class="boomAudio"></audio>
<audio autoplay class="throwAudio"></audio>
<audio autoplay class="over"></audio>
<audio src="sound/menu.mp3" autoplay class="menu"></audio>
<img src="images/声音开启.png" class="soundControl" title="音频开关">
<div class="viewHelp">查看帮助</div>
<div class="helpBG"></div>
<div class="help">
<img src="images/close.png" class="closeHelp">
<div class="scoreRecord"></div>
<div class="batterRecord"></div>
<div class="helpContent">
1.在初始页面点击任意位置即可开始游戏。<br>
2.滑动鼠标或手指切割水果(注意有炸弹)。<br>
3.在不同浏览器中,最高得分等记录会不一样。<br>
4.下方的进度条满后,会进入happy模式。<br>
5.happy模式中,水果掉落也不会扣除失误次数。<br>
6.带有buff的水果,颜色会与众不同。<br>
7.buff水果掉落不会扣除失误次数。
</div>
<div class="cutFruitsInfo">
<div class="apple" style="--fruitImg: url(images/fruit/apple.png); --top: 2%;"></div>
<div class="banana" style="--fruitImg: url(images/fruit/banana.png); --top: 20%;"></div>
<div class="basaha" style="--fruitImg: url(images/fruit/basaha.png); --top: 40%;"></div>
<div class="peach" style="--fruitImg: url(images/fruit/peach.png); --top: 60%;"></div>
<div class="sandia" style="--fruitImg: url(images/fruit/sandia.png); --top: 80%;"></div>
</div>
</div>
<progress value="0" min="0" max="100" class="happyModelProgress"></progress>
<p class="happyModelText">HAPPY!</p>
<script>
// 默认为电脑端
let isComputer = true;
// 判断用户是否处于电脑端、移动端横屏、移动端竖屏
const myResize = () => {
let userAgentInfo = navigator.userAgent;
const Agents = ["Android", "iPhone", "symbianOS", "Windows Phone", "iPad", "iPod"]
for (let i = 0; i < Agents.length; i++) {
if (userAgentInfo.indexOf(Agents[i]) > 0) {
// 是移动端
isComputer = false;
if (window.orientation == 180 || window.orientation == 0) {
// 竖屏状态
alert('为了更好的体验,请横屏。')
}
if (window.orientation == 90 || window.orientation == -90) {
// 横屏状态
}
break;
}
// 电脑端
isComputer = true;
}
}
myResize();
// 窗口大小改变时触发myResize函数
window.addEventListener("resize", myResize);
//#region
const body = document.body;
const splatter = document.querySelector('.splatter');
const score = document.querySelector('.score');
const play = document.querySelector('.play');
const cover = document.querySelector('.cover');
const boomAudio = document.querySelector('.boomAudio');
const throwAudio = document.querySelector('.throwAudio');
const menu = document.querySelector('.menu');
const soundControl = document.querySelector('.soundControl');
const viewHelp = document.querySelector('.viewHelp');
const help = document.querySelector('.help');
const helpBG = document.querySelector('.helpBG');
const closeHelp = document.querySelector('.closeHelp');
const scoreRecord = document.querySelector('.scoreRecord');
const audio = document.querySelectorAll('audio');
const crossNumberImg = document.querySelectorAll('.crossNumber img');
const happyModelProgress = document.querySelector('.happyModelProgress');
const happyModelText = document.querySelector('.happyModelText');
const cutFruitsInfoApple = document.querySelector('.cutFruitsInfo .apple');
const cutFruitsInfoBanana = document.querySelector('.cutFruitsInfo .banana');
const cutFruitsInfoBasaha = document.querySelector('.cutFruitsInfo .basaha');
const cutFruitsInfoPeach = document.querySelector('.cutFruitsInfo .peach');
const cutFruitsInfoSandia = document.querySelector('.cutFruitsInfo .sandia');
const buffState = document.querySelector('.buffState');
const showBatter = document.querySelector('.showBatter');
const batterRecord = document.querySelector('.batterRecord');
let [fail, scoreNum, opportunity, invincible, isHappyModel, isDoubleBuff, isRetardBuff, pauseGame] = [false, 0, 3, true, false, false, false, false];
let happyTimer, retardBuffTimer;
let batter = 0,
maxBatterTimer,
maxBatter = 0;
//#endregion
const fruitsInfo = [{
type: 'apple',
width: 5,
height: 5,
score: 2,
img: 'images/fruit/apple.png'
},
{
type: 'banana',
width: 9,
height: 4,
score: 1,
img: 'images/fruit/banana.png'
},
{
type: 'basaha',
width: 4,
height: 4,
score: 1,
img: 'images/fruit/basaha.png'
},
{
type: 'peach',
width: 5,
height: 5,
score: 2,
img: 'images/fruit/peach.png'
},
{
type: 'sandia',
width: 8,
height: 7,
score: 3,
img: 'images/fruit/sandia.png'
},
]
const buffFruitsInfo = [{
type: 'sandia',
width: 8,
height: 7,
img: 'images/fruit/sandia.png',
buff: 'retard',
appearance: 'hue-rotate(135deg)'
}, {
type: 'sandia',
width: 8,
height: 7,
img: 'images/fruit/sandia.png',
buff: 'double',
appearance: 'brightness(250%) saturate(300%)'
}]
const fruitsNotes = {
apple: 0,
banana: 0,
basaha: 0,
peach: 0,
sandia: 0,
}
//输入一个最小值和最大值,则会返回两者之间的随机整数(包括两者)
const randomNum = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 水果的创建
const fruitCreate = () => {
if (pauseGame) return;
let fruitElement = document.createElement('img');
// 随机一个水果种类
let fruitInfoNum = randomNum(0, 4);
// 添加水果运动的class
fruitElement.classList.add('fruit');
fruitElement.classList.add('fruitRun');
fruitElement.classList.add('ordinaryFruit');
// 很重要的一个class,用于判断是否为一个完整的水果
fruitElement.classList.add('fullFruit');
fruitElement.style.width = fruitsInfo[fruitInfoNum].width + 'vw';
fruitElement.style.height = fruitsInfo[fruitInfoNum].height + 'vw';
// 水果的left范围是: 150 ~ 窗口宽度-150
fruitElement.style.left = randomNum(150, window.innerWidth - 150) + 'px';
// 设置水果元素上的属性
fruitElement.setAttribute('src', fruitsInfo[fruitInfoNum].img);
fruitElement.setAttribute('data-score', fruitsInfo[fruitInfoNum].score);
fruitElement.setAttribute('data-type', fruitsInfo[fruitInfoNum].type);
body.appendChild(fruitElement);
}
// buff水果的创建
const buffFruitCreate = () => {
if (pauseGame) return;
let fruitElement = document.createElement('img');
// 随机一个buff水果种类
let buffFruitInfoNum = randomNum(0, 1);
// 添加水果运动的class
fruitElement.classList.add('fruit');
fruitElement.classList.add('fullFruit');
fruitElement.classList.add('fruitRun');
fruitElement.classList.add('buffFruit');
fruitElement.style.width = buffFruitsInfo[buffFruitInfoNum].width + 'vw';
fruitElement.style.height = buffFruitsInfo[buffFruitInfoNum].height + 'vw';
// 水果的left范围是: 150 ~ 窗口宽度-150
fruitElement.style.left = randomNum(150, window.innerWidth - 150) + 'px';
fruitElement.style.filter = buffFruitsInfo[buffFruitInfoNum].appearance;
// 加快buff水果的运动速度
fruitElement.style.animationDuration = '2s';
// 设置buff水果元素上的属性
fruitElement.setAttribute('src', buffFruitsInfo[buffFruitInfoNum].img);
fruitElement.setAttribute('data-type', buffFruitsInfo[buffFruitInfoNum].type);
fruitElement.setAttribute('data-buff', buffFruitsInfo[buffFruitInfoNum].buff);
fruitElement.setAttribute('data-appearance', buffFruitsInfo[buffFruitInfoNum].appearance);
body.appendChild(fruitElement);
}
const fruitBuffFunction = {
// 减速buff
retardBuffFunction() {
isRetardBuff = true;
buffState.style.display = 'block';
buffState.style.background = 'radial-gradient(ellipse, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0), rgba(18, 174, 246, 0.3))';
// 如果已经有了减速buff,则先清除之前的减速buff
retardBuffTimer && clearInterval(retardBuffTimer);
retardBuffTimer = setInterval(() => {
// 减少完整水果的运动速度
document.querySelectorAll('.ordinaryFruit').forEach(item => item.style.animationDuration = '6s');
// 减少炸弹的运动速度
document.querySelectorAll('.boom').forEach(item => item.style.animationDuration = '3.2s');
}, 100);
// 结束buff效果
setTimeout(() => {
isRetardBuff = false;
buffState.style.display = 'none';
clearInterval(retardBuffTimer);
}, 5000);
},
// 双倍得分buff
doubleBuffFunction() {
isDoubleBuff = true
buffState.style.display = 'block';
buffState.style.background ='radial-gradient(ellipse, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0), rgba(255, 85, 0, 0.3))';
// 结束buff效果
setTimeout(() => {
isDoubleBuff = false;
buffState.style.display = 'none';
}, 7000)
}
}
setInterval(() => {
// 若处于happy模式,则返回
if (isHappyModel) return;
// 获取所有完整的水果
let allFullFruit = document.querySelectorAll('.fullFruit');
allFullFruit.forEach(item => {
if (item.getAttribute('data-buff')) return;
// 检测水果是否到达底部
if (item.offsetTop > window.innerHeight) {
// 使用防抖,不让本事件短时间触发多次
if (!invincible) return;
invincible = false;
setTimeout(() => {
invincible = true;
}, 500);
// 到达底部则机会次数减一
opportunity--;
throwAudio.setAttribute('src', 'sound/throw.mp3');
throwAudio.play();
// 机会次数等于2时,替换右上方示意剩余次数的图片
if (opportunity == 2) {
crossNumberImg[0].setAttribute('src', 'images/xf.png');
} else if (opportunity == 1) {
crossNumberImg[1].setAttribute('src', 'images/xxf.png');
} else if (opportunity <= 0) {
crossNumberImg[2].setAttribute('src', 'images/xxxf.png');
boomAudio.setAttribute('src', 'sound/over.mp3');
boomAudio.play();
failFunction(100);
}
// 最后移除这个元素
item.remove();
}
});
// 检测炸弹是否到达底部
document.querySelectorAll('.boom').forEach(item => item.offsetTop > window.innerHeight && item.remove());
// 检测分裂的水果是否到达底部
document.querySelectorAll('.fruitSplit').forEach(item => item.offsetTop > window.innerHeight && item.remove());
}, 200)
// 若没有cut_fruits_info,则添加一个
localStorage.getItem('cut_fruits_info') || localStorage.setItem('cut_fruits_info', JSON.stringify({
apple: 0,
banana: 0,
basaha: 0,
peach: 0,
sandia: 0,
}));
let beyondString = '本局最终得分',
maxBatterStr = '本局最高连击';
// 失败时触发的函数,根据传进来的参数触发定时器
const failFunction = timerNum => {
setTimeout(() => {
// 防止触发多次
if (fail) return;
fail = true;
// 判断浏览器本地存储中是否含有fruit_ninja_score_record
if (localStorage.getItem('fruit_ninja_score_record')) {
// 与此次的分数对比大小,若此次的分数更大则更新数据
if (scoreNum >= parseInt(localStorage.getItem('fruit_ninja_score_record'))) {
beyondString = '恭喜你!创造了新的得分纪录';
localStorage.setItem('fruit_ninja_score_record', scoreNum);
}
} else {
// 设置fruit_ninja_score_record
localStorage.setItem('fruit_ninja_score_record', scoreNum);
}
// 更新cut_fruits_info
let cutFruitsInfo = JSON.parse(localStorage.getItem('cut_fruits_info'));
cutFruitsInfo.apple += fruitsNotes.apple;
cutFruitsInfo.banana += fruitsNotes.banana;
cutFruitsInfo.basaha += fruitsNotes.basaha;
cutFruitsInfo.peach += fruitsNotes.peach;
cutFruitsInfo.sandia += fruitsNotes.sandia;
localStorage.setItem('cut_fruits_info', JSON.stringify(cutFruitsInfo));
// 判断浏览器本地存储中是否含有fruit_ninja_batter_record
if (localStorage.getItem('fruit_ninja_batter_record')) {
// 与此次的对比大小,若此次的更大则更新数据
if (parseInt(maxBatter) >= parseInt(localStorage.getItem('fruit_ninja_batter_record'))) {
localStorage.setItem('fruit_ninja_batter_record', maxBatter);
maxBatterStr = '恭喜你!创造了新的连击纪录';
}
} else {
// 设置fruit_ninja_batter_record
localStorage.setItem('fruit_ninja_batter_record', maxBatter);
}
// 显示出玩家的最终分数,点击确定后重新开始游戏
alert(`${beyondString}: ${scoreNum}\n${maxBatterStr}: ${maxBatter}\n苹果:${fruitsNotes.apple}、香蕉:${fruitsNotes.banana}、草莓:${fruitsNotes.basaha}、桃子:${fruitsNotes.peach}、西瓜:${fruitsNotes.sandia}`);
location.replace(location.href);
}, timerNum);
}
// 炸弹的创建
const boomCreate = () => {
// 若处于happy模式或暂停期间,则返回
if (isHappyModel || pauseGame) return;
let boomElement = document.createElement('img');
boomElement.classList.add('boom');
// 炸弹的left范围是: 50 ~ 窗口宽度-炸弹宽度-50
boomElement.style.left = randomNum(50, window.innerWidth - boomElement.offsetWidth - 50) + 'px';
boomElement.setAttribute('src', 'images/boom.png');
body.appendChild(boomElement);
}
// 先声明变量
let followX, followY;
// 跟随鼠标或手指的痕迹
const follow = e => {
if (pauseGame) return;
// 根据是否在电脑端,赋于followX、followY相应的值
if (isComputer) {
followX = e.pageX;
followY = e.pageY;
} else {
followX = e.targetTouches[0].pageX;
followY = e.targetTouches[0].pageY;
}
// 创建痕迹,并添加逐渐消失的动画
let trace = document.createElement('p');
trace.classList.add('trace');
// 调整相对于鼠标的位置
trace.style.top = followY - 6 + 'px';
trace.style.left = followX - 6 + 'px';
body.appendChild(trace);
setTimeout(() => {
trace.remove();
}, 300); //此数值与鼠标或手指的滑动痕迹长度成正比,数值越大痕迹越长
}
// happy模式
const happyModelFunction = () => {
// 开启之后立即减一,防止触发多次
happyModelProgress.value -= 1;
isHappyModel = true;
happyModelText.style.display = 'block';
// 清空进度条和目前的所有炸弹
let allBoom = document.querySelectorAll('.boom');
allBoom.forEach(item => item.remove());
happyTimer = setInterval(() => {
// 持续创建水果
fruitCreate();
// 持续减少进度条
happyModelProgress.value -= 10;
// 当进度条小于或等于0时结束本次happy模式
if (happyModelProgress.value <= 0) {
clearInterval(happyTimer);
happyModelProgress.value += 1;
happyModelText.style.display = 'none';
setTimeout(() => {
// 移除底部的水果
document.querySelectorAll('.fullFruit').forEach(item => {
item.offsetTop > window.innerHeight && item.remove();
})
isHappyModel = false;
}, 3000);
}
}, 700);
}
// 先声明变量
let knifeX, knifeY;
const qweasdzxc = e => {
if (pauseGame) return;
if (isComputer) {
knifeX = e.pageX;
knifeY = e.pageY;
} else {
knifeX = e.targetTouches[0].pageX;
knifeY = e.targetTouches[0].pageY;
}
let knifeW = 5;
let knifeH = 5;
let allFullFruit = document.querySelectorAll('.fullFruit');
allFullFruit.forEach(item => {
let fruitAttr = item.getBoundingClientRect();
if ((knifeX + knifeW) < fruitAttr.x || (fruitAttr.x + fruitAttr.width) < knifeX || (knifeY + knifeH) < fruitAttr.y || (fruitAttr.y + fruitAttr.height) < knifeY) {} else {
// 移除fullFruit类,表示它不在是一个完整的水果
item.classList.remove('fullFruit');
// 添加fruitSplit类,表示它已经被分裂
item.classList.add('fruitSplit');
// 连击记录
batter++;
batter >= 2 ? showBatter.style.display = 'block' : false;
// 显示当前连击次数
showBatter.innerHTML = batter;
// 如果最大连击次数小于当前连击次数,则更新最大连击次数
maxBatter < batter ? maxBatter = batter : false;
maxBatterTimer && clearTimeout(maxBatterTimer);
maxBatterTimer = setTimeout(() => {
// 根据连击的次数额外加分
let batterAddPoints = document.createElement('div');
// 添加带有动画的类
batterAddPoints.classList.add('batterAddPoints');
batterAddPoints.innerHTML = '+' + Math.round(batter / 5);
body.appendChild(batterAddPoints);
// 2秒后移除此元素
setTimeout(() => {
batterAddPoints.remove();
}, 2000);
// 额外加分
scoreNum += Math.round(batter / 5);
// 更新分数显示
score.innerHTML = scoreNum;
batter = 0;
showBatter.style.display = 'none';
}, 888); //超过888毫秒未击中水果,则重新计算连击次数
let fruitDataType = item.getAttribute('data-type');
// 更改分裂后的水果的图片
item.setAttribute('src', `images/fruit/${fruitDataType}-1.png`);
// 创建分裂后的另一半水果
let fruitSplit = document.createElement('img');
fruitSplit.setAttribute('src', `images/fruit/${fruitDataType}-2.png`);
fruitSplit.classList.add('fruit');
fruitSplit.classList.add('fruitSplit');
fruitSplit.classList.add('fruitSplitRun');
fruitSplit.style.width = fruitAttr.width / 1.3 + 'px';
fruitSplit.style.height = fruitAttr.height / 1.3 + 'px';
fruitSplit.style.left = fruitAttr.x + fruitAttr.width + 'px';
fruitSplit.style.top = fruitAttr.y + 'px';
body.appendChild(fruitSplit);
// 播放声音
splatter.setAttribute('src', 'sound/splatter.mp3');
splatter.play();
// 如果是buff水果
if (item.getAttribute('data-buff')) {
fruitSplit.style.filter = item.getAttribute('data-appearance');
fruitBuffFunction[`${item.getAttribute('data-buff')}BuffFunction`]();
} else {
// 如果是普通水果
// 记录本局每个普通水果被切割的次数
for (let i in fruitsNotes) i == fruitDataType && fruitsNotes[i]++;
// 更新分数显示
let fruitDataScore = item.getAttribute('data-score');
// 如果有双倍得分buff,则分数加倍
scoreNum += parseInt(isDoubleBuff ? fruitDataScore * 2 : fruitDataScore);
score.innerHTML = scoreNum;
// 显示被切割水果的分值
let showScore = document.createElement('div');
// 如果有双倍得分buff,则显示的分值×2
showScore.innerHTML = isDoubleBuff ? `${fruitDataScore}×2` : `+${fruitDataScore}`;
showScore.classList.add('showScore');
showScore.style.left = fruitAttr.x + fruitAttr.width + 'px';
showScore.style.top = fruitAttr.y + 'px';
body.appendChild(showScore);
// 若处于happy模式则不再增加进度条
isHappyModel ? false : happyModelProgress.value += parseInt(isDoubleBuff ? fruitDataScore * 2 : fruitDataScore);
// 进度条大于或等于100时启动happy模式
happyModelProgress.value >= 100 && happyModelFunction();
// 两秒后移除显示的分值
setTimeout(() => {
showScore.remove();
}, 2000);
}
}
});
// 判断是否碰到炸弹
let boomObject = document.querySelectorAll('.boom');
boomObject.forEach(item => {
let boomObjectAttr = item.getBoundingClientRect();
// 判断是否碰到炸弹
if ((knifeX + knifeW) < boomObjectAttr.x || (boomObjectAttr.x + boomObjectAttr.width) < knifeX || (knifeY + knifeH) < boomObjectAttr.y || (boomObjectAttr.y + boomObjectAttr.height) < knifeY) {} else {
boomAudio.setAttribute('src', 'sound/boom.mp3');
boomAudio.play();
// 画面变成白色
cover.style.display = 'block';
failFunction(1000);
}
});
}
document.addEventListener('touchmove', e => {
// 在手指的移动中检测是否碰撞
qweasdzxc(e);
follow(e);
});
// 点击play开始游戏
play.addEventListener('click', () => {
play.style.display = 'none';
menu.setAttribute('src', 'sound/start.mp3');
menu.play();
// 开始创建各种水果和炸弹
fruitCreate();
// 随机创建水果
setInterval(() => {
// 随机生成0至300之间的一个数字(包括两者)
let fruitAppear = randomNum(0, 300);
// 使水果不规律的出现
setTimeout(fruitCreate, fruitAppear);
}, 600);
// 随机创建炸弹
setInterval(() => {
// 随机生成0至1000之间的一个数字(包括两者)
let boomAppear = randomNum(0, 1000);
boomAppear ? boomAppear += 1000 : false;
// 使炸弹不规律的出现
setTimeout(boomCreate, boomAppear);
}, isRetardBuff ? 5555 : 2500);
// 随机创建buff水果
setInterval(() => {
// 随机生成0至4000之间的一个数字(包括两者)
let buffFruitAppear = randomNum(0, 4000);
buffFruitAppear ? buffFruitAppear += 1000 : false;
// 使buff水果不规律的出现
setTimeout(buffFruitCreate, buffFruitAppear);
}, 18000);
});
document.addEventListener('mousemove', e => {
// 在鼠标的移动中检测是否碰撞
qweasdzxc(e);
follow(e);
});
document.addEventListener('click', e => {
// 在鼠标按下时检测是否碰撞
qweasdzxc(e);
follow(e);
});
document.addEventListener('selectstart', e => {
// 禁止选中,防止玩家按下鼠标时选中图片,影响美观
e.preventDefault();
});
const audioFalse = () => {
// 使页面中的所有音频静音
audio.forEach(item => item.muted = true);
// 替换成声音关闭的图片
soundControl.setAttribute('src', 'images/声音关闭.png');
}
const audioTrue = () => {
// 使页面中的所有音频取消静音
audio.forEach(item => item.muted = false);
// 替换成声音开启的图片
soundControl.setAttribute('src', 'images/声音开启.png');
}
localStorage.getItem('whether_muted') || localStorage.setItem('whether_muted', 'true');
JSON.parse(localStorage.getItem('whether_muted')) ? audioTrue() : audioFalse();
// 控制页面中的音频是否静音
soundControl.addEventListener('click', () => {
if (JSON.parse(localStorage.getItem('whether_muted'))) {
localStorage.setItem('whether_muted', 'false');
audioFalse();
} else {
localStorage.setItem('whether_muted', 'true');
audioTrue();
}
});
const isPauseGameFunction = (isPauseStr, isPause) => {
// 暂停游戏
document.querySelectorAll(".fruit").forEach(item => item.style.animationPlayState = isPauseStr);
document.querySelectorAll(".boom").forEach(item => item.style.animationPlayState = isPauseStr);
pauseGame = isPause;
}
viewHelp.addEventListener('click', () => {
// 暂停游戏
isPauseGameFunction("paused", true);
// 判断浏览器本地存储中是否含有fruit_ninja_score_record,有则显示,没有则显示无
localStorage.getItem('fruit_ninja_score_record') ? scoreRecord.innerHTML = localStorage.getItem(
'fruit_ninja_score_record') : scoreRecord.innerHTML = '无';
// 判断浏览器本地存储中是否含有fruit_ninja_batter_record,有则显示,没有则显示无
localStorage.getItem('fruit_ninja_batter_record') ? batterRecord.innerHTML = localStorage.getItem(
'fruit_ninja_batter_record') : batterRecord.innerHTML = '无';
let cutFruitsInfo = JSON.parse(localStorage.getItem('cut_fruits_info'));
cutFruitsInfoApple.innerHTML = cutFruitsInfo.apple;
cutFruitsInfoBanana.innerHTML = cutFruitsInfo.banana;
cutFruitsInfoBasaha.innerHTML = cutFruitsInfo.basaha;
cutFruitsInfoPeach.innerHTML = cutFruitsInfo.peach;
cutFruitsInfoSandia.innerHTML = cutFruitsInfo.sandia;
// 显示帮助信息
helpBG.style.display = 'block';
help.style.display = 'block';
});
closeHelp.addEventListener('click', () => {
// 取消暂停游戏
isPauseGameFunction("running", false);
// 隐藏帮助和其后面的半透明黑布
helpBG.style.display = 'none';
help.style.display = 'none';
});
helpBG.addEventListener('click', () => {
// 取消暂停游戏
isPauseGameFunction("running", false);
// 隐藏帮助和其后面的半透明黑布
helpBG.style.display = 'none';
help.style.display = 'none';
});
document.addEventListener('keyup', e => {
// 按空格键暂停或取消游戏
if (e.keyCode == 32 || e.key == ' ') {
pauseGame ? closeHelp.click() : viewHelp.click();
}
});
</script>
</body>
</html>
若想要图片或音频资源私信我即可。