Skip to content

Commit 4ba1869

Browse files
authored
feat(frontend): EXIFフレーム機能 (#16725)
* wip * wip * Update ImageEffector.ts * Update image-label-renderer.ts * Update image-label-renderer.ts * wip * Update image-label-renderer.ts * wip * wip * wip * wip * wip * wip * wip * Update use-uploader.ts * Update watermark.ts * wip * wu * wip * Update image-frame-renderer.ts * wip * wip * Update image-frame-renderer.ts * Create ImageCompositor.ts * Update ImageCompositor.ts * wip * wip * Update ImageEffector.ts * wip * Update use-uploader.ts * wip * wip * wip * wip * Update fxs.ts * wip * wip * wip * Update CHANGELOG.md * wip * wip * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.vue * Update MkImageFrameEditorDialog.vue * Update use-uploader.ts * improve error handling * Update use-uploader.ts * 🎨 * wip * wip * lazy load * lazy load * wip * wip * wip
1 parent 26c8914 commit 4ba1869

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2831
-1179
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
- Enhance: DockerのNode.jsが24.10.0に更新されました
66

77
### Client
8+
- Feat: 画像にメタデータを含むフレームをつけられる機能
9+
- Enhance: プリセットを作成しなくても画像にウォーターマークを付与できるように
810
- Enhance: 管理しているチャンネルの見分けがつきやすくなるように
911
- Enhance: プロフィールへのリンクをユーザーポップアップのアバターに追加
1012
- Enhance: ユーザーのノート、フォロー、フォロワーページへのリンクをユーザーポップアップに追加

locales/index.d.ts

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5625,6 +5625,172 @@ export interface Locale extends ILocale {
56255625
* あなたは管理者です
56265626
*/
56275627
"youAreAdmin": string;
5628+
/**
5629+
* フレーム
5630+
*/
5631+
"frame": string;
5632+
/**
5633+
* プリセット
5634+
*/
5635+
"presets": string;
5636+
/**
5637+
* ゼロ埋め
5638+
*/
5639+
"zeroPadding": string;
5640+
"_imageEditing": {
5641+
"_vars": {
5642+
/**
5643+
* ファイルのキャプション
5644+
*/
5645+
"caption": string;
5646+
/**
5647+
* ファイル名
5648+
*/
5649+
"filename": string;
5650+
/**
5651+
* 拡張子無しファイル名
5652+
*/
5653+
"filename_without_ext": string;
5654+
/**
5655+
* 撮影年
5656+
*/
5657+
"year": string;
5658+
/**
5659+
* 撮影月
5660+
*/
5661+
"month": string;
5662+
/**
5663+
* 撮影日
5664+
*/
5665+
"day": string;
5666+
/**
5667+
* 撮影した時刻(時)
5668+
*/
5669+
"hour": string;
5670+
/**
5671+
* 撮影した時刻(分)
5672+
*/
5673+
"minute": string;
5674+
/**
5675+
* 撮影した時刻(秒)
5676+
*/
5677+
"second": string;
5678+
/**
5679+
* カメラ名
5680+
*/
5681+
"camera_model": string;
5682+
/**
5683+
* レンズ名
5684+
*/
5685+
"camera_lens_model": string;
5686+
/**
5687+
* 焦点距離
5688+
*/
5689+
"camera_mm": string;
5690+
/**
5691+
* 焦点距離(35mm判換算)
5692+
*/
5693+
"camera_mm_35": string;
5694+
/**
5695+
* 絞り
5696+
*/
5697+
"camera_f": string;
5698+
/**
5699+
* シャッタースピード
5700+
*/
5701+
"camera_s": string;
5702+
/**
5703+
* ISO感度
5704+
*/
5705+
"camera_iso": string;
5706+
/**
5707+
* 緯度
5708+
*/
5709+
"gps_lat": string;
5710+
/**
5711+
* 経度
5712+
*/
5713+
"gps_long": string;
5714+
};
5715+
};
5716+
"_imageFrameEditor": {
5717+
/**
5718+
* フレームの編集
5719+
*/
5720+
"title": string;
5721+
/**
5722+
* 画像にフレームやメタデータを含んだラベルを追加して装飾できます。
5723+
*/
5724+
"tip": string;
5725+
/**
5726+
* ヘッダー
5727+
*/
5728+
"header": string;
5729+
/**
5730+
* フッター
5731+
*/
5732+
"footer": string;
5733+
/**
5734+
* フチの幅
5735+
*/
5736+
"borderThickness": string;
5737+
/**
5738+
* ラベルの幅
5739+
*/
5740+
"labelThickness": string;
5741+
/**
5742+
* ラベルのスケール
5743+
*/
5744+
"labelScale": string;
5745+
/**
5746+
* 中央揃え
5747+
*/
5748+
"centered": string;
5749+
/**
5750+
* キャプション(大)
5751+
*/
5752+
"captionMain": string;
5753+
/**
5754+
* キャプション(小)
5755+
*/
5756+
"captionSub": string;
5757+
/**
5758+
* 利用可能な変数
5759+
*/
5760+
"availableVariables": string;
5761+
/**
5762+
* 二次元コード
5763+
*/
5764+
"withQrCode": string;
5765+
/**
5766+
* 背景色
5767+
*/
5768+
"backgroundColor": string;
5769+
/**
5770+
* 文字色
5771+
*/
5772+
"textColor": string;
5773+
/**
5774+
* フォント
5775+
*/
5776+
"font": string;
5777+
/**
5778+
* セリフ
5779+
*/
5780+
"fontSerif": string;
5781+
/**
5782+
* サンセリフ
5783+
*/
5784+
"fontSansSerif": string;
5785+
/**
5786+
* 保存せずに終了しますか?
5787+
*/
5788+
"quitWithoutSaveConfirm": string;
5789+
/**
5790+
* 画像の読み込みに失敗しました
5791+
*/
5792+
"failedToLoadImage": string;
5793+
};
56285794
"_compression": {
56295795
"_quality": {
56305796
/**
@@ -12354,7 +12520,7 @@ export interface Locale extends ILocale {
1235412520
"defaultPreset": string;
1235512521
"_watermarkEditor": {
1235612522
/**
12357-
* 画像にクレジット情報などのウォーターマークを追加することができます
12523+
* 画像にクレジット情報などのウォーターマークを追加できます
1235812524
*/
1235912525
"tip": string;
1236012526
/**
@@ -12469,6 +12635,10 @@ export interface Locale extends ILocale {
1246912635
* 空欄にするとアカウントのURLになります
1247012636
*/
1247112637
"leaveBlankToAccountUrl": string;
12638+
/**
12639+
* 画像の読み込みに失敗しました
12640+
*/
12641+
"failedToLoadImage": string;
1247212642
};
1247312643
"_imageEffector": {
1247412644
/**
@@ -12487,6 +12657,10 @@ export interface Locale extends ILocale {
1248712657
* 設定項目はありません
1248812658
*/
1248912659
"nothingToConfigure": string;
12660+
/**
12661+
* 画像の読み込みに失敗しました
12662+
*/
12663+
"failedToLoadImage": string;
1249012664
"_fxs": {
1249112665
/**
1249212666
* 色収差

locales/ja-JP.yml

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,51 @@ widgets: "ウィジェット"
14011401
deviceInfo: "デバイス情報"
14021402
deviceInfoDescription: "技術的なお問い合わせの際に、以下の情報を併記すると問題の解決に役立つことがあります。"
14031403
youAreAdmin: "あなたは管理者です"
1404+
frame: "フレーム"
1405+
presets: "プリセット"
1406+
zeroPadding: "ゼロ埋め"
1407+
1408+
_imageEditing:
1409+
_vars:
1410+
caption: "ファイルのキャプション"
1411+
filename: "ファイル名"
1412+
filename_without_ext: "拡張子無しファイル名"
1413+
year: "撮影年"
1414+
month: "撮影月"
1415+
day: "撮影日"
1416+
hour: "撮影した時刻(時)"
1417+
minute: "撮影した時刻(分)"
1418+
second: "撮影した時刻(秒)"
1419+
camera_model: "カメラ名"
1420+
camera_lens_model: "レンズ名"
1421+
camera_mm: "焦点距離"
1422+
camera_mm_35: "焦点距離(35mm判換算)"
1423+
camera_f: "絞り"
1424+
camera_s: "シャッタースピード"
1425+
camera_iso: "ISO感度"
1426+
gps_lat: "緯度"
1427+
gps_long: "経度"
1428+
1429+
_imageFrameEditor:
1430+
title: "フレームの編集"
1431+
tip: "画像にフレームやメタデータを含んだラベルを追加して装飾できます。"
1432+
header: "ヘッダー"
1433+
footer: "フッター"
1434+
borderThickness: "フチの幅"
1435+
labelThickness: "ラベルの幅"
1436+
labelScale: "ラベルのスケール"
1437+
centered: "中央揃え"
1438+
captionMain: "キャプション(大)"
1439+
captionSub: "キャプション(小)"
1440+
availableVariables: "利用可能な変数"
1441+
withQrCode: "二次元コード"
1442+
backgroundColor: "背景色"
1443+
textColor: "文字色"
1444+
font: "フォント"
1445+
fontSerif: "セリフ"
1446+
fontSansSerif: "サンセリフ"
1447+
quitWithoutSaveConfirm: "保存せずに終了しますか?"
1448+
failedToLoadImage: "画像の読み込みに失敗しました"
14041449

14051450
_compression:
14061451
_quality:
@@ -3307,7 +3352,7 @@ _userLists:
33073352
watermark: "ウォーターマーク"
33083353
defaultPreset: "デフォルトのプリセット"
33093354
_watermarkEditor:
3310-
tip: "画像にクレジット情報などのウォーターマークを追加することができます"
3355+
tip: "画像にクレジット情報などのウォーターマークを追加できます"
33113356
quitWithoutSaveConfirm: "保存せずに終了しますか?"
33123357
driveFileTypeWarn: "このファイルは対応していません"
33133358
driveFileTypeWarnDescription: "画像ファイルを選択してください"
@@ -3336,12 +3381,14 @@ _watermarkEditor:
33363381
polkadotSubDotRadius: "サブドットの大きさ"
33373382
polkadotSubDotDivisions: "サブドットの数"
33383383
leaveBlankToAccountUrl: "空欄にするとアカウントのURLになります"
3384+
failedToLoadImage: "画像の読み込みに失敗しました"
33393385

33403386
_imageEffector:
33413387
title: "エフェクト"
33423388
addEffect: "エフェクトを追加"
33433389
discardChangesConfirm: "変更を破棄して終了しますか?"
33443390
nothingToConfigure: "設定項目はありません"
3391+
failedToLoadImage: "画像の読み込みに失敗しました"
33453392

33463393
_fxs:
33473394
chromaticAberration: "色収差"

packages/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"estree-walker": "3.0.3",
4949
"eventemitter3": "5.0.1",
5050
"execa": "9.6.0",
51+
"exifreader": "4.32.0",
5152
"frontend-shared": "workspace:*",
5253
"icons-subsetter": "workspace:*",
5354
"idb-keyval": "6.2.2",

packages/frontend/src/components/MkEmbedCodeGenDialog.vue

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
2424
:leaveToClass="$style.transition_x_leaveTo"
2525
>
2626
<div v-if="phase === 'input'" key="input" :class="$style.embedCodeGenInputRoot">
27-
<div
28-
:class="$style.embedCodeGenPreviewRoot"
29-
>
27+
<div :class="[$style.embedCodeGenPreviewRoot, prefer.s.animation ? $style.animatedBg : null]">
3028
<MkLoading v-if="iframeLoading" :class="$style.embedCodeGenPreviewSpinner"/>
3129
<div :class="$style.embedCodeGenPreviewWrapper">
3230
<div class="_acrylic" :class="$style.embedCodeGenPreviewTitle">{{ i18n.ts.preview }}</div>
@@ -91,20 +89,18 @@ import { url } from '@@/js/config.js';
9189
import { embedRouteWithScrollbar } from '@@/js/embed-page.js';
9290
import type { EmbeddableEntity, EmbedParams } from '@@/js/embed-page.js';
9391
import MkModalWindow from '@/components/MkModalWindow.vue';
94-
9592
import MkInput from '@/components/MkInput.vue';
9693
import MkSelect from '@/components/MkSelect.vue';
9794
import MkSwitch from '@/components/MkSwitch.vue';
9895
import MkButton from '@/components/MkButton.vue';
99-
10096
import MkCode from '@/components/MkCode.vue';
10197
import MkInfo from '@/components/MkInfo.vue';
102-
10398
import * as os from '@/os.js';
10499
import { i18n } from '@/i18n.js';
105100
import { useMkSelect } from '@/composables/use-mkselect.js';
106101
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
107102
import { normalizeEmbedParams, getEmbedCode } from '@/utility/get-embed-code.js';
103+
import { prefer } from '@/preferences.js';
108104
109105
const emit = defineEmits<{
110106
(ev: 'ok'): void;
@@ -314,10 +310,19 @@ onUnmounted(() => {
314310
315311
.embedCodeGenPreviewRoot {
316312
position: relative;
317-
background-color: var(--MI_THEME-bg);
318-
background-size: auto auto;
319-
background-image: repeating-linear-gradient(135deg, transparent, transparent 6px, var(--MI_THEME-panel) 6px, var(--MI_THEME-panel) 12px);
320313
cursor: not-allowed;
314+
background-color: var(--MI_THEME-bg);
315+
background-image: linear-gradient(135deg, transparent 30%, var(--MI_THEME-panel) 30%, var(--MI_THEME-panel) 50%, transparent 50%, transparent 80%, var(--MI_THEME-panel) 80%, var(--MI_THEME-panel) 100%);
316+
background-size: 20px 20px;
317+
}
318+
319+
.animatedBg {
320+
animation: bg 1.2s linear infinite;
321+
}
322+
323+
@keyframes bg {
324+
0% { background-position: 0 0; }
325+
100% { background-position: -20px -20px; }
321326
}
322327
323328
.embedCodeGenPreviewWrapper {

packages/frontend/src/components/MkImageEffectorDialog.Layer.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
55

66
<template>
77
<MkFolder :defaultOpen="true" :canPage="false">
8-
<template #label>{{ fx.name }}</template>
8+
<template #label>{{ fx.uiDefinition.name }}</template>
99
<template #footer>
1010
<div class="_buttons">
1111
<MkButton iconOnly @click="emit('del')"><i class="ti ti-trash"></i></MkButton>
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
1414
</div>
1515
</template>
1616

17-
<MkImageEffectorFxForm v-model="layer.params" :paramDefs="fx.params" />
17+
<MkImageEffectorFxForm v-model="layer.params" :paramDefs="fx.uiDefinition.params"/>
1818
</MkFolder>
1919
</template>
2020

@@ -26,14 +26,14 @@ import MkImageEffectorFxForm from '@/components/MkImageEffectorFxForm.vue';
2626
import { FXS } from '@/utility/image-effector/fxs.js';
2727
2828
const layer = defineModel<ImageEffectorLayer>('layer', { required: true });
29-
const fx = FXS.find((fx) => fx.id === layer.value.fxId);
29+
const fx = FXS[layer.value.fxId];
3030
if (fx == null) {
3131
throw new Error(`Unrecognized effect: ${layer.value.fxId}`);
3232
}
3333
3434
const emit = defineEmits<{
35-
(e: 'del'): void;
36-
(e: 'swapUp'): void;
37-
(e: 'swapDown'): void;
35+
(ev: 'del'): void;
36+
(ev: 'swapUp'): void;
37+
(ev: 'swapDown'): void;
3838
}>();
3939
</script>

0 commit comments

Comments
 (0)