This commit is contained in:
syuilo 2025-05-30 20:30:55 +09:00
parent d1b4456845
commit ddc8072103
5 changed files with 55 additions and 26 deletions

4
locales/index.d.ts vendored
View File

@ -12083,6 +12083,10 @@ export interface Locale extends ILocale {
* *
*/ */
"addEffect": string; "addEffect": string;
/**
*
*/
"discardChangesConfirm": string;
"_fxs": { "_fxs": {
/** /**
* *

View File

@ -3237,6 +3237,7 @@ _watermarkEditor:
_imageEffector: _imageEffector:
title: "エフェクト" title: "エフェクト"
addEffect: "エフェクトを追加" addEffect: "エフェクトを追加"
discardChangesConfirm: "変更を破棄して終了しますか?"
_fxs: _fxs:
chromaticAberration: "色収差" chromaticAberration: "色収差"

View File

@ -61,7 +61,7 @@ import { deepClone } from '@/utility/clone.js';
import { FXS } from '@/utility/image-effector/fxs.js'; import { FXS } from '@/utility/image-effector/fxs.js';
const props = defineProps<{ const props = defineProps<{
image: ImageBitmap; image: File;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -72,7 +72,14 @@ const emit = defineEmits<{
const dialog = useTemplateRef('dialog'); const dialog = useTemplateRef('dialog');
function cancel() { async function cancel() {
if (layers.length > 0) {
const { canceled } = await os.confirm({
text: i18n.ts._imageEffector.discardChangesConfirm,
});
if (canceled) return;
}
emit('cancel'); emit('cancel');
dialog.value?.close(); dialog.value?.close();
} }
@ -108,14 +115,19 @@ function onLayerDelete(layer: ImageEffectorLayer) {
const canvasEl = useTemplateRef('canvasEl'); const canvasEl = useTemplateRef('canvasEl');
let renderer: ImageEffector | null = null; let renderer: ImageEffector | null = null;
let imageBitmap: ImageBitmap | null = null;
onMounted(async () => { onMounted(async () => {
if (canvasEl.value == null) return; if (canvasEl.value == null) return;
const closeWaiting = os.waiting();
imageBitmap = await window.createImageBitmap(props.image);
const MAX_W = 500; const MAX_W = 500;
const MAX_H = 500; const MAX_H = 500;
let w = props.image.width; let w = imageBitmap.width;
let h = props.image.height; let h = imageBitmap.height;
if (w > MAX_W || h > MAX_H) { if (w > MAX_W || h > MAX_H) {
const scale = Math.min(MAX_W / w, MAX_H / h); const scale = Math.min(MAX_W / w, MAX_H / h);
@ -127,13 +139,15 @@ onMounted(async () => {
canvas: canvasEl.value, canvas: canvasEl.value,
renderWidth: w, renderWidth: w,
renderHeight: h, renderHeight: h,
image: props.image, image: imageBitmap,
fxs: FXS, fxs: FXS,
}); });
await renderer.setLayers(layers); await renderer.setLayers(layers);
renderer.render(); renderer.render();
closeWaiting();
}); });
onUnmounted(() => { onUnmounted(() => {
@ -141,19 +155,26 @@ onUnmounted(() => {
renderer.destroy(); renderer.destroy();
renderer = null; renderer = null;
} }
if (imageBitmap != null) {
imageBitmap.close();
imageBitmap = null;
}
}); });
function save() { function save() {
if (layers.length === 0) { if (layers.length === 0 || renderer == null || imageBitmap == null || canvasEl.value == null) {
cancel(); cancel();
return; return;
} }
renderer!.changeResolution(props.image.width, props.image.height); // const closeWaiting = os.waiting();
renderer!.render(); // toBlob
canvasEl.value!.toBlob((blob) => { renderer.changeResolution(imageBitmap.width, imageBitmap.height); //
renderer.render(); // toBlob
canvasEl.value.toBlob((blob) => {
emit('ok', new File([blob!], `image-${Date.now()}.png`, { type: 'image/png' })); emit('ok', new File([blob!], `image-${Date.now()}.png`, { type: 'image/png' }));
dialog.value?.close(); dialog.value?.close();
closeWaiting();
}, 'image/png'); }, 'image/png');
} }

View File

@ -297,9 +297,8 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
icon: 'ti ti-sparkles', icon: 'ti ti-sparkles',
text: i18n.ts._imageEffector.title, text: i18n.ts._imageEffector.title,
action: async () => { action: async () => {
const imageBitmap = await window.createImageBitmap(item.file);
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkImageEffectorDialog.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkImageEffectorDialog.vue')), {
image: imageBitmap, image: item.file,
}, { }, {
ok: (file) => { ok: (file) => {
URL.revokeObjectURL(item.thumbnail); URL.revokeObjectURL(item.thumbnail);
@ -313,10 +312,7 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
triggerRef(items); triggerRef(items);
}); });
}, },
closed: () => { closed: () => dispose(),
imageBitmap.close();
dispose();
},
}); });
}, },
}); });
@ -353,18 +349,14 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
icon: 'ti ti-plus', icon: 'ti ti-plus',
text: i18n.ts.add, text: i18n.ts.add,
action: async () => { action: async () => {
const imageBitmap = await window.createImageBitmap(item.file);
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWatermarkEditorDialog.vue')), { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWatermarkEditorDialog.vue')), {
image: imageBitmap, image: item.file,
}, { }, {
ok: (preset) => { ok: (preset) => {
prefer.commit('watermarkPresets', [...prefer.s.watermarkPresets, preset]); prefer.commit('watermarkPresets', [...prefer.s.watermarkPresets, preset]);
changeWatermarkPreset(preset.id); changeWatermarkPreset(preset.id);
}, },
closed: () => { closed: () => dispose(),
imageBitmap.close();
dispose();
},
}); });
}, },
}], }],

View File

@ -63,7 +63,7 @@ const $i = ensureSignin();
const props = defineProps<{ const props = defineProps<{
preset?: WatermarkPreset | null; preset?: WatermarkPreset | null;
image?: ImageBitmap | null; image?: File | null;
}>(); }>();
const preset = reactive(deepClone(props.preset) ?? { const preset = reactive(deepClone(props.preset) ?? {
@ -149,6 +149,7 @@ watch(sampleImageType, async () => {
}); });
let renderer: WatermarkRenderer | null = null; let renderer: WatermarkRenderer | null = null;
let imageBitmap: ImageBitmap | null = null;
async function initRenderer() { async function initRenderer() {
if (canvasEl.value == null) return; if (canvasEl.value == null) return;
@ -168,10 +169,12 @@ async function initRenderer() {
image: sampleImage_2_3, image: sampleImage_2_3,
}); });
} else if (props.image != null) { } else if (props.image != null) {
imageBitmap = await window.createImageBitmap(props.image);
const MAX_W = 1000; const MAX_W = 1000;
const MAX_H = 1000; const MAX_H = 1000;
let w = props.image.width; let w = imageBitmap.width;
let h = props.image.height; let h = imageBitmap.height;
if (w > MAX_W || h > MAX_H) { if (w > MAX_W || h > MAX_H) {
const scale = Math.min(MAX_W / w, MAX_H / h); const scale = Math.min(MAX_W / w, MAX_H / h);
@ -183,7 +186,7 @@ async function initRenderer() {
canvas: canvasEl.value, canvas: canvasEl.value,
renderWidth: w, renderWidth: w,
renderHeight: h, renderHeight: h,
image: props.image, image: imageBitmap,
}); });
} }
@ -193,10 +196,14 @@ async function initRenderer() {
} }
onMounted(async () => { onMounted(async () => {
const closeWaiting = os.waiting();
await sampleImage_3_2_loading; await sampleImage_3_2_loading;
await sampleImage_2_3_loading; await sampleImage_2_3_loading;
initRenderer(); await initRenderer();
closeWaiting();
}); });
onUnmounted(() => { onUnmounted(() => {
@ -204,6 +211,10 @@ onUnmounted(() => {
renderer.destroy(); renderer.destroy();
renderer = null; renderer = null;
} }
if (imageBitmap != null) {
imageBitmap.close();
imageBitmap = null;
}
}); });
async function save() { async function save() {