mirror of
https://github.com/igorski/bitmappery.git
synced 2026-07-14 10:58:55 +02:00
### Motivation In order to be more useful as a spritesheet editor, BitMappery now contains a timeline view, a mode where content can be sub grouped into tiles, where each subsequent tile can be traced over the previous one. Each tile can have multiple layers of content for ease of editing. ### Changes * Introduced Document types `default` and `timeline` * Introduced LayerRef types (allow grouping Layers) * Introduced Timeline view for tile based drawing and tracing * Introduced Document background color (omits need to create background layer for each tile in a timeline) * Cleaned up some legacy overrides made superfluous by newer dependencies * Added document presets to document creation flow ### Commits * Add rel to Layer structure * Add Document type * Initial scaffold for timeline view. * Added initial utility to manage timelines * Added action to clone all Layers in a tile group * Moved Layer cloning to layer-util * Converted zoom tool option panel to TypeScript * Added action to add a new tile and layer to the Document * Added action to remove a tile and its layers from the Document * Added Layer grouping property to Document structure * Update type check for test * Initial timeline panel outline * Created tiles now match Document dimensions * Added tile cache * Allow showing a semi transparent carbon copy trace of the previous tile * Add animation preview window * Add fps control to animation preview * Store metadata property inside Documents (allows storing timeline framerate) * Optimise mobile view for timeline * Keep thumbnail ratios when previewing tiles and animations * Button and animation preview window styling * use RAF-based animation timing in animation preview * Changes made to layer content now trigger a re-render of the Group tile * Update Layer reordering logic to also work with subsets * Optimised animated GIF export, updated pixel art definitions to be more sensible * Code cleanups * Added background color to Documents, omitting the need for a background layer on each animation tile * Removing temp code, updating renderer factory test * Update layer styling, added tooltip on drag behaviour * Added document DPI and size unit to meta data * Refactored deprecated event property from modal key handler * Remove unused properties from animation preview window * Update unit test for renderer factory * Fix bug where closing modals would trigger click on canvas * Add presets for all Document types * Added setting to automatically choose appropriate anti-alias setting * Invalidate tile and thumbnail caches on resize and crop functions. Adjust spritesheet export behaviour
87 lines
3.0 KiB
TypeScript
87 lines
3.0 KiB
TypeScript
/**
|
|
* The MIT License (MIT)
|
|
*
|
|
* Igor Zinken 2026 - https://www.igorski.nl
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
import type { Document, RelId } from "@/definitions/document";
|
|
import { scaleToFixedHeight } from "@/math/image-math";
|
|
import { getPixelRatio, resizeImage } from "@/utils/canvas-util";
|
|
import { createGroupSnapshot } from "@/utils/document-util";
|
|
|
|
export const THUMB_HEIGHT = 50;
|
|
|
|
export type Tile = {
|
|
source: HTMLCanvasElement;
|
|
thumb: HTMLCanvasElement;
|
|
};
|
|
const tileCache = new Map<RelId, Tile>;
|
|
|
|
type SubscriberCallback = ( id: RelId, data: Tile ) => void;
|
|
const subscribers = new Map<string, SubscriberCallback>;
|
|
|
|
export const subscribe = ( subscriberId: string, callback: SubscriberCallback ): void => {
|
|
if ( subscribers.has( subscriberId )) {
|
|
return;
|
|
}
|
|
subscribers.set( subscriberId, callback );
|
|
};
|
|
|
|
export const unsubscribe = ( subscriberId: string ): void => {
|
|
subscribers.delete( subscriberId );
|
|
};
|
|
|
|
export const createGroupTile = async ( id: RelId, activeDocument?: Document ): Promise<void> => {
|
|
if ( !activeDocument ) {
|
|
return;
|
|
}
|
|
// console.info( `creating tile for group ${id}` );
|
|
|
|
const snapshot = await createGroupSnapshot( activeDocument, id );
|
|
const thumbSize = scaleToFixedHeight( snapshot.width, snapshot.height, THUMB_HEIGHT * getPixelRatio());
|
|
const thumb = await resizeImage(
|
|
snapshot, thumbSize.width, thumbSize.height,
|
|
);
|
|
const tile = {
|
|
source: snapshot,
|
|
thumb,
|
|
}
|
|
tileCache.set( id, tile );
|
|
|
|
for ( const subscriber of subscribers.values() ) {
|
|
subscriber( id, tile );
|
|
}
|
|
};
|
|
|
|
export const hasTile = ( id: RelId ): boolean => tileCache.has( id );
|
|
|
|
export const getTileForGroup = ( id: RelId ): Tile | undefined => {
|
|
return tileCache.get( id );
|
|
};
|
|
|
|
export const flushTileForGroup = ( id: RelId ): void => {
|
|
// console.info( `flushing tile for group ${id}` );
|
|
tileCache.delete( id );
|
|
};
|
|
|
|
export const flushTileCache = (): void => {
|
|
// console.info( "flushing tile cache" );
|
|
tileCache.clear();
|
|
};
|