import IconRotationAnimation from './IconRotationAnimation';
const rotator = new IconRotationAnimation();
await rotator.initialize('chrome_extesion_icon.png');
rotator.start(); // Continuous rotation
rotator.rotateCycles(2); // Rotate 2 cycles and stop
rotator.stop(); // Stop and reset to initial position
Last active
May 16, 2025 00:48
-
-
Save HereOrCode/7e987249188e406c7fe1f064c56c10ce to your computer and use it in GitHub Desktop.
Chrome Extension Development: Animates the browser action icon by rotating it
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class IconRotationAnimation { | |
private loggedInImage: ImageBitmap | null = null; | |
private canvas: OffscreenCanvas | null = null; | |
private canvasContext: OffscreenCanvasRenderingContext2D | null = null; | |
private animationProgress: number = 0; | |
private isAnimating: boolean = false; | |
private animationTimer: number | null = null; | |
private readonly ANIMATION_DURATION: number = 500; // Duration for one full rotation (ms) | |
private readonly FRAME_INTERVAL: number = 16.67; // Approx 60fps (1000ms / 60) | |
private readonly ROTATION_SPEED: number; // Rotation degrees per frame | |
private cycleCount: number = 0; // Current number of completed cycles | |
private targetCycles: number = 0; // Target number of cycles for rotateCycles | |
constructor() { | |
this.ROTATION_SPEED = 360 / (this.ANIMATION_DURATION / this.FRAME_INTERVAL); // Based on 60fps | |
} | |
// Load the icon image | |
async initialize(imagePath: string): Promise<void> { | |
try { | |
const request = await fetch(chrome.runtime.getURL(imagePath)); | |
const blob = await request.blob(); | |
this.loggedInImage = await createImageBitmap(blob); | |
this.canvas = new OffscreenCanvas( | |
this.loggedInImage.width, | |
this.loggedInImage.height | |
); | |
this.canvasContext = this.canvas.getContext("2d")!; | |
} catch (error) { | |
console.error("Failed to load image:", error); | |
} | |
} | |
// Easing function (provided by user) | |
private ease(x: number): number { | |
return (1 - Math.sin(Math.PI / 2 + x * Math.PI)) / 2; | |
} | |
// Draw the icon at a specific rotation progress | |
private drawIconAtRotation(progress: number): void { | |
if (!this.canvas || !this.canvasContext || !this.loggedInImage) return; | |
const w = this.canvas.width; | |
const h = this.canvas.height; | |
this.canvasContext.clearRect(0, 0, w, h); | |
this.canvasContext.save(); | |
this.canvasContext.translate(w / 2, h / 2); | |
// Apply easing function to rotation angle | |
const easedRotation = 360 * this.ease(progress); | |
this.canvasContext.rotate((easedRotation * Math.PI) / 180); | |
this.canvasContext.drawImage(this.loggedInImage, -w / 2, -h / 2); | |
this.canvasContext.restore(); | |
chrome.action.setIcon({ | |
imageData: this.canvasContext.getImageData(0, 0, w, h), | |
}); | |
} | |
// Animation loop | |
private animateFlip(): void { | |
if (!this.isAnimating) return; | |
// Update animation progress (0 to 1) | |
this.animationProgress = | |
(this.animationProgress + this.ROTATION_SPEED / 360) % 1; | |
this.drawIconAtRotation(this.animationProgress); | |
// Check if a full cycle is completed | |
if (this.animationProgress < this.ROTATION_SPEED / 360) { | |
this.cycleCount++; | |
// Stop if target cycles reached (for rotateCycles) | |
if (this.targetCycles > 0 && this.cycleCount >= this.targetCycles) { | |
this.stop(); | |
return; | |
} | |
} | |
this.animationTimer = setTimeout( | |
() => this.animateFlip(), | |
this.FRAME_INTERVAL | |
); | |
} | |
// Start continuous rotation animation | |
start(): void { | |
if (this.isAnimating || !this.loggedInImage) return; | |
this.isAnimating = true; | |
this.targetCycles = 0; // Continuous rotation | |
this.cycleCount = 0; | |
this.animateFlip(); | |
} | |
// Rotate for a specified number of cycles (default 1) | |
rotateCycles(cycles: number = 1): void { | |
if (this.isAnimating || !this.loggedInImage) return; | |
if (cycles <= 0) return; // Prevent invalid cycles | |
this.isAnimating = true; | |
this.targetCycles = cycles; | |
this.cycleCount = 0; | |
this.animationProgress = 0; | |
this.animateFlip(); | |
} | |
// Stop the rotation animation and reset icon to initial position | |
stop(): void { | |
this.isAnimating = false; | |
if (this.animationTimer !== null) { | |
clearTimeout(this.animationTimer); | |
this.animationTimer = null; | |
} | |
// Reset animation progress and draw initial position | |
this.animationProgress = 0; | |
this.cycleCount = 0; | |
this.targetCycles = 0; | |
this.drawIconAtRotation(0); | |
} | |
} | |
export default IconRotationAnimation; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment