Skip to content

Commit

Permalink
feat: Enhance collage (#249)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhea-so committed May 3, 2024
2 parents eeaf623 + 36c9b51 commit 0cf53cf
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 91 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.4.74",
"version": "0.4.75",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
3 changes: 2 additions & 1 deletion web/src/locales/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@

"lab.description": "The experimental function may appear like the wind and disappear without a sound.",
"lab.collage": "Collage",
"lab.collage-description": "Create a collage with multiple photos."
"lab.collage-description": "Create a collage with multiple photos.",
"lab.collage-options": "You can set the top, bottom, left, and right margins and the space between photos."
}
3 changes: 2 additions & 1 deletion web/src/locales/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@

"lab.description": "実験室機能は風のように現れ、音もなく消えることがあります。",
"lab.collage": "コラージュ",
"lab.collage-description": "写真をコラージュします。"
"lab.collage-description": "写真をコラージュします。",
"lab.collage-options": "上下左右の余白と写真間の余白を設定できます。"
}
3 changes: 2 additions & 1 deletion web/src/locales/translations/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,6 @@

"lab.description": "실험실 기능은 바람처럼 나타났다 소리없이 사라질 수 있습니다.",
"lab.collage": "콜라주",
"lab.collage-description": "여러 장의 사진을 한 장으로 합치는 기능입니다."
"lab.collage-description": "여러 장의 사진을 한 장으로 합치는 기능입니다.",
"lab.collage-options": "상하좌우 여백과 사진 간 여백을 설정할 수 있습니다."
}
37 changes: 0 additions & 37 deletions web/src/pages/lab/collage/components/download-one-photo.button.tsx

This file was deleted.

70 changes: 70 additions & 0 deletions web/src/pages/lab/collage/components/download-photo.button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Icon, ListButton } from 'konsta/react';
import { IoDownloadOutline } from 'react-icons/io5';
import { useStore } from '../store';
import convert from '../../../../core/drawing/convert';
import free from '../../../../core/drawing/free';
import download from '../../../../core/file-system/download';
import * as Root from '../../../../store';
import { useTranslation } from 'react-i18next';
import SIMPLE_FUNC from '../theme/SIMPLE';
import { Capacitor } from '@capacitor/core';
import compress from '../../../../core/file-system/compress';
import Photo from '../../../../core/photo';

const DownloadPhotoButton = () => {
const { t } = useTranslation();
const { exportToJpeg, quality } = Root.useStore();
const { ratio, numberOfRow, numberOfColumn, photos, setLoading, paddingTop, paddingBottom, paddingLeft, paddingRight, marginEach } = useStore();

return (
<ListButton
onClick={async () => {
if (photos.length === 0) return;
setLoading(true);
await new Promise((resolve) => setTimeout(resolve, 100));

const groups: Photo[][] = [];
for (let i = 0; i < photos.length; i += numberOfRow * numberOfColumn) {
groups.push(photos.slice(i, i + numberOfRow * numberOfColumn));
}

if (Capacitor.isNativePlatform()) {
for (const group of groups) {
const canvas = SIMPLE_FUNC(group, { ratio, numberOfRow, numberOfColumn, paddingTop, paddingBottom, paddingLeft, paddingRight, marginEach });
const filename = exportToJpeg ? 'collage.jpg' : 'collage.webp';
const data = await convert(canvas, { type: exportToJpeg ? 'image/jpeg' : 'image/webp', quality });
free(canvas);
await download(filename, data);
}
} else if (groups.length === 1) {
const canvas = SIMPLE_FUNC(groups[0], { ratio, numberOfRow, numberOfColumn, paddingTop, paddingBottom, paddingLeft, paddingRight, marginEach });
const filename = exportToJpeg ? 'collage.jpg' : 'collage.webp';
const data = await convert(canvas, { type: exportToJpeg ? 'image/jpeg' : 'image/webp', quality });
free(canvas);
await download(filename, data);
} else {
const files: { filename: string; data: string }[] = [];
await Promise.all(
groups.map(async (group, index) => {
const canvas = SIMPLE_FUNC(group, { ratio, numberOfRow, numberOfColumn, paddingTop, paddingBottom, paddingLeft, paddingRight, marginEach });
const filename = exportToJpeg ? `collage-${index}.jpg` : `collage-${index}.webp`;
const data = await convert(canvas, { type: exportToJpeg ? 'image/jpeg' : 'image/webp', quality });
free(canvas);
files.push({ filename, data });
})
);
const zip = await compress(files);
await download('images.zip', zip);
}

setLoading(false);
}}
>
<Icon ios={<IoDownloadOutline className="w-5 h-5" />} />
<div style={{ width: 4 }} />
{t('root.download')} ({Math.ceil(photos.length / (numberOfRow * numberOfColumn))})
</ListButton>
);
};

export default DownloadPhotoButton;
21 changes: 21 additions & 0 deletions web/src/pages/lab/collage/components/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Preloader } from 'konsta/react';
import { useTranslation } from 'react-i18next';
import { useStore } from '../store';

const Loading = () => {
const { t } = useTranslation();
const { loading } = useStore();

if (!loading) return null;

return (
<div className="fixed top-0 left-0 z-50 w-screen h-screen bg-gray-900 bg-opacity-50 flex justify-center items-center">
<div className="flex flex-col items-center">
<Preloader className="k-color-brand-white" />
<span className="ml-2 text-white text-sm">{t('root.processing')}</span>
</div>
</div>
);
};

export default Loading;
47 changes: 0 additions & 47 deletions web/src/pages/lab/collage/components/theme.ts

This file was deleted.

