Skip to content

Commit

Permalink
Functional beak animation
Browse files Browse the repository at this point in the history
  • Loading branch information
zelytra committed Jun 11, 2024
1 parent 20f30ad commit 1fa2afd
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 308 deletions.
14 changes: 7 additions & 7 deletions webapp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions webapp/src/components/PoolGame.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<template>
<p>Pool Game</p>
<LoadingVue/>
<section>
<p>Pool Game</p>
<LoadingVue/>
</section>
</template>

<script setup lang="ts">
Expand Down
168 changes: 114 additions & 54 deletions webapp/src/vue/LoadingVue.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
<template>
<div ref="scene" class="scene">
<img v-for="ball in balls" :key="ball.id" :src="ball.image" class="ball"
:style="{ left: ball.position.x + 'px', top: ball.position.y + 'px' }" alt=""/>
<img ref="whiteBall" src="@/assets/icons/balls/white.svg" class="ball white-ball" alt=""/>
</div>
<div ref="scene" id="scene"/>
</template>

<script setup lang="ts">
import {ref, onMounted} from 'vue';
import {ref, onMounted, nextTick} from 'vue';
import * as Matter from "matter-js";
// Import ball images
Expand Down Expand Up @@ -44,42 +40,73 @@ const whiteBall = ref(null);
// Ball data
const balls = ref([
{id: 1, image: eleven, position: {x: 300, y: 150}},
{id: 2, image: two, position: {x: 340, y: 150}},
{id: 3, image: thirteen, position: {x: 380, y: 150}},
{id: 4, image: four, position: {x: 320, y: 190}},
{id: 5, image: five, position: {x: 360, y: 190}},
{id: 6, image: six, position: {x: 400, y: 190}},
{id: 7, image: ten, position: {x: 340, y: 230}},
{id: 8, image: three, position: {x: 380, y: 230}},
{id: 9, image: fourteen, position: {x: 420, y: 230}},
{id: 10, image: fifteen, position: {x: 360, y: 270}},
{id: 11, image: eight, position: {x: 400, y: 270}},
{id: 12, image: one, position: {x: 380, y: 310}},
{id: 13, image: seven, position: {x: 420, y: 310}},
{id: 14, image: twelve, position: {x: 440, y: 310}},
{id: 15, image: nine, position: {x: 460, y: 310}},
{id: 1, image: one, position: {x: 0, y: 0}}, // Apex ball
{id: 2, image: two, position: {x: 0, y: 0}},
{id: 3, image: thirteen, position: {x: 0, y: 0}},
{id: 4, image: four, position: {x: 0, y: 0}},
{id: 5, image: eight, position: {x: 0, y: 0}},
{id: 6, image: six, position: {x: 0, y: 0}},
{id: 7, image: ten, position: {x: 0, y: 0}},
{id: 8, image: three, position: {x: 0, y: 0}},
{id: 9, image: fourteen, position: {x: 0, y: 0}},
{id: 10, image: fifteen, position: {x: 0, y: 0}},
{id: 11, image: five, position: {x: 0, y: 0}},
{id: 12, image: eleven, position: {x: 0, y: 0}},
{id: 13, image: seven, position: {x: 0, y: 0}},
{id: 14, image: twelve, position: {x: 0, y: 0}},
{id: 15, image: nine, position: {x: 0, y: 0}},
]);
onMounted(async () => {
const isMatterReady = ref(false);
const setupPositions = (width: number, _height: number) => {
const startX = width / 2;
const startY = 200;
const offsetX = 20;
const offsetY = 40;
balls.value[0].position = {x: startX, y: startY};
balls.value[1].position = {x: startX - offsetX, y: startY + offsetY};
balls.value[2].position = {x: startX + offsetX, y: startY + offsetY};
balls.value[3].position = {x: startX - 2 * offsetX, y: startY + 2 * offsetY};
balls.value[4].position = {x: startX, y: startY + 2 * offsetY};
balls.value[5].position = {x: startX + 2 * offsetX, y: startY + 2 * offsetY};
balls.value[6].position = {x: startX - 3 * offsetX, y: startY + 3 * offsetY};
balls.value[7].position = {x: startX - offsetX, y: startY + 3 * offsetY};
balls.value[8].position = {x: startX + offsetX, y: startY + 3 * offsetY};
balls.value[9].position = {x: startX + 3 * offsetX, y: startY + 3 * offsetY};
balls.value[10].position = {x: startX - 4 * offsetX, y: startY + 4 * offsetY};
balls.value[11].position = {x: startX - 2 * offsetX, y: startY + 4 * offsetY};
balls.value[12].position = {x: startX, y: startY + 4 * offsetY};
balls.value[13].position = {x: startX + 2 * offsetX, y: startY + 4 * offsetY};
balls.value[14].position = {x: startX + 4 * offsetX, y: startY + 4 * offsetY};
};
async function initializeScene() {
// Preload all images
try {
await nextTick();
await Promise.all(balls.value.map(ball => preloadImage(ball.image)));
await preloadImage(white);
const element = scene.value;
if (!element) return;
const width = element.clientWidth;
const height = element.clientHeight;
// Initialize Matter.js engine
const {Engine, Render, Runner, Bodies, Composite, Events} = Matter;
const engine = Engine.create();
let engine = Engine.create();
engine.gravity.y = 0;
const world = engine.world;
const render = Render.create({
element: scene.value,
engine: engine,
options: {
width: 800,
height: 600,
width: width,
height: height,
wireframes: false,
background: 'transparent'
}
Expand All @@ -89,17 +116,10 @@ onMounted(async () => {
const runner = Runner.create();
Runner.run(runner, engine);
// Add walls with appropriate thickness and friction to simulate a table surface
const walls = [
Bodies.rectangle(400, 0, 800, 50, {isStatic: true, friction: 0.01}), // top
Bodies.rectangle(400, 600, 800, 50, {isStatic: true, friction: 0.01}), // bottom
Bodies.rectangle(800, 300, 50, 600, {isStatic: true, friction: 0.01}), // right
Bodies.rectangle(0, 300, 50, 600, {isStatic: true, friction: 0.01}) // left
];
Composite.add(world, walls);
// Add balls with low friction to simulate rolling
const ballRadius = 20;
// Setup positions for balls and white ball
setupPositions(width, height);
const ballRadius = 20; // Half of 144px
const matterBalls = balls.value.map(ball => {
return Bodies.circle(ball.position.x, ball.position.y, ballRadius, {
restitution: 0.9,
Expand All @@ -108,29 +128,29 @@ onMounted(async () => {
render: {
sprite: {
texture: ball.image,
xScale: 2 * ballRadius / 40, // Assuming ball images are 40x40 pixels
yScale: 2 * ballRadius / 40 // Adjust scale to match ball size
xScale: ballRadius * 2 / 140, // Adjust scale to match ball size
yScale: ballRadius * 2 / 140 // Adjust scale to match ball size
}
}
});
});
const whiteBallBody = Bodies.circle(400, 500, ballRadius, {
const whiteBallBody = Bodies.circle(width / 2, 20, ballRadius, {
restitution: 0.9,
friction: 0.05,
frictionAir: 0.01,
render: {
sprite: {
texture: white,
xScale: 2 * ballRadius / 40, // Assuming ball images are 40x40 pixels
yScale: 2 * ballRadius / 40 // Adjust scale to match ball size
xScale: ballRadius * 2 / 140, // Adjust scale to match ball size
yScale: ballRadius * 2 / 140 // Adjust scale to match ball size
}
}
});
Composite.add(world, [...matterBalls, whiteBallBody]);
// Apply force to the white ball
Matter.Body.applyForce(whiteBallBody, {x: whiteBallBody.position.x, y: whiteBallBody.position.y}, {x: 0, y: -0.05});
// Apply force to the white ball (if needed)
Matter.Body.applyForce(whiteBallBody, {x: whiteBallBody.position.x, y: whiteBallBody.position.y}, {x: 0, y: 0.05});
// Update Vue ball positions on each tick
Events.on(engine, 'afterUpdate', () => {
Expand All @@ -143,24 +163,64 @@ onMounted(async () => {
whiteBall.value.style.top = whiteBallBody.position.y + 'px';
}
});
// Add reset logic
setTimeout(() => {
Engine.clear(engine);
Render.stop(render);
Runner.stop(runner);
render.canvas.remove();
render.textures = {};
initializeScene(); // Reinitialize scene
}, 10000); // Duration of the fade-out animation
isMatterReady.value = true;
} catch (error) {
console.error('Error loading images:', error);
}
};
onMounted(() => {
initializeScene();
});
</script>

<style scoped>
.scene {
position: relative;
width: 800px;
height: 600px;
background: green;
<style scoped lang="scss">
#scene {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
backdrop-filter: blur(2px);
z-index: 99;
animation: fadeOut 10s ease-in-out infinite, fadeIn 10s ease-in-out infinite;
}
.ball {
position: absolute;
width: 40px;
height: 40px;
border-radius: 50%;
@keyframes fadeOut {
0% {
opacity: 1;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
100% {
opacity: 1;
}
}
</style>
Loading

0 comments on commit 1fa2afd

Please sign in to comment.