Use some icons
This commit is contained in:
@@ -11,9 +11,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gif.js": "^0.2.0",
|
"gif.js": "^0.2.0",
|
||||||
|
"unplugin-icons": "^0.18.5",
|
||||||
"vue": "^3.3.11"
|
"vue": "^3.3.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@iconify-json/octicon": "^1.1.52",
|
||||||
"@types/gif.js": "^0.2.5",
|
"@types/gif.js": "^0.2.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as GIF from 'gif.js';
|
import * as GIF from 'gif.js';
|
||||||
|
import { downloadBlob } from '../download.ts';
|
||||||
|
import IconPlus from '~icons/octicon/feed-plus-16';
|
||||||
|
import IconPlay from '~icons/octicon/play-16';
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
selected: [image: HTMLImageElement]
|
selected: [image: HTMLImageElement]
|
||||||
@@ -31,12 +34,7 @@ function onAnimateClick(): void {
|
|||||||
});
|
});
|
||||||
images.value.forEach((img) => gif.addFrame(img, { delay: 200 }));
|
images.value.forEach((img) => gif.addFrame(img, { delay: 200 }));
|
||||||
gif.on('finished', (blob: Blob) => {
|
gif.on('finished', (blob: Blob) => {
|
||||||
const url = URL.createObjectURL(blob);
|
downloadBlob(blob, 'animation.gif')
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = 'animation.gif';
|
|
||||||
a.click();
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
});
|
});
|
||||||
gif.render();
|
gif.render();
|
||||||
}
|
}
|
||||||
@@ -49,12 +47,16 @@ defineExpose({
|
|||||||
<template>
|
<template>
|
||||||
<div class="file-column">
|
<div class="file-column">
|
||||||
<input id="file-upload" type="file" @change="onFileChange">
|
<input id="file-upload" type="file" @change="onFileChange">
|
||||||
<label for="file-upload" class="button">Add</label>
|
<label for="file-upload" class="button">
|
||||||
|
Add
|
||||||
|
<icon-plus />
|
||||||
|
</label>
|
||||||
<div v-for="(img, idx) in images" :key="idx" class="image-file">
|
<div v-for="(img, idx) in images" :key="idx" class="image-file">
|
||||||
<img :src="img.src" width="64" height="64" alt="" draggable="true" @click="$emit('selected', img)" @dragstart="onDragStart($event, idx)" />
|
<img :src="img.src" width="64" height="64" alt="" draggable="true" @click="$emit('selected', img)" @dragstart="onDragStart($event, idx)" />
|
||||||
</div>
|
</div>
|
||||||
<button v-if="images.length" @click="onAnimateClick">
|
<button v-if="images.length" @click="onAnimateClick">
|
||||||
Animate
|
Animate
|
||||||
|
<icon-play />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -4,6 +4,8 @@ import { blackAndWhite, grayScale } from '../processing/gray-scale.ts';
|
|||||||
import ImageCollection from './ImageCollection.vue';
|
import ImageCollection from './ImageCollection.vue';
|
||||||
import { mergeImages } from '../processing/merge.ts';
|
import { mergeImages } from '../processing/merge.ts';
|
||||||
import { drawOffscreen } from '../processing/offscreen.ts';
|
import { drawOffscreen } from '../processing/offscreen.ts';
|
||||||
|
import { download } from '../download.ts';
|
||||||
|
import IconDownload from '~icons/octicon/download-16';
|
||||||
|
|
||||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||||
const collection = ref<typeof ImageCollection>();
|
const collection = ref<typeof ImageCollection>();
|
||||||
@@ -102,6 +104,10 @@ function onDrop(event: DragEvent) {
|
|||||||
function onDragLeave() {
|
function onDragLeave() {
|
||||||
overlay.value = null;
|
overlay.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadImage() {
|
||||||
|
download(canvas.value!.toDataURL('image/png'), 'image.png');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -115,6 +121,9 @@ function onDragLeave() {
|
|||||||
<button @click="grayScaleApply">
|
<button @click="grayScaleApply">
|
||||||
Gray-scale
|
Gray-scale
|
||||||
</button>
|
</button>
|
||||||
|
<button @click="downloadImage">
|
||||||
|
Download <icon-download />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<input v-model="currentHeight" type="range" min="5" max="200" value="100" orient="vertical" @change="onChangeSlideHeight">
|
<input v-model="currentHeight" type="range" min="5" max="200" value="100" orient="vertical" @change="onChangeSlideHeight">
|
||||||
|
13
src/download.ts
Normal file
13
src/download.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export function download(value: string, filename: string): void {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = value;
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function downloadBlob(blob: Blob, filename: string): void {
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
download(url, filename);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
@@ -79,7 +79,7 @@ button:focus-visible,
|
|||||||
a:hover {
|
a:hover {
|
||||||
color: #747bff;
|
color: #747bff;
|
||||||
}
|
}
|
||||||
button {
|
button, .button {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,3 +108,7 @@ button:focus-visible,
|
|||||||
height: 50%;
|
height: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"types": ["vite/client", "unplugin-icons/types/vue"],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import Icons from 'unplugin-icons/vite'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue(), Icons({ compiler: 'vue3' })],
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user