diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-06-03 19:18:29 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-03 19:18:29 +0900 |
| commit | cd9322a8243b12632db2dd9a29a702d7531a5aa0 (patch) | |
| tree | 2828957ed7c27c537386cda13ace2372903185b8 /packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue | |
| parent | chore(frontend): remove duplicate declarations (diff) | |
| download | misskey-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.vue | 318 |
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> |