基于vite2+vue3制作個招財(cái)貓游戲
介紹
端午將至,大家都開始吃粽子了么,你是喜歡吃北方的甜的紅棗粽?還是南方的大肉粽呢?
本期我們將使用vite2與vue3開發(fā)出一個招財(cái)貓小游戲,通過考驗(yàn)眼力和預(yù)判能力,在圖案不停滾動的同時選出可以轉(zhuǎn)出不同的素材最終得到粽子獎勵,康康你能用多少次才會轉(zhuǎn)出自己喜愛口味的粽子吧~
演示

預(yù)覽地址:jsmask.gitee.io/dwgame_laohuji/
正文
游戲分析

在開發(fā)之前,我們要想好游戲設(shè)計(jì)和規(guī)則是如何,才能進(jìn)行往下進(jìn)行。通過上圖的演示和規(guī)則介紹,我們大致可以了解游戲流程了。
然后,根據(jù)了解的流程,進(jìn)行拆解,接下來我們主要會講解這些問題:
- 招財(cái)貓css3幀動畫。
- 圖案條帶中的素材在vite2的批量加載。
- 老虎機(jī)圖案自定義條帶生成。
- 老虎機(jī)無限滾動。
- 讓老虎機(jī)停止并且獲取抽獎碼。
- 獲得粽子后的紙屑飛舞效果的實(shí)現(xiàn)。
招財(cái)動畫

下圖是本期用的所有素材,招財(cái)貓招手的是由四張圖拼湊而成的,用了一些在線的雪碧圖生成工具。在里面我們會得到每張圖對應(yīng)的 background-position , 最后再用 animation 來完成這個幀動畫。其中,animation-timing-function:steps(1, end) 是幀動畫實(shí)現(xiàn)的核心,steps() 函數(shù)符號定義了一個階梯函數(shù),將輸出值的域劃分為等距階梯。第一個值是需要傳入正數(shù),表示等距的數(shù),而后一個表示插值的位置。
.cat {
width: 574px;
height: 630px;
margin: 0px auto;
position: relative;
background-image: url("../assets/image/cat.png");
background-position: -10px -10px;
&.active {
animation: play-game 0.64s steps(1, end) infinite;
}
@keyframes play-game {
0% {
background-position: -10px -10px;
}
33% {
background-position: -604px -10px;
}
66% {
background-position: -1198px -10px;
}
100% {
background-position: -10px -660px;
}
}
}
素材加載
我們后面需要通過canvas合成條帶,所以要先加載出需要轉(zhuǎn)出的圖案來。
按照原始的方案來,我們要手寫好多圖片資源文件的引入,所以十分麻煩。
import item0 from "../assets/image/item_0.png" import item1 from "../assets/image/item_1.png" import item2 from "../assets/image/item_2.png" // ...more import item9 from "../assets/image/item_9.png"
但是,Vite 中提供了 import.meta.glob 的語法糖來解決這種批量導(dǎo)入的問題,一次性加載出這些圖片文件來。
const imgs = import.meta.globEager("../assets/image/item_*.png");
let num = Object.keys(imgs).length;
let items = Object.values(imgs).map((mod) => {
let img = new Image();
img.onload = () => --num <= 0 && initGame();
img.src = mod.default;
return img;
});
當(dāng)然我們這里用 import.meta.globEager 來可以同步加載這些資源。

條帶生成
所謂的條帶,就是老虎機(jī)中滾動的背景圖,不停改變 backgroundPositionY 來實(shí)現(xiàn)滾動效果,是老虎機(jī)的核心,所使用到的條帶自然就是重中之重,但在我們平時開發(fā)條帶一般都是設(shè)計(jì)給的圖片,但經(jīng)常替換圖片后又要重新問他們要新圖甚是麻煩。所以,這里我想用 canvas 把剛才的那十張圖案拼接起來,形成條帶供我們使用。上一步,我們已經(jīng)把資源加載完成了,接下來,可以需要寫一個 createBackgroundImage 函數(shù)。
function createBackgroundImage({items = [], w = 45, h = 60, size = 40,test=false}) {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = ctx.width = w;
canvas.height = ctx.height = h * items.length;
let BackgroundImage = [...items];
BackgroundImage.forEach((img, i) => {
ctx.save();
ctx.drawImage(img, (w - size) / 2, (h - size) / 2 + h * i, size, size);
if(test){
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "bold 36px Baloo Bhaijaan";
ctx.fillText(i, w / 2, h * i + h / 2 + 5, w);
}
ctx.restore();
});
return convertCanvasToImage(canvas);
}
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
export default createBackgroundImage;
這里我們不光可以傳入資源數(shù)組,而且可以傳入塊的寬和高,還有圖案的大小,為了方便測試后面驗(yàn)證開獎碼的正確性,加入 test 字段,如果開啟,則會繪制上對應(yīng)的數(shù)字編號。

無限滾動
當(dāng)我們點(diǎn)擊開始游戲按鈕后,剛才生成出的條帶就會不停的改變 backgroundPositionY ,那有什么好辦法可以輕松控制這件事呢?
這里我推薦使用anime.js ,它一個功能強(qiáng)大且輕量級的 JavaScript 動畫庫。
# NPM npm i animejs -S # YARN yarn add animejs # PNPM pnpm i animejs -S
<ul class="content">
<li
ref="block"
v-for="(item, index) in 3"
:key="index"
:style="{ backgroundImage: `url(${backgroundImage.src})` }"
>
<button :disabled="stops[index]" @click="handleStop(index)">
Stop
</button>
</li>
</ul>
import anime from "animejs";
function play() {
if (isActive.value) return false;
isActive.value = true;
count.value += 1;
block.value.forEach((el, index) => {
setTimeout(() => {
stops.value[index] = false;
let y = parseInt(el.style.backgroundPositionY || "0", 10);
animes.value[index] = anime({
targets: el,
backgroundPositionY: [h / 2, h * items.length + h / 2],
loop: true, // 循環(huán)播放
direction: "normal", // 方向
easing: "linear", // 時間曲線
duration: 1200, // 播放時間
autoplay: true, // 是否立即播放
});
}, index * 240);
});
}
在 play 方法時,我們獲取當(dāng)綁定好條帶的元素塊,通過 animejs 給 backgroundPositionY 屬性設(shè)置一個數(shù)組,這個數(shù)組第0位代表起始狀態(tài),第1位代表要到達(dá)的狀態(tài)。然后把 loop 屬性設(shè)置 true 。那么一個簡單的條帶無限滾動就完成了。

