[FEAT] Add sharing function, UI enhancement
This commit is contained in:
135
src/composables/useShare.ts
Normal file
135
src/composables/useShare.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import type { Ref } from 'vue';
|
||||
import type { Layer } from '@/types/sprites';
|
||||
|
||||
const POCKETBASE_URL = 'https://pb1.adhd.sh';
|
||||
const COLLECTION = 'spritesheets';
|
||||
|
||||
export interface SpritesheetConfig {
|
||||
version: number;
|
||||
columns: number;
|
||||
negativeSpacingEnabled: boolean;
|
||||
backgroundColor: string;
|
||||
manualCellSizeEnabled: boolean;
|
||||
manualCellWidth: number;
|
||||
manualCellHeight: number;
|
||||
}
|
||||
|
||||
export interface SpritesheetRecord {
|
||||
id: string;
|
||||
config: SpritesheetConfig;
|
||||
sprites: any[];
|
||||
created: string;
|
||||
updated: string;
|
||||
}
|
||||
|
||||
export interface ShareResult {
|
||||
id: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the shareable URL for a spritesheet
|
||||
*/
|
||||
export const buildShareUrl = (id: string): string => {
|
||||
return `${window.location.origin}/share/${id}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Share a spritesheet by uploading to PocketBase
|
||||
*/
|
||||
export const shareSpritesheet = async (layersRef: Ref<Layer[]>, columns: Ref<number>, negativeSpacingEnabled: Ref<boolean>, backgroundColor?: Ref<string>, manualCellSizeEnabled?: Ref<boolean>, manualCellWidth?: Ref<number>, manualCellHeight?: Ref<number>): Promise<ShareResult> => {
|
||||
// Build layers data with base64 sprites (same format as exportSpritesheetJSON)
|
||||
const layersData = await Promise.all(
|
||||
layersRef.value.map(async layer => {
|
||||
const sprites = await Promise.all(
|
||||
layer.sprites.map(async sprite => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return null;
|
||||
canvas.width = sprite.width;
|
||||
canvas.height = sprite.height;
|
||||
ctx.drawImage(sprite.img, 0, 0);
|
||||
const base64 = canvas.toDataURL('image/png');
|
||||
return {
|
||||
id: sprite.id,
|
||||
width: sprite.width,
|
||||
height: sprite.height,
|
||||
x: sprite.x,
|
||||
y: sprite.y,
|
||||
base64,
|
||||
name: sprite.file.name,
|
||||
};
|
||||
})
|
||||
);
|
||||
return {
|
||||
id: layer.id,
|
||||
name: layer.name,
|
||||
visible: layer.visible,
|
||||
locked: layer.locked,
|
||||
sprites: sprites.filter(Boolean),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const config: SpritesheetConfig = {
|
||||
version: 2,
|
||||
columns: columns.value,
|
||||
negativeSpacingEnabled: negativeSpacingEnabled.value,
|
||||
backgroundColor: backgroundColor?.value || 'transparent',
|
||||
manualCellSizeEnabled: manualCellSizeEnabled?.value || false,
|
||||
manualCellWidth: manualCellWidth?.value || 64,
|
||||
manualCellHeight: manualCellHeight?.value || 64,
|
||||
};
|
||||
|
||||
const response = await fetch(`${POCKETBASE_URL}/api/collections/${COLLECTION}/records`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
config,
|
||||
sprites: layersData,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => '');
|
||||
throw new Error(text || `Request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
const record = await response.json();
|
||||
return {
|
||||
id: record.id,
|
||||
url: buildShareUrl(record.id),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch a shared spritesheet from PocketBase
|
||||
*/
|
||||
export const fetchSpritesheet = async (id: string): Promise<SpritesheetRecord> => {
|
||||
const response = await fetch(`${POCKETBASE_URL}/api/collections/${COLLECTION}/records/${id}`);
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
throw new Error('Spritesheet not found');
|
||||
}
|
||||
const text = await response.text().catch(() => '');
|
||||
throw new Error(text || `Request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Composable hook for share functionality
|
||||
*/
|
||||
export const useShare = (layersRef: Ref<Layer[]>, columns: Ref<number>, negativeSpacingEnabled: Ref<boolean>, backgroundColor?: Ref<string>, manualCellSizeEnabled?: Ref<boolean>, manualCellWidth?: Ref<number>, manualCellHeight?: Ref<number>) => {
|
||||
const share = () => shareSpritesheet(layersRef, columns, negativeSpacingEnabled, backgroundColor, manualCellSizeEnabled, manualCellWidth, manualCellHeight);
|
||||
|
||||
return {
|
||||
share,
|
||||
fetchSpritesheet,
|
||||
buildShareUrl,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user