Use rollup and split up files

This commit is contained in:
Idrees Hassan
2025-10-26 13:17:18 -04:00
parent 26105fc66d
commit 8be8ab2858
13 changed files with 4088 additions and 3679 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/node_modules
.DS_Store
/dist/birb.bundled.js

View File

@@ -5,4 +5,24 @@ This project is still being worked on, but if you wish to help me beta test it,
1. Install [Tampermonkey](https://www.tampermonkey.net/) on your web browser
2. Enable the Tampermonkey extension and give it the permissions requested
3. Install my Pocket Bird script by going to this link and clicking install: [https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js](https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js)
4. Now any websites you visit will have a little bird hopping around!
4. Now any websites you visit will have a little bird hopping around!
## Development
This project uses Rollup to bundle the source files.
### Building
```bash
npm run build
```
### Development Mode
Watch for changes and rebuild automatically:
```bash
npm run dev
```
The source files are in the `src/` directory. The main entry point is `src/birb.js`, which bundles all the other modules together.

View File

@@ -1,6 +1,7 @@
// @ts-check
import { readFileSync, writeFileSync } from 'fs';
import { rollup } from 'rollup';
import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
const spriteSheets = [
{
@@ -52,7 +53,6 @@ try {
throw e;
}
const userScriptHeader =
`// ==UserScript==
// @name Pocket Bird
@@ -70,8 +70,19 @@ const userScriptHeader =
`;
// Bundle with rollup
const bundle = await rollup({
input: 'src/birb.js',
});
let birbJs = readFileSync('birb.js', 'utf8');
await bundle.write({
file: 'dist/birb.bundled.js',
format: 'iife',
});
await bundle.close();
let birbJs = readFileSync('dist/birb.bundled.js', 'utf8');
// Compile and insert sprite sheets
for (const spriteSheet of spriteSheets) {
@@ -86,6 +97,9 @@ birbJs = birbJs.replace(STYLESHEET_KEY, stylesheetContent);
// Build standard javascript file
writeFileSync('./dist/birb.js', birbJs);
// Delete bundled file
unlinkSync('./dist/birb.bundled.js');
// Build user script
const userScript = userScriptHeader + birbJs;
writeFileSync('./dist/birb.user.js', userScript);

3493
dist/birb.js vendored

File diff suppressed because it is too large Load Diff

3495
dist/birb.user.js vendored

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Pocket Bird",
"description": "It's a bird, in your browser. What more could you want?",
"version": "2025.10.25.130",
"version": "2025.10.26.16",
"homepage_url": "https://idreesinc.com",
"content_scripts": [
{

360
package-lock.json generated
View File

@@ -9,9 +9,325 @@
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
"nodemon": "^3.1.10"
"nodemon": "^3.1.10",
"rollup": "^4.52.5"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
"integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
"integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
"integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
"integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
"integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
"integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
"integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
"integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
"integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
"integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
"integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
"integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
"integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
"integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
"integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
"integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
"integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
"integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
"integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
"integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
"integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
"integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -316,6 +632,48 @@
"node": ">=8.10.0"
}
},
"node_modules/rollup": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
"integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.52.5",
"@rollup/rollup-android-arm64": "4.52.5",
"@rollup/rollup-darwin-arm64": "4.52.5",
"@rollup/rollup-darwin-x64": "4.52.5",
"@rollup/rollup-freebsd-arm64": "4.52.5",
"@rollup/rollup-freebsd-x64": "4.52.5",
"@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
"@rollup/rollup-linux-arm-musleabihf": "4.52.5",
"@rollup/rollup-linux-arm64-gnu": "4.52.5",
"@rollup/rollup-linux-arm64-musl": "4.52.5",
"@rollup/rollup-linux-loong64-gnu": "4.52.5",
"@rollup/rollup-linux-ppc64-gnu": "4.52.5",
"@rollup/rollup-linux-riscv64-gnu": "4.52.5",
"@rollup/rollup-linux-riscv64-musl": "4.52.5",
"@rollup/rollup-linux-s390x-gnu": "4.52.5",
"@rollup/rollup-linux-x64-gnu": "4.52.5",
"@rollup/rollup-linux-x64-musl": "4.52.5",
"@rollup/rollup-openharmony-arm64": "4.52.5",
"@rollup/rollup-win32-arm64-msvc": "4.52.5",
"@rollup/rollup-win32-ia32-msvc": "4.52.5",
"@rollup/rollup-win32-x64-gnu": "4.52.5",
"@rollup/rollup-win32-x64-msvc": "4.52.5",
"fsevents": "~2.3.2"
}
},
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",

View File

@@ -7,9 +7,10 @@
"type": "module",
"scripts": {
"build": "node build.js",
"dev": "nodemon --watch birb.js --watch stylesheet.css --watch build.js --exec \"npm run build\""
"dev": "nodemon --watch src --watch stylesheet.css --watch build.js --exec \"npm run build\""
},
"devDependencies": {
"nodemon": "^3.1.10"
"nodemon": "^3.1.10",
"rollup": "^4.52.5"
}
}

74
src/Frame.js Normal file
View File

@@ -0,0 +1,74 @@
// @ts-check
import { TRANSPARENT, Directions } from './constants.js';
import Layer from './Layer.js';
import BirdType from './birdType.js';
class Frame {
/** @type {{ [tag: string]: string[][] }} */
#pixelsByTag = {};
/**
* @param {Layer[]} layers
*/
constructor(layers) {
/** @type {Set<string>} */
let tags = new Set();
for (let layer of layers) {
tags.add(layer.tag);
}
tags.add("default");
for (let tag of tags) {
let maxHeight = layers.reduce((max, layer) => Math.max(max, layer.pixels.length), 0);
if (layers[0].tag !== "default") {
throw new Error("First layer must have the 'default' tag");
}
this.pixels = layers[0].pixels.map(row => row.slice());
// Pad from top with transparent pixels
while (this.pixels.length < maxHeight) {
this.pixels.unshift(new Array(this.pixels[0].length).fill(TRANSPARENT));
}
// Combine layers
for (let i = 1; i < layers.length; i++) {
if (layers[i].tag === "default" || layers[i].tag === tag) {
let layerPixels = layers[i].pixels;
let topMargin = maxHeight - layerPixels.length;
for (let y = 0; y < layerPixels.length; y++) {
for (let x = 0; x < layerPixels[y].length; x++) {
this.pixels[y + topMargin][x] = layerPixels[y][x] !== TRANSPARENT ? layerPixels[y][x] : this.pixels[y + topMargin][x];
}
}
}
}
this.#pixelsByTag[tag] = this.pixels.map(row => row.slice());
}
}
/**
* @param {string} [tag]
* @returns {string[][]}
*/
getPixels(tag = "default") {
return this.#pixelsByTag[tag] ?? this.#pixelsByTag["default"];
}
/**
* @param {CanvasRenderingContext2D} ctx
* @param {BirdType} [species]
* @param {number} direction
* @param {number} canvasPixelSize
*/
draw(ctx, direction, canvasPixelSize, species) {
const pixels = this.getPixels(species?.tags[0]);
for (let y = 0; y < pixels.length; y++) {
const row = pixels[y];
for (let x = 0; x < pixels[y].length; x++) {
const cell = direction === Directions.LEFT ? row[x] : row[pixels[y].length - x - 1];
ctx.fillStyle = species?.colors[cell] ?? cell;
ctx.fillRect(x * canvasPixelSize, y * canvasPixelSize, canvasPixelSize, canvasPixelSize);
};
};
}
}
export default Frame;

14
src/Layer.js Normal file
View File

@@ -0,0 +1,14 @@
// @ts-check
class Layer {
/**
* @param {string[][]} pixels
* @param {string} [tag]
*/
constructor(pixels, tag = "default") {
this.pixels = pixels;
this.tag = tag;
}
}
export default Layer;

View File

@@ -1,5 +1,33 @@
// @ts-check
import {
THEME_HIGHLIGHT,
TRANSPARENT,
OUTLINE,
BORDER,
FOOT,
BEAK,
EYE,
FACE,
HOOD,
NOSE,
BELLY,
UNDERBELLY,
WING,
WING_EDGE,
HEART,
HEART_BORDER,
HEART_SHINE,
FEATHER_SPINE,
SPRITE_SHEET_COLOR_MAP,
Directions
} from './constants.js';
import Frame from './Frame.js';
import Layer from './Layer.js';
import BirdType from './birdType.js';
// @ts-ignore
const SHARED_CONFIG = {
birbCssScale: 1,
uiCssScale: 1,
@@ -57,84 +85,6 @@ let userSettings = {};
const STYLESHEET = `___STYLESHEET___`;
class Layer {
/**
* @param {string[][]} pixels
* @param {string} [tag]
*/
constructor(pixels, tag = "default") {
this.pixels = pixels;
this.tag = tag;
}
}
class Frame {
/** @type {{ [tag: string]: string[][] }} */
#pixelsByTag = {};
/**
* @param {Layer[]} layers
*/
constructor(layers) {
/** @type {Set<string>} */
let tags = new Set();
for (let layer of layers) {
tags.add(layer.tag);
}
tags.add("default");
for (let tag of tags) {
let maxHeight = layers.reduce((max, layer) => Math.max(max, layer.pixels.length), 0);
if (layers[0].tag !== "default") {
throw new Error("First layer must have the 'default' tag");
}
this.pixels = layers[0].pixels.map(row => row.slice());
// Pad from top with transparent pixels
while (this.pixels.length < maxHeight) {
this.pixels.unshift(new Array(this.pixels[0].length).fill(TRANSPARENT));
}
// Combine layers
for (let i = 1; i < layers.length; i++) {
if (layers[i].tag === "default" || layers[i].tag === tag) {
let layerPixels = layers[i].pixels;
let topMargin = maxHeight - layerPixels.length;
for (let y = 0; y < layerPixels.length; y++) {
for (let x = 0; x < layerPixels[y].length; x++) {
this.pixels[y + topMargin][x] = layerPixels[y][x] !== TRANSPARENT ? layerPixels[y][x] : this.pixels[y + topMargin][x];
}
}
}
}
this.#pixelsByTag[tag] = this.pixels.map(row => row.slice());
}
}
/**
* @param {string} [tag]
* @returns {string[][]}
*/
getPixels(tag = "default") {
return this.#pixelsByTag[tag] ?? this.#pixelsByTag["default"];
}
/**
* @param {CanvasRenderingContext2D} ctx
* @param {number} direction
* @param {BirdType} [species]
*/
draw(ctx, direction, species) {
const pixels = this.getPixels(species?.tags[0]);
for (let y = 0; y < pixels.length; y++) {
const row = pixels[y];
for (let x = 0; x < pixels[y].length; x++) {
const cell = direction === Directions.LEFT ? row[x] : row[pixels[y].length - x - 1];
ctx.fillStyle = species?.colors[cell] ?? cell;
ctx.fillRect(x * CANVAS_PIXEL_SIZE, y * CANVAS_PIXEL_SIZE, CANVAS_PIXEL_SIZE, CANVAS_PIXEL_SIZE);
};
};
}
}
class Anim {
/**
* @param {Frame[]} frames
@@ -168,85 +118,16 @@ class Anim {
for (let i = 0; i < this.durations.length; i++) {
totalDuration += this.durations[i];
if (time < totalDuration) {
this.frames[i].draw(ctx, direction, species);
this.frames[i].draw(ctx, direction, CANVAS_PIXEL_SIZE, species);
return false;
}
}
// Draw the last frame if the animation is complete
this.frames[this.frames.length - 1].draw(ctx, direction, species);
this.frames[this.frames.length - 1].draw(ctx, direction, CANVAS_PIXEL_SIZE, species);
return true;
}
}
const THEME_HIGHLIGHT = "theme-highlight";
const TRANSPARENT = "transparent";
const OUTLINE = "outline";
const BORDER = "border";
const FOOT = "foot";
const BEAK = "beak";
const EYE = "eye";
const FACE = "face";
const HOOD = "hood";
const NOSE = "nose";
const BELLY = "belly";
const UNDERBELLY = "underbelly";
const WING = "wing";
const WING_EDGE = "wing-edge";
const HEART = "heart";
const HEART_BORDER = "heart-border";
const HEART_SHINE = "heart-shine";
const FEATHER_SPINE = "feather-spine";
/** @type {Record<string, string>} */
const SPRITE_SHEET_COLOR_MAP = {
"transparent": TRANSPARENT,
"#ffffff": BORDER,
"#000000": OUTLINE,
"#010a19": BEAK,
"#190301": EYE,
"#af8e75": FOOT,
"#639bff": FACE,
"#99e550": HOOD,
"#d95763": NOSE,
"#f8b143": BELLY,
"#ec8637": UNDERBELLY,
"#578ae6": WING,
"#326ed9": WING_EDGE,
"#c82e2e": HEART,
"#501a1a": HEART_BORDER,
"#ff6b6b": HEART_SHINE,
"#373737": FEATHER_SPINE,
};
class BirdType {
/**
* @param {string} name
* @param {string} description
* @param {Record<string, string>} colors
* @param {string[]} [tags]
*/
constructor(name, description, colors, tags = []) {
this.name = name;
this.description = description;
const defaultColors = {
[TRANSPARENT]: "transparent",
[OUTLINE]: "#000000",
[BORDER]: "#ffffff",
[BEAK]: "#000000",
[EYE]: "#000000",
[HEART]: "#c82e2e",
[HEART_BORDER]: "#501a1a",
[HEART_SHINE]: "#ff6b6b",
[FEATHER_SPINE]: "#373737",
[HOOD]: colors.face,
[NOSE]: colors.face,
};
/** @type {Record<string, string>} */
this.colors = { ...defaultColors, ...colors, [THEME_HIGHLIGHT]: colors[THEME_HIGHLIGHT] ?? colors.hood ?? colors.face };
this.tags = tags;
}
}
/** @type {Record<string, BirdType>} */
const species = {
bluebird: new BirdType("Eastern Bluebird",
@@ -377,12 +258,6 @@ const species = {
const DEFAULT_BIRD = "bluebird";
const Directions = {
LEFT: -1,
RIGHT: 1,
};
const SPRITE_WIDTH = 32;
const SPRITE_HEIGHT = 32;
const DECORATIONS_SPRITE_WIDTH = 48;
@@ -1339,7 +1214,7 @@ Promise.all([
if (!speciesCtx) {
return;
}
birbFrames.base.draw(speciesCtx, Directions.RIGHT, type);
birbFrames.base.draw(speciesCtx, Directions.RIGHT, CANVAS_PIXEL_SIZE, type);
speciesElement.appendChild(speciesCanvas);
content.appendChild(speciesElement);
if (unlocked) {

47
src/birdType.js Normal file
View File

@@ -0,0 +1,47 @@
// @ts-check
import {
THEME_HIGHLIGHT,
OUTLINE,
BORDER,
BEAK,
EYE,
HEART,
HEART_BORDER,
HEART_SHINE,
FEATHER_SPINE,
TRANSPARENT,
NOSE,
HOOD
} from './constants.js';
class BirdType {
/**
* @param {string} name
* @param {string} description
* @param {Record<string, string>} colors
* @param {string[]} [tags]
*/
constructor(name, description, colors, tags = []) {
this.name = name;
this.description = description;
const defaultColors = {
[TRANSPARENT]: "transparent",
[OUTLINE]: "#000000",
[BORDER]: "#ffffff",
[BEAK]: "#000000",
[EYE]: "#000000",
[HEART]: "#c82e2e",
[HEART_BORDER]: "#501a1a",
[HEART_SHINE]: "#ff6b6b",
[FEATHER_SPINE]: "#373737",
[HOOD]: colors.face,
[NOSE]: colors.face,
};
/** @type {Record<string, string>} */
this.colors = { ...defaultColors, ...colors, [THEME_HIGHLIGHT]: colors[THEME_HIGHLIGHT] ?? colors.hood ?? colors.face };
this.tags = tags;
}
}
export default BirdType;

47
src/constants.js Normal file
View File

@@ -0,0 +1,47 @@
// @ts-check
// Theme color indicators
export const THEME_HIGHLIGHT = "theme-highlight";
export const TRANSPARENT = "transparent";
export const OUTLINE = "outline";
export const BORDER = "border";
export const FOOT = "foot";
export const BEAK = "beak";
export const EYE = "eye";
export const FACE = "face";
export const HOOD = "hood";
export const NOSE = "nose";
export const BELLY = "belly";
export const UNDERBELLY = "underbelly";
export const WING = "wing";
export const WING_EDGE = "wing-edge";
export const HEART = "heart";
export const HEART_BORDER = "heart-border";
export const HEART_SHINE = "heart-shine";
export const FEATHER_SPINE = "feather-spine";
/** @type {Record<string, string>} */
export const SPRITE_SHEET_COLOR_MAP = {
"transparent": TRANSPARENT,
"#ffffff": BORDER,
"#000000": OUTLINE,
"#010a19": BEAK,
"#190301": EYE,
"#af8e75": FOOT,
"#639bff": FACE,
"#99e550": HOOD,
"#d95763": NOSE,
"#f8b143": BELLY,
"#ec8637": UNDERBELLY,
"#578ae6": WING,
"#326ed9": WING_EDGE,
"#c82e2e": HEART,
"#501a1a": HEART_BORDER,
"#ff6b6b": HEART_SHINE,
"#373737": FEATHER_SPINE,
};
export const Directions = {
LEFT: -1,
RIGHT: 1,
};