Files
Curse/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
かっこかり b43dfa260b fix/refactor(frontend): 画像編集機能の修正・型強化 (#16156)
* enhance: refine uploadFile

* fix: missing locale

* refactor: harden types

* refactor: シェーダーファイルをlazy-loadingできるように

* fix(frontend): omit console.log in production environment

* fix: glslのバージョン表記は最初の行になければならない

* fix: シェーダーの読み込みが完了してからレンダリングを行うように

* fix merge failure

* fix: ウォーターマークのプリセットがない場合にdividerが2重に表示される問題を修正

* fix: アップローダーダイアログの機能設定でウォーターマークが無効な場合でもデフォルトのプリセットが適用されてしまう問題を修正

* fix lint

* Revert "fix: シェーダーの読み込みが完了してからレンダリングを行うように"

This reverts commit e06f37a7d4.

* Revert "fix: glslのバージョン表記は最初の行になければならない"

This reverts commit afcc37d886.

* Revert "refactor: シェーダーファイルをlazy-loadingできるように"

This reverts commit a1ab2fa38c.

* fix: ウォーターマークのFX定義を分ける

* Update packages/frontend/src/components/MkWatermarkEditorDialog.vue

* Update packages/frontend/src/components/MkWatermarkEditorDialog.vue

* Update packages/frontend/src/components/MkWatermarkEditorDialog.vue

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-06-04 16:22:09 +09:00

326 lines
7.4 KiB
Vue

<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root" class="_gaps">
<template v-if="layer.type === 'text'">
<MkInput v-model="layer.text">
<template #label>{{ i18n.ts._watermarkEditor.text }}</template>
</MkInput>
<FormSlot>
<template #label>{{ i18n.ts._watermarkEditor.position }}</template>
<MkPositionSelector
v-model:x="layer.align.x"
v-model:y="layer.align.y"
></MkPositionSelector>
</FormSlot>
<MkRange
v-model="layer.scale"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.scale }}</template>
</MkRange>
<MkRange
v-model="layer.angle"
:min="-1"
:max="1"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.angle }}</template>
</MkRange>
<MkRange
v-model="layer.opacity"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.opacity }}</template>
</MkRange>
<MkSwitch v-model="layer.repeat">
<template #label>{{ i18n.ts._watermarkEditor.repeat }}</template>
</MkSwitch>
</template>
<template v-else-if="layer.type === 'image'">
<MkButton inline rounded primary @click="chooseFile">{{ i18n.ts.selectFile }}</MkButton>
<FormSlot>
<template #label>{{ i18n.ts._watermarkEditor.position }}</template>
<MkPositionSelector
v-model:x="layer.align.x"
v-model:y="layer.align.y"
></MkPositionSelector>
</FormSlot>
<MkRange
v-model="layer.scale"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.scale }}</template>
</MkRange>
<MkRange
v-model="layer.angle"
:min="-1"
:max="1"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.angle }}</template>
</MkRange>
<MkRange
v-model="layer.opacity"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.opacity }}</template>
</MkRange>
<MkSwitch v-model="layer.repeat">
<template #label>{{ i18n.ts._watermarkEditor.repeat }}</template>
</MkSwitch>
<MkSwitch v-model="layer.cover">
<template #label>{{ i18n.ts._watermarkEditor.cover }}</template>
</MkSwitch>
</template>
<template v-else-if="layer.type === 'stripe'">
<MkRange
v-model="layer.frequency"
:min="1"
:max="30"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.stripeFrequency }}</template>
</MkRange>
<MkRange
v-model="layer.threshold"
:min="0"
:max="1"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.stripeWidth }}</template>
</MkRange>
<MkRange
v-model="layer.angle"
:min="-1"
:max="1"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.angle }}</template>
</MkRange>
<MkRange
v-model="layer.opacity"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.opacity }}</template>
</MkRange>
</template>
<template v-else-if="layer.type === 'polkadot'">
<MkRange
v-model="layer.angle"
:min="-1"
:max="1"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.angle }}</template>
</MkRange>
<MkRange
v-model="layer.scale"
:min="0"
:max="10"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.scale }}</template>
</MkRange>
<MkRange
v-model="layer.majorRadius"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.polkadotMainDotRadius }}</template>
</MkRange>
<MkRange
v-model="layer.majorOpacity"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.polkadotMainDotOpacity }}</template>
</MkRange>
<MkRange
v-model="layer.minorDivisions"
:min="0"
:max="16"
:step="1"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.polkadotSubDotDivisions }}</template>
</MkRange>
<MkRange
v-model="layer.minorRadius"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.polkadotSubDotRadius }}</template>
</MkRange>
<MkRange
v-model="layer.minorOpacity"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.polkadotSubDotOpacity }}</template>
</MkRange>
</template>
<template v-else-if="layer.type === 'checker'">
<MkRange
v-model="layer.angle"
:min="-1"
:max="1"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.angle }}</template>
</MkRange>
<MkRange
v-model="layer.scale"
:min="0"
:max="10"
:step="0.01"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.scale }}</template>
</MkRange>
<MkRange
v-model="layer.opacity"
:min="0"
:max="1"
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
>
<template #label>{{ i18n.ts._watermarkEditor.opacity }}</template>
</MkRange>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import * as Misskey from 'misskey-js';
import type { WatermarkPreset } from '@/utility/watermark.js';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkRange from '@/components/MkRange.vue';
import FormSlot from '@/components/form/slot.vue';
import MkPositionSelector from '@/components/MkPositionSelector.vue';
import * as os from '@/os.js';
import { selectFile } from '@/utility/drive.js';
import { misskeyApi } from '@/utility/misskey-api.js';
const layer = defineModel<WatermarkPreset['layers'][number]>('layer', { required: true });
const driveFile = ref<Misskey.entities.DriveFile | null>(null);
const driveFileError = ref(false);
onMounted(async () => {
if (layer.value.type === 'image' && layer.value.imageId != null) {
await misskeyApi('drive/files/show', {
fileId: layer.value.imageId,
}).then((res) => {
driveFile.value = res;
}).catch((err) => {
driveFileError.value = true;
});
}
});
function chooseFile(ev: MouseEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
label: i18n.ts.selectFile,
features: {
watermark: false,
},
}).then((file) => {
if (layer.value.type !== 'image') return;
if (!file.type.startsWith('image')) {
os.alert({
type: 'warning',
title: i18n.ts._watermarkEditor.driveFileTypeWarn,
text: i18n.ts._watermarkEditor.driveFileTypeWarnDescription,
});
return;
}
layer.value.imageId = file.id;
layer.value.imageUrl = file.url;
driveFileError.value = false;
});
}
</script>
<style module>
.root {
}
</style>