adding advanced settings

This commit is contained in:
Dan 2026-01-09 20:20:18 +00:00
parent 5a004981a9
commit b27f9cfb39
3 changed files with 423 additions and 5 deletions

View file

@ -1,5 +1,7 @@
import { ButtonEffect } from './effect-base.js';
import { ColorQuantizer } from './color-quantizer.js';
/**
* Animation state class - passed to effects for frame-based rendering
*/
@ -7,7 +9,7 @@ export class AnimationState {
constructor(frameNumber = 0, totalFrames = 40, fps = 20) {
this.frame = frameNumber;
this.totalFrames = totalFrames;
this.progress = frameNumber / totalFrames; // 0 to 1
this.progress = totalFrames > 1 ? frameNumber / (totalFrames - 1) : 0; // 0 to 1, inclusive of last frame
this.fps = fps;
this.time = (frameNumber / fps) * 1000; // milliseconds
}
@ -39,6 +41,13 @@ export class ButtonGenerator {
}
};
// GIF export configuration
this.gifConfig = {
quality: config.gifQuality || 1, // 1-30, lower is better quality quantization
dither: config.gifDither || false, // false, 'FloydSteinberg', 'FalseFloydSteinberg', 'Stucki', 'Atkinson'
colorCount: config.gifColorCount || 256, // 2-256, number of colors to reduce to (custom quantization)
};
// Effect registry organized by type
this.effects = {
transform: [],
@ -250,6 +259,7 @@ export class ButtonGenerator {
this.animConfig.fps
);
this.draw(animState);
this.applyPreviewQuantization();
frameNum = (frameNum + 1) % this.animConfig.totalFrames;
lastFrameTime = currentTime - (elapsed % frameDelay);
@ -280,15 +290,31 @@ export class ButtonGenerator {
} else {
this.stopAnimatedPreview();
this.draw();
this.applyPreviewQuantization();
}
}
/**
* Apply color quantization to preview if enabled
*/
applyPreviewQuantization() {
const colorCount = this.gifConfig.colorCount;
if (colorCount < 256) {
const quantizedData = ColorQuantizer.quantize(this.canvas, colorCount, 'floyd-steinberg');
this.ctx.putImageData(quantizedData, 0, 0);
}
}
/**
* Export as animated GIF
* @param {Function} progressCallback - Called with progress (0-1)
* @param {Object} options - Export options
* @param {number} options.quality - Quality (1-30, lower is better, default: 10)
* @param {boolean|string} options.dither - Dithering algorithm for gif.js
* @param {number} options.colorCount - Number of colors (2-256, default: 256) - uses custom quantization
* @returns {Promise<Blob>}
*/
async exportAsGif(progressCallback = null) {
async exportAsGif(progressCallback = null, options = {}) {
return new Promise((resolve, reject) => {
try {
// Create temporary canvas for frame generation
@ -297,14 +323,30 @@ export class ButtonGenerator {
frameCanvas.height = this.canvas.height;
const frameCtx = frameCanvas.getContext('2d');
// Merge options with defaults
const quality = options.quality !== undefined ? options.quality : this.gifConfig.quality;
const gifDither = options.dither !== undefined ? options.dither : this.gifConfig.dither;
const colorCount = options.colorCount !== undefined ? options.colorCount : this.gifConfig.colorCount;
// Determine if we need custom quantization
const useCustomQuantization = colorCount < 256;
const customDither = useCustomQuantization ? 'floyd-steinberg' : false;
// Initialize gif.js
const gif = new GIF({
const gifOptions = {
workers: 2,
quality: 10,
quality: quality,
workerScript: '/js/gif.worker.js',
width: this.canvas.width,
height: this.canvas.height
});
};
// Add gif.js dither option if specified (only when not using custom quantization)
if (!useCustomQuantization && gifDither !== false) {
gifOptions.dither = gifDither;
}
const gif = new GIF(gifOptions);
// Generate frames
const totalFrames = this.animConfig.totalFrames;
@ -328,6 +370,12 @@ export class ButtonGenerator {
tempGenerator.draw(animState);
// Apply custom color quantization if needed
if (useCustomQuantization) {
const quantizedData = ColorQuantizer.quantize(frameCanvas, colorCount, customDither);
frameCtx.putImageData(quantizedData, 0, 0);
}
gif.addFrame(frameCtx, {
delay: 1000 / this.animConfig.fps,
copy: true