food app and cleanup

This commit is contained in:
Davide Tantillo 2025-04-29 10:55:18 +02:00
parent 49afa43d8f
commit b1370ceae3
19 changed files with 925 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,222 @@
food-app-pro.png
size:2047,812
filter:Linear,Linear
pma:true
scale:0.25
blob-delivery
bounds:567,422,561,388
blob-list
bounds:54,375,511,435
blob-pan
bounds:1130,387,510,423
blob-ready
bounds:567,13,494,407
bottle-base
bounds:153,203,98,170
bottle-cork
bounds:1517,237,55,43
bottle-liquid-base
bounds:2,116,91,143
bottle-liquid-top
bounds:1947,479,68,21
bottle-reflection-middle
bounds:1746,565,16,11
bottle-reflection-top
bounds:31,696,13,9
bottle-top-open
bounds:1936,606,40,10
bottle-wrap
bounds:95,62,99,66
box
bounds:1821,123,133,132
rotate:90
bread
bounds:1689,59,81,68
bread-bottom
bounds:1396,282,180,103
bread-top
bounds:1829,360,187,112
bubble-base-b
bounds:446,147,119,119
bubble-base-c
bounds:253,149,119,119
bubble-base-m
bounds:1396,161,119,119
bubble-base-t
bounds:1063,161,119,119
bubble-big
bounds:1805,599,13,12
bubble-small
bounds:2008,619,9,9
bubble-tail-b
bounds:429,345,29,28
bubble-tail-c
bounds:2018,442,28,27
rotate:90
bubble-tail-m
bounds:2018,472,27,28
bubble-tail-t
bounds:1097,392,28,29
rotate:90
building
bounds:2,261,149,112
building2
bounds:1849,258,149,100
burger-case-base
bounds:1760,474,229,142
burger-case-front
bounds:1508,115,230,123
bush
bounds:1063,89,111,70
bush-small
bounds:486,21,75,53
carrot-body
bounds:1988,554,50,62
carrot-green
bounds:2000,324,42,34
cheese
bounds:1642,366,185,115
circle
bounds:1642,613,197,197
offsets:1,2,200,200
cloud
bounds:1576,92,111,70
flame-orange-1
bounds:1746,537,12,11
flame-orange-2
bounds:31,707,16,16
flame-orange-3
bounds:31,725,16,20
flame-orange-4
bounds:1972,589,12,15
flame-orange-5
bounds:1746,550,11,13
flame-red-1
bounds:2018,412,25,28
flame-red-2
bounds:2,385,40,42
flame-red-3
bounds:2,429,39,45
flame-red-4
bounds:2008,733,31,37
flame-red-5
bounds:2008,660,30,34
food-piece-1
bounds:2000,300,23,22
food-piece-2
bounds:2008,696,32,35
food-piece-3
bounds:2018,388,27,22
food-piece-4
bounds:1578,366,23,19
food-piece-5
bounds:2,476,46,43
food-piece-6
bounds:2008,772,37,38
food-piece-7
bounds:1789,597,14,14
fries-1
bounds:1515,28,59,87
fries-10
bounds:374,135,68,129
fries-11
bounds:1363,96,63,126
rotate:90
fries-12
bounds:2,571,49,121
fries-13
bounds:161,15,37,100
rotate:90
fries-2
bounds:196,54,46,114
rotate:90
fries-3
bounds:1880,22,39,101
rotate:90
fries-4
bounds:1256,93,76,105
rotate:90
fries-5
bounds:1184,149,70,131
fries-6
bounds:1955,126,57,130
fries-7
bounds:95,130,71,115
rotate:90
fries-8
bounds:1176,47,72,100
fries-9
bounds:1256,171,72,115
rotate:90
fries-case-back
bounds:1259,245,135,140
fries-case-front
bounds:1578,240,132,124
fries-case-side
bounds:1811,25,67,104
house-distant
bounds:2,50,83,64
house-front
bounds:1642,497,102,114
lamp-post
bounds:2,694,27,116
list-base
bounds:1841,618,192,165
rotate:90
list-writing
bounds:1712,239,135,119
magnifier-holder
bounds:410,19,55,74
rotate:90
magnifier-reflection
bounds:1063,393,32,27
magnifier-round
bounds:466,278,88,95
mushroom
bounds:1983,2,60,59
mushroom-1
bounds:1250,32,82,59
mushroom-2
bounds:312,12,77,51
mushroom-3
bounds:1576,33,78,57
offsets:0,0,78,61
mushroom-4
bounds:1899,63,88,61
offsets:0,1,88,62
olive
bounds:2008,630,33,28
olive-pick
bounds:31,747,8,63
pan-front
bounds:196,43,212,104
pan-handle
bounds:407,76,123,57
pan-handle-knot
bounds:2018,363,23,23
pan-inside
bounds:253,265,211,108
salad
bounds:1063,282,194,103
sparkle-1
bounds:2,521,45,48
sparkle-2
bounds:1990,502,50,50
tomato-1
bounds:1656,4,72,53
tomato-2
bounds:1656,4,72,53
tomato-3
bounds:87,7,72,53
tomato-body
bounds:1363,27,76,67
tomato-green
bounds:1746,578,41,33
tree-top
bounds:1741,129,68,108
tree-trunk
bounds:41,752,11,58
wing-back
bounds:1441,32,69,62
wing-front
bounds:1063,29,73,58

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -3557,14 +3557,713 @@ TODO`
-->
<!--
/////////////////////
// start section //
/////////////////////
-->
<div class="section vertical-split">
<style>
.phone {
min-width: 288px;
height: 80%;
background: white;
border-radius: 30px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
overflow: hidden;
border: 16px solid black;
position: relative;
display: flex;
aspect-ratio: 1/2;
color: black;
}
.screen-container {
display: flex;
width: 100%;
transition: transform 0.25s linear;
}
.screen {
width: 100%;
height: 100%;
flex-shrink: 0;
display: flex;
flex-direction: column;
}
.top-section {
flex: 1;
background: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
font-weight: bold;
border-bottom: 5px solid black;
}
.bottom-section {
display: flex;
flex-direction: row;
/* height: 40%; */
background: #fff;
}
.left-section, .right-section {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 5px solid black;
}
.right-section {
border-right: none;
}
.left-section {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 5px;
padding: 5px;
width: 100%;
max-width: 800px;
}
.item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
background: #f0f0f0;
padding: 1px;
border-radius: 5px;
}
.item img {
width: 100%;
height: auto;
max-width: 30px;
}
.controls {
display: flex;
gap: 5px;
flex-direction: row-reverse;
}
.controls button {
width: 20px;
height: 20px;
font-size: 12px;
}
.btn-small {
font-size: 16px;
border: none;
background: #007bff;
color: white;
cursor: pointer;
border-radius: 3px;
margin: 1px 0;
}
.btn-small:active {
background: #0056b3;
}
.list {
flex: 1;
display: flex;
flex-direction: column;
background: #f0f0f0;
}
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
font-size: 16px;
font-weight: bold;
padding: 5px;
border-bottom: 3px solid black;
}
.buttons {
display: flex;
}
.btn {
flex: 1;
padding: 10px;
font-size: 16px;
font-weight: bold;
border: none;
background: #dc3545;
color: white;
cursor: pointer;
}
.btn.next {
background: #28a745;
}
.btn:active {
opacity: 0.8;
}
.buttons-section-2 {
height: 40%;
}
.food-piece-circle {
width: 45px;
height: 45px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border: 3px solid transparent;
transition: border-color 0.3s ease-in-out;
cursor: pointer;
background: white;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
}
.box-button:active {
box-shadow: inset 0px 4px 6px rgba(0, 0, 0, 0.1);
transform: translateY(2px);
}
.group-buttons {
width: 100%;
padding: 10px;
box-sizing: border-box;
}
</style>
<div style="max-width: 300px; text-align: center; margin: 10px;">
<div class="phone">
<spine-overlay overlay-id="phone" scrollable></spine-overlay>
<div class="screen-container" id="screenContainer">
<!-- SECTION 1 -->
<div class="screen">
<div class="top-section">
<spine-widget
identifier="list"
overlay-id="phone"
default-mix=".2"
atlas="assets/food/food-app-pro.atlas"
skeleton="assets/food/list-search.json"
animation="animation"
isinteractive
></spine-widget>
</div>
<div class="bottom-section">
<div class="left-section">
<div id="carrotDiv" class="item">
<img src="assets/food/carrot-body.png">
<div class="controls">
<button id="carrotPlus" class="btn-small">+</button>
<button id="carrotMinus" class="btn-small">-</button>
</div>
</div>
<div id="tomatoDiv" class="item">
<img src="assets/food/tomato-body.png">
<div class="controls">
<button id="tomatoPlus" class="btn-small">+</button>
<button id="tomatoMinus" class="btn-small">-</button>
</div>
</div>
<div id="breadDiv" class="item">
<img src="assets/food/bread.png">
<div class="controls">
<button id="breadPlus" class="btn-small">+</button>
<button id="breadMinus" class="btn-small">-</button>
</div>
</div>
<div id="mushroomDiv" class="item">
<img src="assets/food/mushroom.png">
<div class="controls">
<button id="mushroomPlus" class="btn-small">+</button>
<button id="mushroomMinus" class="btn-small">-</button>
</div>
</div>
</div>
<div class="right-section">
<div class="list">
<div class="list-item"><span>Carrots</span> <span id="list-item-carrot">0</span></div>
<div class="list-item"><span>Tomatoes</span> <span id="list-item-tomato">0</span></div>
<div class="list-item"><span>Breads</span> <span id="list-item-bread">0</span></div>
<div class="list-item"><span>Mushrooms</span> <span id="list-item-mushroom">0</span></div>
</div>
<div class="buttons">
<button class="btn">Clear</button>
<button class="btn next" onclick="nextScreen()">Next</button>
</div>
</div>
</div>
</div>
<!-- SECTION 1 -->
<!-- SECTION 2 -->
<div class="screen">
<div class="top-section">
<spine-widget
identifier="pan"
overlay-id="phone"
default-mix=".2"
atlas="assets/food/food-app-pro.atlas"
skeleton="assets/food/pan-cooking-pro.json"
animation="animation"
></spine-widget>
</div>
<div class="buttons-section-2">
<div class="group-buttons">
<div style="text-align: center;">
Click on the food to cook it
</div>
<div class="buttons">
<button class="btn" style="padding: 5px;" onclick="prevScreen()">Prev</button>
<button id="btn-next-2" class="btn" style="padding: 5px; background-color: gainsboro; transition: background-color 0.25s ease-in-out;" onclick="nextScreen()" disabled>Next</button>
</div>
</div>
<div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 10px">
<div id="foodPieceDiv1" class="food-piece-circle">
<img src="assets/food/food-piece-1.png" style="width: 70%; height: auto;">
</div>
<div id="foodPieceDiv2" class="food-piece-circle">
<img src="assets/food/food-piece-2.png" style="width: 70%; height: auto;">
</div>
<div id="foodPieceDiv3" class="food-piece-circle">
<img src="assets/food/food-piece-3.png" style="width: 70%; height: auto;">
</div>
<div id="foodPieceDiv4" class="food-piece-circle">
<img src="assets/food/food-piece-4.png" style="width: 70%; height: auto;">
</div>
<div id="foodPieceDiv5" class="food-piece-circle">
<img src="assets/food/food-piece-5.png" style="width: 70%; height: auto;">
</div>
<div id="foodPieceDiv6" class="food-piece-circle">
<img src="assets/food/food-piece-6.png" style="width: 70%; height: auto;">
</div>
<div id="foodPieceDiv7" class="food-piece-circle">
<img src="assets/food/food-piece-7.png" style="width: 70%; height: auto;">
</div>
</div>
</div>
</div>
<!-- SECTION 2 -->
<!-- SECTION 3 -->
<div class="screen">
<div class="top-section">
<spine-widget
identifier="delivery"
overlay-id="phone"
default-mix=".2"
atlas="assets/food/food-app-pro.atlas"
skeleton="assets/food/meal-delivery-pro.json"
animation="animation"
></spine-widget>
</div>
<div class="buttons-section-2">
<div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 10px">
<div id="buttonDistance" class="food-piece-circle box-button" style="user-select: none;">
<img src="assets/food/box.png" style="width: 70%; height: auto;">
</div>
</div>
<div class="group-buttons">
<div style="text-align: center;">
Help the box to reach the destination by clicking the button above!
</div>
<div class="buttons">
<button class="btn" style="padding: 5px;" onclick="prevScreen()">Prev</button>
<button id="btn-next-3" class="btn" style="padding: 5px; background-color: gainsboro; transition: background-color 0.25s ease-in-out;" onclick="nextScreen()" disabled>Next</button>
</div>
</div>
</div>
</div>
<!-- SECTION 3 -->
<!-- SECTION 4 -->
<div class="screen">
<div class="top-section">
<spine-widget
identifier="ready"
overlay-id="phone"
default-mix=".2"
atlas="assets/food/food-app-pro.atlas"
skeleton="assets/food/meal-ready-pro.json"
animation="base"
isinteractive
></spine-widget>
</div>
<div class="buttons-section-2">
<div class="group-buttons">
<div style="text-align: center;">
Congratulation! You food has just been delivered!
</div>
<div class="buttons">
<button class="btn" style="padding: 5px;" onclick="prevScreen()">Prev</button>
</div>
</div>
</div>
</div>
<!-- SECTION 4 -->
</div>
</div>
</div>
<script>
let currentIndex = 0;
function nextScreen() {
const container = document.getElementById('screenContainer');
const totalScreens = document.querySelectorAll('.screen').length;
if (currentIndex < totalScreens - 1) {
currentIndex++;
container.style.transform = `translateX(-${currentIndex * 100}%)`;
}
}
function prevScreen() {
const container = document.getElementById('screenContainer');
const totalScreens = document.querySelectorAll('.screen').length;
if (currentIndex > 0) {
currentIndex--;
container.style.transform = `translateX(-${currentIndex * 100}%)`;
}
}
</script>
<script>
(async () => {
/* SECTION 1 */
const widget1 = await spine.getSpineWidget("list").loadingPromise;
const setInteractionSectionOne = (itemName, trackNumber) => {
const divName = `${itemName}Div`;
const buttonNamePlus = `${itemName}Plus`;
const buttonNameMinus = `${itemName}Minus`;
const listItemName = `list-item-${itemName}`;
const itemDiv = document.getElementById(divName);
const listItemDiv = document.getElementById(listItemName);
itemDiv.addEventListener('mouseenter', () => {
widget1.state.setAnimation(0, `focus-${itemName}`, true);
widget1.state.setAnimation(trackNumber, `shake-${itemName}`, true);
});
const setDefaultState = () => {
const currentEntry = widget1.state.getCurrent(trackNumber);
if (!currentEntry) return;
if (currentEntry.animation.name === `shake-${itemName}`) {
widget1.state.setEmptyAnimation(trackNumber);
} else if (currentEntry.next && currentEntry.next.animation && currentEntry.next.animation.name === `shake-${itemName}`) {
widget1.state.clearNext(currentEntry);
widget1.state.addEmptyAnimation(trackNumber);
}
widget1.state.setAnimation(0, "animation", true);
}
itemDiv.addEventListener('mouseleave', setDefaultState);
const addItemAction = () => {
widget1.state.setAnimation(trackNumber, `add-${itemName}`, false);
widget1.state.addAnimation(trackNumber, `shake-${itemName}`, true);
listItemDiv.textContent = parseInt(listItemDiv.textContent) + 1;
}
const itemSlot = widget1.skeleton.findSlot(`bubble-base-${itemName.charAt(0)}`);
let onItem = false;
widget1.addCursorSlotEventCallbacks(itemSlot, (slot, event) => {
if (event === "enter") {
console.log("focus");
widget1.state.setAnimation(0, `focus-${itemName}`, true);
widget1.state.setAnimation(trackNumber, `shake-${itemName}`, true);
}
if (event === "leave") {
onItem = false;
const currentEntry = widget1.state.getCurrent(trackNumber);
if (currentEntry.animation.name === `shake-${itemName}`) {
widget1.state.setEmptyAnimation(trackNumber);
} else if (currentEntry.next && currentEntry.next.animation && currentEntry.next.animation.name === `shake-${itemName}`) {
widget1.state.clearNext(currentEntry);
widget1.state.addEmptyAnimation(trackNumber);
}
const currentEntryZero = widget1.state.getCurrent(0);
if (currentEntryZero.animation.name === `focus-${itemName}`) {
widget1.state.setAnimation(0, "animation", true);
}
}
if (event === "down") {
addItemAction();
}
});
const itemButtonPlus = document.getElementById(buttonNamePlus);
itemButtonPlus.onclick = addItemAction;
const itemButtonMinus = document.getElementById(buttonNameMinus);
const removeItemAction = () => {
const current = parseInt(listItemDiv.textContent);
if (current > 0) listItemDiv.textContent = current - 1;
}
itemButtonMinus.onclick = removeItemAction;
}
setInteractionSectionOne("carrot", 1);
setInteractionSectionOne("tomato", 2);
setInteractionSectionOne("bread", 3);
setInteractionSectionOne("mushroom", 4);
/* SECTION 1 */
/* SECTION 2 */
const btnNext2 = document.getElementById("btn-next-2");
const widget2 = await spine.getSpineWidget("pan").loadingPromise;
const foodPiece1 = widget2.skeleton.findSlot(`food-piece-1`);
const foodPiece2 = widget2.skeleton.findSlot(`food-piece-2`);
const foodPiece3 = widget2.skeleton.findSlot(`food-piece-3`);
const foodPiece4 = widget2.skeleton.findSlot(`food-piece-4`);
const foodPiece5 = widget2.skeleton.findSlot(`food-piece-5`);
const foodPiece6 = widget2.skeleton.findSlot(`food-piece-6`);
const foodPiece7 = widget2.skeleton.findSlot(`food-piece-7`);
const foodPieces = [
[foodPiece1, document.getElementById("foodPieceDiv1")],
[foodPiece2, document.getElementById("foodPieceDiv2")],
[foodPiece3, document.getElementById("foodPieceDiv3")],
[foodPiece4, document.getElementById("foodPieceDiv4")],
[foodPiece5, document.getElementById("foodPieceDiv5")],
[foodPiece6, document.getElementById("foodPieceDiv6")],
[foodPiece7, document.getElementById("foodPieceDiv7")],
];
foodPieces.forEach(([foodPiece, itemDiv], index) => {
foodPiece.color.set(1, 1, 1, 0);
itemDiv.addEventListener('mousedown', () => {
if (itemDiv.dataset.cooking) {
itemDiv.style.borderColor = "transparent";
delete itemDiv.dataset.cooking;
const interval = setInterval(() => {
let alpha = foodPiece.color.a;
if (alpha <= 0) {
clearInterval(interval);
return;
}
foodPiece.color.set(1, 1, 1, alpha - 0.1);
}, 10);
} else {
itemDiv.style.borderColor = "#4CAF50";
itemDiv.dataset.cooking = true;
const interval = setInterval(() => {
let alpha = foodPiece.color.a;
if (alpha >= 1) {
clearInterval(interval);
return;
}
foodPiece.color.set(1, 1, 1, alpha + 0.1);
}, 10);
}
if (foodPieces.every(([,{ dataset }]) => dataset.cooking)) {
btnNext2.style.backgroundColor = "#28a745";
btnNext2.removeAttribute("disabled");
} else {
btnNext2.style.backgroundColor = "gainsboro";
btnNext2.setAttribute("disabled", "");
}
});
})
/* SECTION 2 */
/* SECTION 3 */
const widget3 = await spine.getSpineWidget("delivery").loadingPromise;
const btnNext3 = document.getElementById("btn-next-3");
const box = widget3.skeleton.findSlot("box");
let distance = -1300;
const buttonDistance = document.getElementById("buttonDistance");
buttonDistance.addEventListener("mousedown", () => {
let toAdd = 200;
const interval = setInterval(() => {
if (toAdd <= 0) {
clearInterval(interval);
return;
}
toAdd -= 10;
distance += 10;
}, 10)
})
setInterval(() => {
if (distance <= -1300) return;
distance -= 5;
if (distance > 2000) {
btnNext3.style.backgroundColor = "#28a745";
btnNext3.removeAttribute("disabled");
} else {
btnNext3.style.backgroundColor = "gainsboro";
btnNext3.setAttribute("disabled", "");
}
}, 10)
widget3.beforeUpdateWorldTransforms = () => {
box.bone.x += distance;
}
if (foodPieces.every(([,{ dataset }]) => dataset.cooking)) {
btnNext3.style.backgroundColor = "#28a745";
btnNext3.removeAttribute("disabled");
} else {
btnNext3.style.backgroundColor = "gainsboro";
btnNext3.setAttribute("disabled", "");
}
/* SECTION 3 */
/* SECTION 4 */
const widget4 = await spine.getSpineWidget("ready").loadingPromise;
const slot4Bread = widget4.skeleton.findSlot("salad");
widget4.addCursorSlotEventCallbacks(slot4Bread, (slot, event) => {
if (event === "enter") {
widget4.state.setAnimation(1, "bread-opening", false);
widget4.state.addAnimation(1, "bread-open", true);
}
if (event === "leave") {
widget4.state.setAnimation(1, "bread-closing", false);
}
});
const slot4Bottle = widget4.skeleton.findSlot("bottle-base");
widget4.addCursorSlotEventCallbacks(slot4Bottle, (slot, event) => {
if (event === "enter") {
widget4.state.setAnimation(2, "bottle-opening", false);
widget4.state.addAnimation(2, "bottle-open", true);
}
if (event === "leave") {
widget4.state.setAnimation(2, "bottle-closing", false);
}
});
const slot4Fries = widget4.skeleton.findSlot("fries-case-back");
widget4.addCursorSlotEventCallbacks(slot4Fries, (slot, event) => {
if (event === "enter") {
widget4.state.setAnimation(3, "fries", true);
}
if (event === "leave") {
let track = widget4.state.setEmptyAnimation(3);
track.mixDuration = 1;
}
});
/* SECTION 4 */
})();
</script>
<div class="split-bottom">
<pre><code id="code-display">
<script>escapeHTMLandInject(`
TODO`
);</script>
</code></pre>
</div>
</div>
<!--
/////////////////////
// end section //
/////////////////////
-->
<!--
/////////////////////
// start section //
/////////////////////
-->
<div class="section vertical-split" id="spneboy-game">
<div class="split-top split">
<div class="split-left" style="padding: 0;">
@ -3788,7 +4487,6 @@ TODO`
const key = e.key.toLowerCase();
if (key in keys) {
keys[key] = true;
e.preventDefault();
}
}