diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2025-08-09 14:11:19 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-09 14:11:19 +0900 |
| commit | 785b85ee462ac6f3af416be1d3ed68f3e478118b (patch) | |
| tree | 81c738be824246664274cd5aca1955e5e9a27f91 /packages/frontend/src/components/MkImageEffectorFxForm.vue | |
| parent | fix: カラムの名前が正しくリスト/チャンネルの名前にな... (diff) | |
| download | misskey-785b85ee462ac6f3af416be1d3ed68f3e478118b.tar.gz misskey-785b85ee462ac6f3af416be1d3ed68f3e478118b.tar.bz2 misskey-785b85ee462ac6f3af416be1d3ed68f3e478118b.zip | |
enhance(frontend): 画像エフェクトのUI改善 (#16191)
* enhance(frontend): 画像エフェクトの改善
* enhance: i18n colorClampAdvanced
* fix: missing translation
* enhance: i18n blockNoise
* fix lint
* fix: narrow down fx defs types
* fix
* fix: watermark用エフェクトは別で定義し直す
* fix lint
* ImageEffectorをwatermarkに隠蔽
* watermark関連の定義を完全に分離
* refactor
* fix
* ぼかし効果 -> スムージング
* refactor: remove unnecessary `as const`
* Update Changelog
Diffstat (limited to 'packages/frontend/src/components/MkImageEffectorFxForm.vue')
| -rw-r--r-- | packages/frontend/src/components/MkImageEffectorFxForm.vue | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/packages/frontend/src/components/MkImageEffectorFxForm.vue b/packages/frontend/src/components/MkImageEffectorFxForm.vue new file mode 100644 index 0000000000..d7ab620132 --- /dev/null +++ b/packages/frontend/src/components/MkImageEffectorFxForm.vue @@ -0,0 +1,95 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="_gaps"> + <div v-for="v, k in paramDefs" :key="k"> + <MkSwitch + v-if="v.type === 'boolean'" + v-model="params[k]"> + <template #label>{{ v.label ?? k }}</template> + <template v-if="v.caption != null" #caption>{{ v.caption }}</template> + </MkSwitch> + <MkRange + v-else-if="v.type === 'number'" + v-model="params[k]" + continuousUpdate + :min="v.min" + :max="v.max" + :step="v.step" + :textConverter="v.toViewValue" + @thumbDoubleClicked="() => { + params[k] = v.default; + }" + > + <template #label>{{ v.label ?? k }}</template> + <template v-if="v.caption != null" #caption>{{ v.caption }}</template> + </MkRange> + <MkRadios v-else-if="v.type === 'number:enum'" v-model="params[k]"> + <template #label>{{ v.label ?? k }}</template> + <template v-if="v.caption != null" #caption>{{ v.caption }}</template> + <option v-for="item in v.enum" :value="item.value"> + <i v-if="item.icon" :class="item.icon"></i> + <template v-else>{{ item.label }}</template> + </option> + </MkRadios> + <div v-else-if="v.type === 'seed'"> + <MkRange v-model="params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1"> + <template #label>{{ v.label ?? k }}</template> + <template v-if="v.caption != null" #caption>{{ v.caption }}</template> + </MkRange> + </div> + <MkInput v-else-if="v.type === 'color'" :modelValue="getHex(params[k])" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) params[k] = c; }"> + <template #label>{{ v.label ?? k }}</template> + <template v-if="v.caption != null" #caption>{{ v.caption }}</template> + </MkInput> + </div> + <div v-if="Object.keys(paramDefs).length === 0" :class="$style.nothingToConfigure"> + {{ i18n.ts._imageEffector.nothingToConfigure }} + </div> +</div> +</template> + +<script setup lang="ts"> +import MkInput from '@/components/MkInput.vue'; +import MkRadios from '@/components/MkRadios.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkRange from '@/components/MkRange.vue'; +import { i18n } from '@/i18n.js'; +import type { ImageEffectorRGB, ImageEffectorFxParamDefs } from '@/utility/image-effector/ImageEffector.js'; + +defineProps<{ + paramDefs: ImageEffectorFxParamDefs; +}>(); + +const params = defineModel<Record<string, any>>({ required: true }); + +function getHex(c: ImageEffectorRGB) { + return `#${c.map(x => (x * 255).toString(16).padStart(2, '0')).join('')}`; +} + +function getRgb(hex: string | number): ImageEffectorRGB | null { + if ( + typeof hex === 'number' || + typeof hex !== 'string' || + !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex) + ) { + return null; + } + + const m = hex.slice(1).match(/[0-9a-fA-F]{2}/g); + if (m == null) return [0, 0, 0]; + return m.map(x => parseInt(x, 16) / 255) as ImageEffectorRGB; +} +</script> + +<style module> +.nothingToConfigure { + opacity: 0.7; + text-align: center; + font-size: 14px; + padding: 0 10px; +} +</style> |