詳しくは「初夏クリエイティブコンテスト」に参加しておりますので、こちらをご覧ください:初夏クリエイティブコンテスト
導入する
ドラゴンボートフェスティバルが近づいていますが、みんな餃子を食べ始めましたか?北からの甘い赤いナツメ餃子を食べたいですか?それとも南部の大きな肉団子?
今号では、vite2とvue3を使ってラッキーキャットミニゲームを開発します。視力と事前判断能力をテストすることで、パターンがスクロールし続けている間に転送できるさまざまな素材を選択し、最終的にzongziの報酬を得ることができます、あなたはそれを使うことができます。あなたは何回あなたの好きなちまきを作りますか?
デモ
プレビューアドレス:jsmask.gitee.io/dwgame_laoh…
文章
ゲーム分析
開発の前に、ゲームのデザインとルールがどのようになっているのかを考えてから先に進む必要があります。上図のデモンストレーションとルール紹介により、ゲームの流れを大まかに理解することができます。
次に、プロセスの理解に従って、解体し、主にこれらの問題について説明します。
- ラッキーキャットcss3フレームアニメーション。
- パターンストリップのマテリアルは、vite2にバッチロードされます。
- スロットマシンパターンのカスタムストリップ生成。
- 無限スクロールのスロットマシン。
- スロットマシンを停止し、ラッフルコードを取得します。
- ちまきを入手した後の紙吹雪飛行効果の実現。
ラッキーアニメーション
下の写真は、この号で使用されているすべての資料です。幸運な猫の手招きは4つの写真で構成されており、いくつかのオンラインスプライト画像生成ツールが使用されています。その中で、各画像の対応する画像を取得し、background-position
最後にanimation
それを使用してフレームアニメーションを完成させます。それらの中で、それanimation-timing-function:steps(1, end)
はフレームアニメーション実装のコアです。steps()
関数シンボルは、出力値のドメインを等距離のステップに分割するステップ関数を定義します。最初の値は、渡す必要のある正の数であり、等距離の数を表し、後者は補間の位置を表します。
.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;
}
}
}
复制代码
材料の充填
後でキャンバスを介してストリップを合成する必要があるため、最初に転送する必要のあるパターンをロードする必要があります。
当初の計画では、手作業で大量の画像リソースファイルを導入する必要があるため、非常に面倒です。
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"
复制代码
ただし、Viteimport.meta.glob
は、バッチインポートの問題を解決し、これらのイメージファイルを一度にロードするためのシンタックスシュガーを提供します。
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;
});
复制代码
もちろん、ここでこれを使用して、これらのリソースを同期的import.meta.globEager
にロードできます。
条带生成
所谓的条带,就是老虎机中滚动的背景图,不停改变 backgroundPositionY
来实现滚动效果,是老虎机的核心,所使用到的条带自然就是重中之重,但在我们平时开发条带一般都是设计给的图片,但经常替换图片后又要重新问他们要新图甚是麻烦。所以,这里我想用 canvas
把刚才的那十张图案拼接起来,形成条带供我们使用。上一步,我们已经把资源加载完成了,接下来,可以需要写一个 createBackgroundImage
函数。
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;
复制代码
这里我们不光可以传入资源数组,而且可以传入块的宽和高,还有图案的大小,为了方便测试后面验证开奖码的正确性,加入 test
字段,如果开启,则会绘制上对应的数字编号。
无限滚动
当我们点击开始游戏按钮后,刚才生成出的条带就会不停的改变 backgroundPositionY
,那有什么好办法可以轻松控制这件事呢?
这里我推荐使用anime.js ,它一个功能强大且轻量级的 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, // 循环播放
direction: "normal", // 方向
easing: "linear", // 时间曲线
duration: 1200, // 播放时间
autoplay: true, // 是否立即播放
});
}, index * 240);
});
}
复制代码
在 play
方法时,我们获取当绑定好条带的元素块,通过 animejs
给 backgroundPositionY
属性设置一个数组,这个数组第0位代表起始状态,第1位代表要到达的状态。然后把 loop
属性设置 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();
}
}
复制代码
当我们每按停一个时,我们会迅速移除对应元素 animejs
动画,计算并赋值给最数值最接近的坐标点。然后获取到其对应的数字编号存起来形成抽奖码数组,当三个全部停止时,会调用 getResult
方法来根据刚才得到的抽奖码来开奖。
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();
}
}
复制代码
这里,我们把得到的抽奖码进行从小往大排列生成字符串,然后在 giftData
数组中找寻配合的组合,达成后进行不同的奖励画面。
纸屑飞舞
ここでは、飛んでいる紙吹雪のアニメーション用のライブラリであるcanvas-confettiを使用しています。原理は、ページを作成しcanvas
(コンテナを指定することもできます)、その中にいくつかの形状を描画して選択することです。数学的な計算によって、紙吹雪アニメーションのカラフルな効果を完成させるために、多くの物理的な動きがシミュレートされます。
# 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);
}
})();
}
复制代码
エピローグ
これで、そのようなプロジェクトの開発で遭遇する可能性のある問題または解決策が大まかに説明されました。どれだけお役に立てるかわかりませんし、何回クリックしてお気に入りのちまきを手に入れたかはわかりませんが、それが何であれ、幸せで幸せです。最後に、皆さんが健康であることを願っています。ドラゴンボートフェスティバル、富と幸運。たくさんの幸せ。