Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save HereOrCode/7e987249188e406c7fe1f064c56c10ce to your computer and use it in GitHub Desktop.
Save HereOrCode/7e987249188e406c7fe1f064c56c10ce to your computer and use it in GitHub Desktop.
Chrome Extension Development: Animates the browser action icon by rotating it
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
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