43 changes: 40 additions & 3 deletions web/src/pages/lab/collage/main.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import { BlockTitle, BlockHeader, List } from 'konsta/react';
import { BlockTitle, BlockHeader, List, ListInput, Block } from 'konsta/react';
import { useTranslation } from 'react-i18next';
import AddPhotoButton from './components/add-photo.button';
import AddedPhotoListItem from './components/added-photo.list-item';
import DownloadOnePhotoButton from './components/download-one-photo.button';
import DownloadPhotoButton from './components/download-photo.button';
import Loading from './components/loading';
import { useStore } from './store';

const Collage = () => {
const { t } = useTranslation();
const {
ratio,
numberOfRow,
numberOfColumn,
paddingTop,
paddingBottom,
paddingLeft,
paddingRight,
marginEach,
setRatio,
setNumberOfRow,
setNumberOfColumn,
setPaddingTop,
setPaddingBottom,
setPaddingLeft,
setPaddingRight,
setMarginEach,
} = useStore();

return (
<>
Expand All @@ -15,8 +35,25 @@ const Collage = () => {
<List strong inset>
<AddedPhotoListItem />
<AddPhotoButton />
<DownloadOnePhotoButton />
<DownloadPhotoButton />
</List>

<Block>
<p>{t('lab.collage-options')}</p>
</Block>

<List strong inset>
<ListInput title="RATIO" info="width:height" value={ratio} onChange={(e) => setRatio(e.target.value)} />
<ListInput title="NUMBER_OF_ROW" info="count" value={numberOfRow} onChange={(e) => setNumberOfRow(Number(e.target.value))} />
<ListInput title="NUMBER_OF_COLUMN" info="count" value={numberOfColumn} onChange={(e) => setNumberOfColumn(Number(e.target.value))} />
<ListInput title="PADDING_TOP" info="px" value={paddingTop} onChange={(e) => setPaddingTop(Number(e.target.value))} />
<ListInput title="PADDING_BOTTOM" info="px" value={paddingBottom} onChange={(e) => setPaddingBottom(Number(e.target.value))} />
<ListInput title="PADDING_LEFT" info="px" value={paddingLeft} onChange={(e) => setPaddingLeft(Number(e.target.value))} />
<ListInput title="PADDING_RIGHT" info="px" value={paddingRight} onChange={(e) => setPaddingRight(Number(e.target.value))} />
<ListInput title="MARGIN_EACH" info="px" value={marginEach} onChange={(e) => setMarginEach(Number(e.target.value))} />
</List>

<Loading />
</>
);
};
Expand Down
72 changes: 72 additions & 0 deletions web/src/pages/lab/collage/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ type Store = {

loading: boolean;
setLoading: (loading: boolean) => void;

ratio: string;
setRatio: (ratio: string) => void;

numberOfRow: number;
setNumberOfRow: (numberOfRow: number) => void;

numberOfColumn: number;
setNumberOfColumn: (numberOfColumn: number) => void;

paddingTop: number;
setPaddingTop: (paddingTop: number) => void;

paddingBottom: number;
setPaddingBottom: (paddingBottom: number) => void;

paddingLeft: number;
setPaddingLeft: (paddingLeft: number) => void;

paddingRight: number;
setPaddingRight: (paddingRight: number) => void;

marginEach: number;
setMarginEach: (marginEach: number) => void;
};

export const useStore = create<Store>((set) => ({
Expand All @@ -15,4 +39,52 @@ export const useStore = create<Store>((set) => ({

loading: false,
setLoading: (loading) => set({ loading }),

ratio: localStorage.getItem('lab:ratio') || '4:5',
setRatio: (ratio) => {
localStorage.setItem('lab:ratio', ratio);
set({ ratio });
},

numberOfRow: localStorage.getItem('lab:numberOfRow') ? Number(localStorage.getItem('lab:numberOfRow')) : 2,
setNumberOfRow: (numberOfRow) => {
localStorage.setItem('lab:numberOfRow', String(numberOfRow));
set({ numberOfRow });
},

numberOfColumn: localStorage.getItem('lab:numberOfColumn') ? Number(localStorage.getItem('lab:numberOfColumn')) : 2,
setNumberOfColumn: (numberOfColumn) => {
localStorage.setItem('lab:numberOfColumn', String(numberOfColumn));
set({ numberOfColumn });
},

paddingTop: localStorage.getItem('lab:paddingTop') ? Number(localStorage.getItem('lab:paddingTop')) : 30,
setPaddingTop: (paddingTop) => {
localStorage.setItem('lab:paddingTop', String(paddingTop));
set({ paddingTop });
},

paddingBottom: localStorage.getItem('lab:paddingBottom') ? Number(localStorage.getItem('lab:paddingBottom')) : 30,
setPaddingBottom: (paddingBottom) => {
localStorage.setItem('lab:paddingBottom', String(paddingBottom));
set({ paddingBottom });
},

paddingLeft: localStorage.getItem('lab:paddingLeft') ? Number(localStorage.getItem('lab:paddingLeft')) : 30,
setPaddingLeft: (paddingLeft) => {
localStorage.setItem('lab:paddingLeft', String(paddingLeft));
set({ paddingLeft });
},

paddingRight: localStorage.getItem('lab:paddingRight') ? Number(localStorage.getItem('lab:paddingRight')) : 30,
setPaddingRight: (paddingRight) => {
localStorage.setItem('lab:paddingRight', String(paddingRight));
set({ paddingRight });
},

marginEach: localStorage.getItem('lab:marginEach') ? Number(localStorage.getItem('lab:marginEach')) : 10,
setMarginEach: (marginEach) => {
localStorage.setItem('lab:marginEach', String(marginEach));
set({ marginEach });
},
}));
Loading

0 comments on commit 0cf53cf

Please sign in to comment.