[chapter 1] sprite animation

- generate sprite state data
- create animation loop
- implement animation logic
- implement control to switch different animation
- use rxjs for handling chenge select
main
Vasily Guzov 2 years ago
parent 614621e1f4
commit a7e7b6a3e2

@ -0,0 +1,16 @@
.container {
border: 5px solid black;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 600px;
height: 600px;
}
.select {
position: absolute;
top: 20px;
left: 50%;
transform: translate(0, -50%);
}

@ -0,0 +1,62 @@
import styles from "./chapter-1.module.css";
import { animationPhases, getAnimationStates } from "./data/index";
import { drawSelect } from "./select";
import { AnimationName } from "./type";
export function chapter1() {
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d");
canvas.classList.add(styles.container);
const select$ = drawSelect();
const CANVAS_W = (canvas.width = 600);
const CANVAS_H = (canvas.height = 600);
const playerImage = new Image();
playerImage.src = "/shadow_dog.png";
const SPRITE_W = 6876 / 12;
const SPRITE_H = 5230 / 10;
let stateName: AnimationName = "run";
let gameFrame = 0;
const staggerFrame = 5;
const animationState = getAnimationStates(
SPRITE_W,
SPRITE_H,
animationPhases
);
select$.subscribe((event) => {
stateName = (event.target as HTMLSelectElement).value as AnimationName;
});
function animate() {
if (ctx) {
ctx.clearRect(0, 0, CANVAS_W, CANVAS_H);
let position =
Math.floor(gameFrame / staggerFrame) %
animationState[stateName].loc.length;
let frameX = SPRITE_W * position;
let frameY = animationState[stateName].loc[position].y;
ctx.drawImage(
playerImage,
frameX,
frameY,
SPRITE_W,
SPRITE_H,
0,
0,
SPRITE_W,
SPRITE_H
);
gameFrame++;
requestAnimationFrame(animate);
}
}
animate();
}

@ -0,0 +1,44 @@
import { AnimationPhases } from "../type";
export const animationPhases: AnimationPhases[] = [
{
name: "idle",
frames: 7,
},
{
name: "jump",
frames: 7,
},
{
name: "fall",
frames: 7,
},
{
name: "run",
frames: 9,
},
{
name: "dizzy",
frames: 11,
},
{
name: "sit",
frames: 5,
},
{
name: "roll",
frames: 7,
},
{
name: "bit",
frames: 7,
},
{
name: "ko",
frames: 12,
},
{
name: "getHit",
frames: 4,
},
];

@ -0,0 +1,2 @@
export * from "./animationPhases";
export * from "./spriteAnimationData";

@ -0,0 +1,24 @@
import { AnimationPhases, AnimationName, AnimationState } from "../type";
export function getAnimationStates(
spriteW: number,
spriteH: number,
animationPhases: AnimationPhases[]
) {
let animationStates: unknown = {};
animationPhases.forEach((state, index) => {
let loc = [];
for (let j = 0; j < state.frames; j++) {
let x = j * spriteW;
let y = index * spriteH;
loc.push({ x, y });
}
(animationStates as Record<AnimationName, AnimationState>)[state.name] = {
loc,
};
});
return animationStates as Record<AnimationName, AnimationState>;
}

@ -0,0 +1,31 @@
import { animationPhases } from "./data/animationPhases";
import styles from "./chapter-1.module.css";
import { fromEvent } from "rxjs";
export function drawSelect() {
const app = document.getElementById("app");
const select = document.createElement("select");
select.id = "stateName";
select.name = "stateKey";
select.classList.add(styles.select);
const options = animationPhases.map((phase) => {
const opt = document.createElement("option");
opt.value = phase.name;
opt.text = phase.name.toUpperCase();
return opt;
});
options.forEach((opt) => {
select.appendChild(opt);
});
app?.appendChild(select);
const selectElem = document.getElementById("stateName") as HTMLSelectElement;
const select$ = fromEvent(selectElem, "change");
return select$;
}

@ -0,0 +1,20 @@
export type AnimationName =
| "idle"
| "jump"
| "fall"
| "run"
| "dizzy"
| "sit"
| "roll"
| "bit"
| "ko"
| "getHit";
export type AnimationPhases = {
name: AnimationName;
frames: number;
};
export type AnimationState = {
loc: { x: number; y: number }[];
};
Loading…
Cancel
Save