mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-26 04:07:24 +00:00
Only draw frames when necessary
This commit is contained in:
70
dist/birb.js
vendored
70
dist/birb.js
vendored
@@ -454,6 +454,9 @@
|
|||||||
* @param {number} canvasPixelSize
|
* @param {number} canvasPixelSize
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, canvasPixelSize, species) {
|
draw(ctx, direction, canvasPixelSize, species) {
|
||||||
|
// Clear the canvas before drawing the new frame
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
const pixels = this.getPixels(species?.tags[0]);
|
const pixels = this.getPixels(species?.tags[0]);
|
||||||
for (let y = 0; y < pixels.length; y++) {
|
for (let y = 0; y < pixels.length; y++) {
|
||||||
const row = pixels[y];
|
const row = pixels[y];
|
||||||
@@ -474,12 +477,49 @@
|
|||||||
this.frames = frames;
|
this.frames = frames;
|
||||||
this.durations = durations;
|
this.durations = durations;
|
||||||
this.loop = loop;
|
this.loop = loop;
|
||||||
|
this.lastFrameIndex = -1;
|
||||||
|
this.lastDirection = null;
|
||||||
|
this.lastTimeStart = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationDuration() {
|
getAnimationDuration() {
|
||||||
return this.durations.reduce((a, b) => a + b, 0);
|
return this.durations.reduce((a, b) => a + b, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current frame index based on elapsed time
|
||||||
|
* @param {number} time The elapsed time since animation start
|
||||||
|
* @returns {number} The index of the current frame
|
||||||
|
*/
|
||||||
|
getCurrentFrameIndex(time) {
|
||||||
|
let totalDuration = 0;
|
||||||
|
for (let i = 0; i < this.durations.length; i++) {
|
||||||
|
totalDuration += this.durations[i];
|
||||||
|
if (time < totalDuration) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.frames.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cached frame state
|
||||||
|
*/
|
||||||
|
#clearCache() {
|
||||||
|
this.lastFrameIndex = -1;
|
||||||
|
this.lastDirection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the frame needs to be redrawn
|
||||||
|
* @param {number} frameIndex The current frame index
|
||||||
|
* @param {number} direction The current direction
|
||||||
|
* @returns {boolean} Whether the frame needs to be redrawn
|
||||||
|
*/
|
||||||
|
#shouldRedraw(frameIndex, direction) {
|
||||||
|
return frameIndex !== this.lastFrameIndex || direction !== this.lastDirection;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
* @param {number} direction
|
* @param {number} direction
|
||||||
@@ -489,22 +529,29 @@
|
|||||||
* @returns {boolean} Whether the animation is complete
|
* @returns {boolean} Whether the animation is complete
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, timeStart, canvasPixelSize, species) {
|
draw(ctx, direction, timeStart, canvasPixelSize, species) {
|
||||||
|
// Reset cache if animation was restarted
|
||||||
|
if (this.lastTimeStart !== timeStart) {
|
||||||
|
this.#clearCache();
|
||||||
|
this.lastTimeStart = timeStart;
|
||||||
|
}
|
||||||
|
|
||||||
let time = Date.now() - timeStart;
|
let time = Date.now() - timeStart;
|
||||||
const duration = this.getAnimationDuration();
|
const duration = this.getAnimationDuration();
|
||||||
|
|
||||||
if (this.loop) {
|
if (this.loop) {
|
||||||
time %= duration;
|
time %= duration;
|
||||||
}
|
}
|
||||||
let totalDuration = 0;
|
|
||||||
for (let i = 0; i < this.durations.length; i++) {
|
const currentFrameIndex = this.getCurrentFrameIndex(time);
|
||||||
totalDuration += this.durations[i];
|
|
||||||
if (time < totalDuration) {
|
if (this.#shouldRedraw(currentFrameIndex, direction)) {
|
||||||
this.frames[i].draw(ctx, direction, canvasPixelSize, species);
|
this.frames[currentFrameIndex].draw(ctx, direction, canvasPixelSize, species);
|
||||||
return false;
|
this.lastFrameIndex = currentFrameIndex;
|
||||||
}
|
this.lastDirection = direction;
|
||||||
}
|
}
|
||||||
// Draw the last frame if the animation is complete
|
|
||||||
this.frames[this.frames.length - 1].draw(ctx, direction, canvasPixelSize, species);
|
// Return whether animation is complete (for non-looping animations)
|
||||||
return true;
|
return !this.loop && time >= duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +676,6 @@
|
|||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
draw(species) {
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
const anim = this.animations[this.currentAnimation];
|
const anim = this.animations[this.currentAnimation];
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
}
|
}
|
||||||
@@ -1606,7 +1652,7 @@
|
|||||||
insertModal(`${birdBirb()} Mode`, message);
|
insertModal(`${birdBirb()} Mode`, message);
|
||||||
}),
|
}),
|
||||||
new Separator(),
|
new Separator(),
|
||||||
new MenuItem("2025.10.28.47", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.47"); }, false),
|
new MenuItem("2025.10.28.80", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.80"); }, false),
|
||||||
];
|
];
|
||||||
|
|
||||||
const styleElement = document.createElement("style");
|
const styleElement = document.createElement("style");
|
||||||
|
|||||||
72
dist/birb.user.js
vendored
72
dist/birb.user.js
vendored
@@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Pocket Bird
|
// @name Pocket Bird
|
||||||
// @namespace https://idreesinc.com
|
// @namespace https://idreesinc.com
|
||||||
// @version 2025.10.28.47
|
// @version 2025.10.28.80
|
||||||
// @description birb
|
// @description birb
|
||||||
// @author Idrees
|
// @author Idrees
|
||||||
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js
|
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js
|
||||||
@@ -468,6 +468,9 @@
|
|||||||
* @param {number} canvasPixelSize
|
* @param {number} canvasPixelSize
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, canvasPixelSize, species) {
|
draw(ctx, direction, canvasPixelSize, species) {
|
||||||
|
// Clear the canvas before drawing the new frame
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
const pixels = this.getPixels(species?.tags[0]);
|
const pixels = this.getPixels(species?.tags[0]);
|
||||||
for (let y = 0; y < pixels.length; y++) {
|
for (let y = 0; y < pixels.length; y++) {
|
||||||
const row = pixels[y];
|
const row = pixels[y];
|
||||||
@@ -488,12 +491,49 @@
|
|||||||
this.frames = frames;
|
this.frames = frames;
|
||||||
this.durations = durations;
|
this.durations = durations;
|
||||||
this.loop = loop;
|
this.loop = loop;
|
||||||
|
this.lastFrameIndex = -1;
|
||||||
|
this.lastDirection = null;
|
||||||
|
this.lastTimeStart = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationDuration() {
|
getAnimationDuration() {
|
||||||
return this.durations.reduce((a, b) => a + b, 0);
|
return this.durations.reduce((a, b) => a + b, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current frame index based on elapsed time
|
||||||
|
* @param {number} time The elapsed time since animation start
|
||||||
|
* @returns {number} The index of the current frame
|
||||||
|
*/
|
||||||
|
getCurrentFrameIndex(time) {
|
||||||
|
let totalDuration = 0;
|
||||||
|
for (let i = 0; i < this.durations.length; i++) {
|
||||||
|
totalDuration += this.durations[i];
|
||||||
|
if (time < totalDuration) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.frames.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cached frame state
|
||||||
|
*/
|
||||||
|
#clearCache() {
|
||||||
|
this.lastFrameIndex = -1;
|
||||||
|
this.lastDirection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the frame needs to be redrawn
|
||||||
|
* @param {number} frameIndex The current frame index
|
||||||
|
* @param {number} direction The current direction
|
||||||
|
* @returns {boolean} Whether the frame needs to be redrawn
|
||||||
|
*/
|
||||||
|
#shouldRedraw(frameIndex, direction) {
|
||||||
|
return frameIndex !== this.lastFrameIndex || direction !== this.lastDirection;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
* @param {number} direction
|
* @param {number} direction
|
||||||
@@ -503,22 +543,29 @@
|
|||||||
* @returns {boolean} Whether the animation is complete
|
* @returns {boolean} Whether the animation is complete
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, timeStart, canvasPixelSize, species) {
|
draw(ctx, direction, timeStart, canvasPixelSize, species) {
|
||||||
|
// Reset cache if animation was restarted
|
||||||
|
if (this.lastTimeStart !== timeStart) {
|
||||||
|
this.#clearCache();
|
||||||
|
this.lastTimeStart = timeStart;
|
||||||
|
}
|
||||||
|
|
||||||
let time = Date.now() - timeStart;
|
let time = Date.now() - timeStart;
|
||||||
const duration = this.getAnimationDuration();
|
const duration = this.getAnimationDuration();
|
||||||
|
|
||||||
if (this.loop) {
|
if (this.loop) {
|
||||||
time %= duration;
|
time %= duration;
|
||||||
}
|
}
|
||||||
let totalDuration = 0;
|
|
||||||
for (let i = 0; i < this.durations.length; i++) {
|
const currentFrameIndex = this.getCurrentFrameIndex(time);
|
||||||
totalDuration += this.durations[i];
|
|
||||||
if (time < totalDuration) {
|
if (this.#shouldRedraw(currentFrameIndex, direction)) {
|
||||||
this.frames[i].draw(ctx, direction, canvasPixelSize, species);
|
this.frames[currentFrameIndex].draw(ctx, direction, canvasPixelSize, species);
|
||||||
return false;
|
this.lastFrameIndex = currentFrameIndex;
|
||||||
}
|
this.lastDirection = direction;
|
||||||
}
|
}
|
||||||
// Draw the last frame if the animation is complete
|
|
||||||
this.frames[this.frames.length - 1].draw(ctx, direction, canvasPixelSize, species);
|
// Return whether animation is complete (for non-looping animations)
|
||||||
return true;
|
return !this.loop && time >= duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,7 +690,6 @@
|
|||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
draw(species) {
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
const anim = this.animations[this.currentAnimation];
|
const anim = this.animations[this.currentAnimation];
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
}
|
}
|
||||||
@@ -1620,7 +1666,7 @@
|
|||||||
insertModal(`${birdBirb()} Mode`, message);
|
insertModal(`${birdBirb()} Mode`, message);
|
||||||
}),
|
}),
|
||||||
new Separator(),
|
new Separator(),
|
||||||
new MenuItem("2025.10.28.47", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.47"); }, false),
|
new MenuItem("2025.10.28.80", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.28.80"); }, false),
|
||||||
];
|
];
|
||||||
|
|
||||||
const styleElement = document.createElement("style");
|
const styleElement = document.createElement("style");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Pocket Bird",
|
"name": "Pocket Bird",
|
||||||
"description": "It's a bird, in your browser. What more could you want?",
|
"description": "It's a bird, in your browser. What more could you want?",
|
||||||
"version": "2025.10.28.47",
|
"version": "2025.10.28.80",
|
||||||
"homepage_url": "https://idreesinc.com",
|
"homepage_url": "https://idreesinc.com",
|
||||||
"icons": {
|
"icons": {
|
||||||
"48": "images/icons/transparent/48x48x1.png",
|
"48": "images/icons/transparent/48x48x1.png",
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ class Frame {
|
|||||||
* @param {number} canvasPixelSize
|
* @param {number} canvasPixelSize
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, canvasPixelSize, species) {
|
draw(ctx, direction, canvasPixelSize, species) {
|
||||||
|
// Clear the canvas before drawing the new frame
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
const pixels = this.getPixels(species?.tags[0]);
|
const pixels = this.getPixels(species?.tags[0]);
|
||||||
for (let y = 0; y < pixels.length; y++) {
|
for (let y = 0; y < pixels.length; y++) {
|
||||||
const row = pixels[y];
|
const row = pixels[y];
|
||||||
|
|||||||
64
src/anim.js
64
src/anim.js
@@ -11,12 +11,49 @@ class Anim {
|
|||||||
this.frames = frames;
|
this.frames = frames;
|
||||||
this.durations = durations;
|
this.durations = durations;
|
||||||
this.loop = loop;
|
this.loop = loop;
|
||||||
|
this.lastFrameIndex = -1;
|
||||||
|
this.lastDirection = null;
|
||||||
|
this.lastTimeStart = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationDuration() {
|
getAnimationDuration() {
|
||||||
return this.durations.reduce((a, b) => a + b, 0);
|
return this.durations.reduce((a, b) => a + b, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current frame index based on elapsed time
|
||||||
|
* @param {number} time The elapsed time since animation start
|
||||||
|
* @returns {number} The index of the current frame
|
||||||
|
*/
|
||||||
|
getCurrentFrameIndex(time) {
|
||||||
|
let totalDuration = 0;
|
||||||
|
for (let i = 0; i < this.durations.length; i++) {
|
||||||
|
totalDuration += this.durations[i];
|
||||||
|
if (time < totalDuration) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.frames.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cached frame state
|
||||||
|
*/
|
||||||
|
#clearCache() {
|
||||||
|
this.lastFrameIndex = -1;
|
||||||
|
this.lastDirection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the frame needs to be redrawn
|
||||||
|
* @param {number} frameIndex The current frame index
|
||||||
|
* @param {number} direction The current direction
|
||||||
|
* @returns {boolean} Whether the frame needs to be redrawn
|
||||||
|
*/
|
||||||
|
#shouldRedraw(frameIndex, direction) {
|
||||||
|
return frameIndex !== this.lastFrameIndex || direction !== this.lastDirection;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
* @param {number} direction
|
* @param {number} direction
|
||||||
@@ -26,22 +63,29 @@ class Anim {
|
|||||||
* @returns {boolean} Whether the animation is complete
|
* @returns {boolean} Whether the animation is complete
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, timeStart, canvasPixelSize, species) {
|
draw(ctx, direction, timeStart, canvasPixelSize, species) {
|
||||||
|
// Reset cache if animation was restarted
|
||||||
|
if (this.lastTimeStart !== timeStart) {
|
||||||
|
this.#clearCache();
|
||||||
|
this.lastTimeStart = timeStart;
|
||||||
|
}
|
||||||
|
|
||||||
let time = Date.now() - timeStart;
|
let time = Date.now() - timeStart;
|
||||||
const duration = this.getAnimationDuration();
|
const duration = this.getAnimationDuration();
|
||||||
|
|
||||||
if (this.loop) {
|
if (this.loop) {
|
||||||
time %= duration;
|
time %= duration;
|
||||||
}
|
}
|
||||||
let totalDuration = 0;
|
|
||||||
for (let i = 0; i < this.durations.length; i++) {
|
const currentFrameIndex = this.getCurrentFrameIndex(time);
|
||||||
totalDuration += this.durations[i];
|
|
||||||
if (time < totalDuration) {
|
if (this.#shouldRedraw(currentFrameIndex, direction)) {
|
||||||
this.frames[i].draw(ctx, direction, canvasPixelSize, species);
|
this.frames[currentFrameIndex].draw(ctx, direction, canvasPixelSize, species);
|
||||||
return false;
|
this.lastFrameIndex = currentFrameIndex;
|
||||||
}
|
this.lastDirection = direction;
|
||||||
}
|
}
|
||||||
// Draw the last frame if the animation is complete
|
|
||||||
this.frames[this.frames.length - 1].draw(ctx, direction, canvasPixelSize, species);
|
// Return whether animation is complete (for non-looping animations)
|
||||||
return true;
|
return !this.loop && time >= duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,6 @@ export class Birb {
|
|||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
draw(species) {
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
const anim = this.animations[this.currentAnimation];
|
const anim = this.animations[this.currentAnimation];
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ class Frame {
|
|||||||
* @param {number} canvasPixelSize
|
* @param {number} canvasPixelSize
|
||||||
*/
|
*/
|
||||||
draw(ctx, direction, canvasPixelSize, species) {
|
draw(ctx, direction, canvasPixelSize, species) {
|
||||||
|
// Clear the canvas before drawing the new frame
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
const pixels = this.getPixels(species?.tags[0]);
|
const pixels = this.getPixels(species?.tags[0]);
|
||||||
for (let y = 0; y < pixels.length; y++) {
|
for (let y = 0; y < pixels.length; y++) {
|
||||||
const row = pixels[y];
|
const row = pixels[y];
|
||||||
|
|||||||
Reference in New Issue
Block a user