image loading for web
This commit is contained in:
parent
dbc883924a
commit
6b728c98c0
|
@ -6,22 +6,21 @@
|
|||
'Eyes closed | Mouth open'
|
||||
];
|
||||
|
||||
let loading = [false, false, false, false];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { frames, mode } from '../store';
|
||||
import { fs, invoke } from '@tauri-apps/api';
|
||||
import { fade } from 'svelte/transition';
|
||||
import Context from './context.svelte';
|
||||
import Image from 'image-js';
|
||||
import { Gif } from '../gifler';
|
||||
|
||||
import { ProgressRadial } from '@skeletonlabs/skeleton';
|
||||
import { info } from '$lib/logging';
|
||||
import { loadImage } from '$lib/io';
|
||||
export let index: number;
|
||||
|
||||
let menuTimeout: NodeJS.Timeout | null = null;
|
||||
let showMenu = false;
|
||||
let src: string | null;
|
||||
let fileInput: HTMLInputElement | undefined;
|
||||
let files: FileList;
|
||||
|
||||
$: loading = false;
|
||||
|
||||
$: {
|
||||
const frame = $frames[index];
|
||||
|
@ -36,36 +35,73 @@
|
|||
}
|
||||
|
||||
const openImage = async () => {
|
||||
loading[index] = true;
|
||||
const path = (await invoke('open_image')) as {
|
||||
kind: 'png' | 'gif';
|
||||
data: string;
|
||||
};
|
||||
|
||||
if (path) {
|
||||
const data = await fs.readBinaryFile(path.data);
|
||||
if (path.kind == 'png')
|
||||
$frames[index] = {
|
||||
kind: 'still',
|
||||
value: await Image.load(data)
|
||||
};
|
||||
else {
|
||||
$frames[index] = {
|
||||
kind: 'GIF',
|
||||
value: new Gif(new Promise((res) => res(data))),
|
||||
data: data
|
||||
};
|
||||
}
|
||||
} else {
|
||||
console.error('Error loading image');
|
||||
if (mode === 'web') {
|
||||
fileInput?.click();
|
||||
loading = true;
|
||||
}
|
||||
loading[index] = false;
|
||||
// loading[index] = true;
|
||||
// const path = (await invoke('open_image')) as {
|
||||
// kind: 'png' | 'gif';
|
||||
// data: string;
|
||||
// };
|
||||
|
||||
// if (path) {
|
||||
// const data = await fs.readBinaryFile(path.data);
|
||||
// if (path.kind == 'png')
|
||||
// $frames[index] = {
|
||||
// kind: 'still',
|
||||
// value: await Image.load(data)
|
||||
// };
|
||||
// else {
|
||||
// $frames[index] = {
|
||||
// kind: 'GIF',
|
||||
// value: new Gif(new Promise((res) => res(data))),
|
||||
// data: data
|
||||
// };
|
||||
// }
|
||||
// } else {
|
||||
// console.error('Error loading image');
|
||||
// }
|
||||
// loading[index] = false;
|
||||
};
|
||||
|
||||
const finishLoad = async () => {
|
||||
info("Loading image in web mode");
|
||||
const file = files.item(0);
|
||||
info("Got file:", file);
|
||||
if (file) {
|
||||
switch(file.type) {
|
||||
case "image/gif":
|
||||
info("TODO");
|
||||
break;
|
||||
default:
|
||||
const img = await loadImage(file);
|
||||
$frames[index] = img ?
|
||||
{
|
||||
kind:"still",
|
||||
value: img
|
||||
} : null
|
||||
}
|
||||
}
|
||||
|
||||
loading = false;
|
||||
};
|
||||
|
||||
const clearImage = () => {
|
||||
$frames[index] = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if mode === 'web'}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
bind:this={fileInput}
|
||||
class="hidden w-0 h-0"
|
||||
bind:files
|
||||
on:change={finishLoad}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="btn btn-xl w-24 md:w-32 variant-ghost-primary aspect-square cursor-pointer z-50"
|
||||
|
@ -76,15 +112,8 @@
|
|||
openImage();
|
||||
}
|
||||
}}
|
||||
on:mouseenter={() => (menuTimeout = setTimeout(() => (showMenu = true), 200))}
|
||||
on:mouseleave={() => {
|
||||
if (menuTimeout) {
|
||||
clearTimeout(menuTimeout);
|
||||
}
|
||||
showMenu = false;
|
||||
}}
|
||||
>
|
||||
{#if loading[index]}
|
||||
{#if loading}
|
||||
<ProgressRadial />
|
||||
{:else if src}
|
||||
<img {src} alt="Frame {{ index }}" />
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { frames, type FrameData } from "../store";
|
||||
import { onMount } from "svelte";
|
||||
import anime from "animejs";
|
||||
import type { Animator } from "../gifler";
|
||||
import { active, blink } from "$lib/state";
|
||||
import { frames, type FrameData } from '../store';
|
||||
import { onMount } from 'svelte';
|
||||
import anime from 'animejs';
|
||||
import type { Animator } from '$lib/gifler';
|
||||
import { active, blink } from '$lib/state';
|
||||
import { debug, info } from '$lib/logging';
|
||||
|
||||
type MaybeSrc = | { mode: "png"; data: ImageBitmap }
|
||||
| { mode: "gif"; data: Animator }
|
||||
| null;
|
||||
type MaybeSrc = { mode: 'png'; data: ImageBitmap } | { mode: 'gif'; data: Animator } | null;
|
||||
|
||||
$: bitMaps = [null, null, null, null] as MaybeSrc[];
|
||||
$: currFrame = 0;
|
||||
|
@ -15,15 +14,16 @@
|
|||
$: src = null as MaybeSrc;
|
||||
|
||||
$: {
|
||||
if (src?.mode === "gif" && src.data.running()) src.data.stop();
|
||||
if (src?.mode === 'gif' && src.data.running()) src.data.stop();
|
||||
src = bitMaps[currFrame] as MaybeSrc;
|
||||
if (src?.mode === "gif") src.data.reset().start();
|
||||
debug('canvas source', src);
|
||||
if (src?.mode === 'gif') src.data.reset().start();
|
||||
}
|
||||
|
||||
let pos = { x: 0, y: 0 };
|
||||
|
||||
$: {
|
||||
if ($active) {
|
||||
if (!$active) {
|
||||
currFrame = 0;
|
||||
} else {
|
||||
currFrame = 1;
|
||||
|
@ -42,10 +42,10 @@
|
|||
targets: pos,
|
||||
y: [
|
||||
{ value: 50, duration: 200 },
|
||||
{ value: 0, duration: 200 },
|
||||
{ value: 0, duration: 200 }
|
||||
],
|
||||
autoplay: true,
|
||||
easing: "easeOutCubic",
|
||||
easing: 'easeOutCubic'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -66,55 +66,61 @@
|
|||
const createBitmaps = async (f: Array<FrameData | null>): Promise<MaybeSrc[]> => {
|
||||
return await Promise.all(
|
||||
f.map(async (v) => {
|
||||
debug('creating bitmap for', v);
|
||||
if (!v) return null;
|
||||
return v.kind === "still"
|
||||
? {
|
||||
mode: "png",
|
||||
data: await createImageBitmap(await v.value.toBlob()),
|
||||
}
|
||||
: {
|
||||
mode: "gif",
|
||||
data: (
|
||||
await v.value.frames(
|
||||
"#png",
|
||||
(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
frame: {
|
||||
buffer: CanvasImageSource;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
) => {
|
||||
ctx.drawImage(
|
||||
frame.buffer,
|
||||
canvas.width / 2 - pos.x - frame.width / 2,
|
||||
canvas.height / 2 - pos.y - frame.height / 2
|
||||
);
|
||||
},
|
||||
false
|
||||
)
|
||||
).stop(),
|
||||
};
|
||||
if (v?.kind === 'still') {
|
||||
info('Creating bitmap for still image');
|
||||
|
||||
return {
|
||||
mode: 'png',
|
||||
data: await createImageBitmap(await v?.value.toBlob())
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
mode: 'gif',
|
||||
data: (
|
||||
await v?.value.frames(
|
||||
'#png',
|
||||
(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
frame: {
|
||||
buffer: CanvasImageSource;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
) => {
|
||||
ctx.drawImage(
|
||||
frame.buffer,
|
||||
canvas.width / 2 - pos.x - frame.width / 2,
|
||||
canvas.height / 2 - pos.y - frame.height / 2
|
||||
);
|
||||
},
|
||||
false
|
||||
)
|
||||
).stop()
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
bitMaps = await createBitmaps($frames);
|
||||
console.log("bitMaps: ", bitMaps);
|
||||
console.log('bitMaps: ', bitMaps);
|
||||
frames.subscribe(async (f) => {
|
||||
bitMaps.forEach((v) => (v && v.mode === "gif" ? v.data.stop() : null));
|
||||
debug('Updating image bitmaps');
|
||||
// make sure any loaded gifs are stopped or they'll keep drawing themselves
|
||||
bitMaps.forEach((v) => (v && v.mode === 'gif' ? v.data.stop() : null));
|
||||
bitMaps = await createBitmaps(f);
|
||||
});
|
||||
|
||||
|
||||
updateCanvasSize();
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
const update = async () => {
|
||||
try {
|
||||
if (src?.mode === "png") {
|
||||
if (src?.mode === 'png') {
|
||||
const img = src?.data;
|
||||
ctx?.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx?.drawImage(
|
||||
|
@ -122,10 +128,12 @@
|
|||
canvas.width / 2 - pos.x - img.width / 2,
|
||||
canvas.height / 2 - pos.y - img.height / 2
|
||||
);
|
||||
} else if (src?.mode === "gif" && !src.data.running()) {
|
||||
} else if (src?.mode === 'gif' && !src.data.running()) {
|
||||
src.data.start();
|
||||
}
|
||||
} catch {}
|
||||
} catch(e) {
|
||||
debug('Rendering error', e);
|
||||
}
|
||||
requestAnimationFrame(update);
|
||||
};
|
||||
|
||||
|
@ -135,12 +143,13 @@
|
|||
const updateCanvasSize = () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
info(`Set canvas size to ${canvas.width}, ${canvas.height}`);
|
||||
};
|
||||
|
||||
let canvas: HTMLCanvasElement;
|
||||
</script>
|
||||
|
||||
<canvas bind:this={canvas} id="png" />
|
||||
<canvas bind:this={canvas} id="png" />
|
||||
|
||||
<style lang="scss">
|
||||
canvas {
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
export const openImage = async () => {
|
||||
import { Image } from 'image-js';
|
||||
import { mode } from '$lib/store';
|
||||
|
||||
}
|
||||
export const loadImage = async (data: File | Blob): Promise<Image | null> => {
|
||||
if (mode === 'web') {
|
||||
return new Promise(async (resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
const raw = reader.result as ArrayBuffer | null;
|
||||
if (!raw) return null;
|
||||
let img = await Image.load(raw);
|
||||
if (img.width > 600 || img.height > 400){
|
||||
img = img.resize({
|
||||
width: 600
|
||||
})
|
||||
}
|
||||
resolve(
|
||||
img
|
||||
);
|
||||
};
|
||||
reader.readAsArrayBuffer(data);
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { mode } from "$lib/store"
|
||||
|
||||
export const info = <T extends String>(...args: T[]) => {
|
||||
export const info = (...args: any[]) => {
|
||||
if(mode === "web")
|
||||
console.log("%c[INFO]", "color: lightgreen", ":", ...args);
|
||||
}
|
||||
|
||||
export const debug = (...args: any[]) => {
|
||||
if(mode === "web")
|
||||
console.log("%c[DEBUG]", "color: yellow", ":", ...args);
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
if (deinitAudio) clearInterval(deinitAudio);
|
||||
});
|
||||
|
||||
$: bgColor = $transparent ? 'bg-pink-' : 'bg-surface-900';
|
||||
$: bgColor = $transparent ? 'bg-green-800' : 'bg-surface-900';
|
||||
</script>
|
||||
|
||||
<div class="{bgColor} w-screen h-screen" id="container">
|
||||
|
|
Loading…
Reference in New Issue