summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-06-03 19:18:29 +0900
committerGitHub <noreply@github.com>2025-06-03 19:18:29 +0900
commitcd9322a8243b12632db2dd9a29a702d7531a5aa0 (patch)
tree2828957ed7c27c537386cda13ace2372903185b8 /packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
parentchore(frontend): remove duplicate declarations (diff)
downloadmisskey-cd9322a8243b12632db2dd9a29a702d7531a5aa0.tar.gz
misskey-cd9322a8243b12632db2dd9a29a702d7531a5aa0.tar.bz2
misskey-cd9322a8243b12632db2dd9a29a702d7531a5aa0.zip
feat(frontend): 画像編集機能 (#16121)
* wip * wip * wip * wip * Update watermarker.ts * wip * wip * Update watermarker.ts * Update MkUploaderDialog.vue * wip * Update ImageEffector.ts * Update ImageEffector.ts * wip * wip * wip * wip * wip * wip * Update MkRange.vue * Update MkRange.vue * wip * wip * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.Layer.vue * wip * Update zoomLines.ts * Update zoomLines.ts * wip * wip * Update ImageEffector.ts * wip * Update ImageEffector.ts * wip * Update ImageEffector.ts * swip * wip * Update ImageEffector.ts * wop * Update MkUploaderDialog.vue * Update ImageEffector.ts * wip * wip * wip * Update def.ts * Update def.ts * test * test * Update manager.ts * Update manager.ts * Update manager.ts * Update manager.ts * Update MkImageEffectorDialog.vue * wip * use WEBGL_lose_context * wip * Update MkUploaderDialog.vue * Update drive.vue * wip * Update MkUploaderDialog.vue * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip
Diffstat (limited to 'packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue')
-rw-r--r--packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue318
1 files changed, 318 insertions, 0 deletions
diff --git a/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue b/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
new file mode 100644
index 0000000000..10de04c16a
--- /dev/null
+++ b/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
@@ -0,0 +1,318 @@
+<!--
+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, useTemplateRef, watch, onMounted, onUnmounted } from 'vue';
+import type { WatermarkPreset } from '@/utility/watermark.js';
+import { i18n } from '@/i18n.js';
+import MkSelect from '@/components/MkSelect.vue';
+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';
+import { prefer } from '@/preferences.js';
+
+const layer = defineModel<WatermarkPreset['layers'][number]>('layer', { required: true });
+
+const driveFile = ref();
+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(ev.currentTarget ?? ev.target, i18n.ts.selectFile).then((file) => {
+ 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>