中獎判定
function handleStop(index) {
stops.value[index] = true;
let el = block.value[index];
let y = parseInt(el.style.backgroundPositionY || "0", 10);
animes.value[index].remove(el);
let n = Math.round((y - h / 2) / h);
el.style.backgroundPositionY = n * h + h / 2 + "px";
giftCode.value[index] = (10 - n) % 10;
if (stops.value.find((item) => !item) === undefined) {
getResult();
}
}
當(dāng)我們每按停一個時,我們會迅速移除對應(yīng)元素 animejs 動畫,計(jì)算并賦值給最數(shù)值最接近的坐標(biāo)點(diǎn)。然后獲取到其對應(yīng)的數(shù)字編號存起來形成抽獎碼數(shù)組,當(dāng)三個全部停止時,會調(diào)用 getResult 方法來根據(jù)剛才得到的抽獎碼來開獎。
const giftData = [
{
name: "紅棗粽",
score: 100,
show: true,
type:1,
value: ["012", "025", "126", "256"],
},
// ...
]
function getResult() {
let str = giftCode.value.sort().join("");
let _obj = giftData.find((item) => item.value.includes(str));
if (!_obj) return (isActive.value = false);
if (_obj.show) {
giftObj.value = _obj;
ruleShow.value = false;
giftShow.value = true;
} else {
confetti();
hideGift();
}
}
這里,我們把得到的抽獎碼進(jìn)行從小往大排列生成字符串,然后在 giftData 數(shù)組中找尋配合的組合,達(dá)成后進(jìn)行不同的獎勵畫面。
紙屑飛舞
這里我使用了 canvas-confetti,它是專門來制作紙屑飛散的動畫庫。其原理是在頁面創(chuàng)建了canvas (也可以指定容器), 然后在里面繪制了幾種形狀可供選擇,通過數(shù)學(xué)計(jì)算,模擬了很多物理運(yùn)動來完成紙屑動畫多彩的效果。
# NPM npm i canvas-confetti -S # YARN yarn add canvas-confetti # PNPM pnpm i canvas-confetti -S
import confetti from "canvas-confetti";
function handleSucces() {
let endTime = Date.now() + 3 * 1000;
const colors = ["#bb0000", "#ffffff"];
(function frame() {
confetti({
particleCount: 2,
angle: 45,
spread: 155,
origin: { x: 0 },
colors: colors,
});
confetti({
particleCount: 2,
angle: 135,
spread: 60,
origin: { x: 1 },
colors: colors,
});
if (Date.now() < endTime) {
requestAnimationFrame(frame);
}
})();
}

結(jié)語
現(xiàn)在已經(jīng)把大家開發(fā)這類項(xiàng)目可能遭遇的問題或者方案大致給大家講述完了。不知道,給你帶來的幫助是多是少,也不知道大家點(diǎn)擊了多少次才得到喜歡吃的粽子了,但不管是什么,開心快樂就好,最后祝大家端午安康,財(cái)運(yùn),福運(yùn)多多,幸福滿滿。
到此這篇關(guān)于基于vite2+vue3制作個招財(cái)貓游戲的文章就介紹到這了,更多相關(guān)vite2 vue3招財(cái)貓游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+vue+對接支付寶接口+二維碼掃描支付功能(沙箱環(huán)境)
這篇文章主要介紹了springboot+vue+對接支付寶接口+二維碼掃描支付(沙箱環(huán)境),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
vue iview組件表格 render函數(shù)的使用方法詳解
下面小編就為大家分享一篇vue iview組件表格 render函數(shù)的使用方法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03
vue 中使用 watch 出現(xiàn)了如下的報錯的原因分析
這篇文章主要介紹了vue 中使用 watch 出現(xiàn)了如下的報錯信息的原因分析及解決方法,本文附有代碼解決方案,非常不錯,需要的朋友可以參考下2019-05-05
vue中集成省市區(qū)街四級地址組件的實(shí)現(xiàn)過程
我們在開發(fā)中常會遇到選擇地址的需求,有時候只需要選擇省就可以,有時候則需要選擇到市、縣,以至于鄉(xiāng)鎮(zhèn),甚至哪個村都有可能,下面這篇文章主要給大家介紹了關(guān)于vue中集成省市區(qū)街四級地址組件的相關(guān)資料,需要的朋友可以參考下2022-12-12
vue ElementUI的from表單實(shí)現(xiàn)登錄效果的示例
本文主要介紹了vue ElementUI的from表單實(shí)現(xiàn)登錄效果的示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
關(guān)于electron-vue打包后運(yùn)行白屏的解決方案
這篇文章主要介紹了關(guān)于electron-vue打包后運(yùn)行白屏的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
基于vue.js路由參數(shù)的實(shí)例講解——簡單易懂
下面小編就為大家?guī)硪黄趘ue.js路由參數(shù)的實(shí)例講解——簡單易懂。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09

