mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-02 20:15:55 +02:00
enhance(frontend): マスクエフェクト (#16556)
* wip * wip * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.vue * Update MkImageEffectorDialog.vue * Update fillSquare.ts * Update CHANGELOG.md * Update fillSquare.ts
This commit is contained in:
@@ -19,9 +19,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div :class="$style.root">
|
||||
<div :class="$style.container">
|
||||
<div :class="$style.preview">
|
||||
<canvas ref="canvasEl" :class="$style.previewCanvas"></canvas>
|
||||
<canvas ref="canvasEl" :class="$style.previewCanvas" @pointerdown="onImagePointerdown"></canvas>
|
||||
<div :class="$style.previewContainer">
|
||||
<div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
|
||||
<div class="_acrylic" :class="$style.editControls">
|
||||
<button class="_button" :class="[$style.previewControlsButton, fillSquare ? $style.active : null]" @click="fillSquare = true"><i class="ti ti-pencil"></i></button>
|
||||
</div>
|
||||
<div class="_acrylic" :class="$style.previewControls">
|
||||
<button class="_button" :class="[$style.previewControlsButton, !enabled ? $style.active : null]" @click="enabled = false">Before</button>
|
||||
<button class="_button" :class="[$style.previewControlsButton, enabled ? $style.active : null]" @click="enabled = true">After</button>
|
||||
@@ -212,6 +215,100 @@ watch(enabled, () => {
|
||||
renderer.render();
|
||||
}
|
||||
});
|
||||
|
||||
const fillSquare = ref(false);
|
||||
|
||||
function onImagePointerdown(ev: PointerEvent) {
|
||||
if (canvasEl.value == null || imageBitmap == null || !fillSquare.value) return;
|
||||
|
||||
const AW = canvasEl.value.clientWidth;
|
||||
const AH = canvasEl.value.clientHeight;
|
||||
const BW = imageBitmap.width;
|
||||
const BH = imageBitmap.height;
|
||||
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
|
||||
if (AW / AH < BW / BH) { // 横長
|
||||
yOffset = AH - BH * (AW / BW);
|
||||
} else { // 縦長
|
||||
xOffset = AW - BW * (AH / BH);
|
||||
}
|
||||
|
||||
xOffset /= 2;
|
||||
yOffset /= 2;
|
||||
|
||||
let startX = ev.offsetX - xOffset;
|
||||
let startY = ev.offsetY - yOffset;
|
||||
|
||||
if (AW / AH < BW / BH) { // 横長
|
||||
startX = startX / (Math.max(AW, AH) / Math.max(BH / BW, 1));
|
||||
startY = startY / (Math.max(AW, AH) / Math.max(BW / BH, 1));
|
||||
} else { // 縦長
|
||||
startX = startX / (Math.min(AW, AH) / Math.max(BH / BW, 1));
|
||||
startY = startY / (Math.min(AW, AH) / Math.max(BW / BH, 1));
|
||||
}
|
||||
|
||||
const id = genId();
|
||||
layers.push({
|
||||
id,
|
||||
fxId: 'fillSquare',
|
||||
params: {
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
scaleX: 0.1,
|
||||
scaleY: 0.1,
|
||||
angle: 0,
|
||||
opacity: 1,
|
||||
color: [1, 1, 1],
|
||||
},
|
||||
});
|
||||
|
||||
_move(ev.offsetX, ev.offsetY);
|
||||
|
||||
function _move(pointerX: number, pointerY: number) {
|
||||
let x = pointerX - xOffset;
|
||||
let y = pointerY - yOffset;
|
||||
|
||||
if (AW / AH < BW / BH) { // 横長
|
||||
x = x / (Math.max(AW, AH) / Math.max(BH / BW, 1));
|
||||
y = y / (Math.max(AW, AH) / Math.max(BW / BH, 1));
|
||||
} else { // 縦長
|
||||
x = x / (Math.min(AW, AH) / Math.max(BH / BW, 1));
|
||||
y = y / (Math.min(AW, AH) / Math.max(BW / BH, 1));
|
||||
}
|
||||
|
||||
const scaleX = Math.abs(x - startX);
|
||||
const scaleY = Math.abs(y - startY);
|
||||
|
||||
const layerIndex = layers.findIndex((l) => l.id === id);
|
||||
const layer = layerIndex !== -1 ? layers[layerIndex] : null;
|
||||
if (layer != null) {
|
||||
layer.params.offsetX = (x + startX) - 1;
|
||||
layer.params.offsetY = (y + startY) - 1;
|
||||
layer.params.scaleX = scaleX;
|
||||
layer.params.scaleY = scaleY;
|
||||
layers[layerIndex] = layer;
|
||||
}
|
||||
}
|
||||
|
||||
function move(ev: PointerEvent) {
|
||||
_move(ev.offsetX, ev.offsetY);
|
||||
}
|
||||
|
||||
function up() {
|
||||
canvasEl.value?.removeEventListener('pointermove', move);
|
||||
canvasEl.value?.removeEventListener('pointerup', up);
|
||||
canvasEl.value?.removeEventListener('pointercancel', up);
|
||||
canvasEl.value?.releasePointerCapture(ev.pointerId);
|
||||
|
||||
fillSquare.value = false;
|
||||
}
|
||||
|
||||
canvasEl.value.addEventListener('pointermove', move);
|
||||
canvasEl.value.addEventListener('pointerup', up);
|
||||
canvasEl.value.setPointerCapture(ev.pointerId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
@@ -251,6 +348,18 @@ watch(enabled, () => {
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
.editControls {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
bottom: 8px;
|
||||
left: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.previewControls {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
@@ -283,9 +392,11 @@ watch(enabled, () => {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
width: -webkit-fill-available;
|
||||
width: stretch;
|
||||
height: -webkit-fill-available;
|
||||
height: stretch;
|
||||
margin: 20px;
|
||||
box-sizing: border-box;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user