fix(frontend): ウィンドウのドラッグ・サイズ変更のポインターをキャプチャするように (#17159)

* fix(frontend): ウィンドウのドラッグ・サイズ変更のポインターをキャプチャするように

* Update Changelog

* fix
This commit is contained in:
かっこかり
2026-02-16 19:23:30 +09:00
committed by GitHub
parent b5121dc70e
commit 41620600cc
2 changed files with 74 additions and 42 deletions

View File

@@ -27,6 +27,7 @@
- Fix: mCaptchaが正しく動作しない問題を修正
- Fix: 非ログイン時にリバーシの対局が表示されない問題を修正
- Fix: ノートの詳細表示でリアクションが全件表示されない問題を修正
- Fix: 動画埋め込みプレイヤーなどの一部ウィンドウで、ウィンドウのサイズ変更や移動が正常に行えない問題を修正
- Fix: 画像エフェクトの修正
- 塗りつぶし・モザイク・ぼかしエフェクトを回転させると歪む問題を修正
- モザイクの格子のサイズが画像の縦横比によって長方形となる問題を修正

View File

@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-for="button in buttonsLeft" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button>
</template>
</span>
<span :class="$style.headerTitle" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
<span :class="$style.headerTitle" @pointerdown.prevent="onHeaderPointerdown">
<slot name="header"></slot>
</span>
<span :class="$style.headerRight">
@@ -39,14 +39,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<template v-if="canResize && !minimized">
<div :class="$style.handleTop" @mousedown.prevent="onTopHandleMousedown"></div>
<div :class="$style.handleRight" @mousedown.prevent="onRightHandleMousedown"></div>
<div :class="$style.handleBottom" @mousedown.prevent="onBottomHandleMousedown"></div>
<div :class="$style.handleLeft" @mousedown.prevent="onLeftHandleMousedown"></div>
<div :class="$style.handleTopLeft" @mousedown.prevent="onTopLeftHandleMousedown"></div>
<div :class="$style.handleTopRight" @mousedown.prevent="onTopRightHandleMousedown"></div>
<div :class="$style.handleBottomRight" @mousedown.prevent="onBottomRightHandleMousedown"></div>
<div :class="$style.handleBottomLeft" @mousedown.prevent="onBottomLeftHandleMousedown"></div>
<div :class="$style.handleTop" @pointerdown.prevent="onTopHandlePointerdown"></div>
<div :class="$style.handleRight" @pointerdown.prevent="onRightHandlePointerdown"></div>
<div :class="$style.handleBottom" @pointerdown.prevent="onBottomHandlePointerdown"></div>
<div :class="$style.handleLeft" @pointerdown.prevent="onLeftHandlePointerdown"></div>
<div :class="$style.handleTopLeft" @pointerdown.prevent="onTopLeftHandlePointerdown"></div>
<div :class="$style.handleTopRight" @pointerdown.prevent="onTopRightHandlePointerdown"></div>
<div :class="$style.handleBottomRight" @pointerdown.prevent="onBottomRightHandlePointerdown"></div>
<div :class="$style.handleBottomLeft" @pointerdown.prevent="onBottomLeftHandlePointerdown"></div>
</template>
</div>
</Transition>
@@ -70,20 +70,39 @@ type WindowButton = {
const minHeight = 50;
const minWidth = 250;
function dragListen(fn: (ev: MouseEvent | TouchEvent) => void) {
window.addEventListener('mousemove', fn);
window.addEventListener('touchmove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
window.addEventListener('mouseup', dragClear.bind(null, fn));
window.addEventListener('touchend', dragClear.bind(null, fn));
function dragListen(fn: (ev: PointerEvent) => void) {
window.addEventListener('pointermove', fn);
const clear = () => {
dragClear(fn);
};
window.addEventListener('pointerup', clear, { once: true });
window.addEventListener('pointercancel', clear, { once: true });
window.addEventListener('blur', clear, { once: true });
}
function dragClear(fn: (ev: MouseEvent | TouchEvent) => void) {
window.removeEventListener('mousemove', fn);
window.removeEventListener('touchmove', fn);
window.removeEventListener('mouseleave', dragClear as any);
window.removeEventListener('mouseup', dragClear as any);
window.removeEventListener('touchend', dragClear as any);
function dragClear(fn: (ev: PointerEvent) => void) {
window.removeEventListener('pointermove', fn);
}
function capturePointer(evt: PointerEvent) {
const target = evt.currentTarget;
if (!(target instanceof HTMLElement)) return;
if (!target.setPointerCapture) return;
try {
target.setPointerCapture(evt.pointerId);
} catch {
return;
}
const release = () => {
if (target.hasPointerCapture(evt.pointerId)) {
target.releasePointerCapture(evt.pointerId);
}
};
window.addEventListener('pointerup', release, { once: true });
window.addEventListener('pointercancel', release, { once: true });
}
const props = withDefaults(defineProps<{
@@ -209,15 +228,17 @@ function onDblClick() {
}
}
function getPositionX(event: MouseEvent | TouchEvent) {
return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientX : 'clientX' in event ? event.clientX : 0;
function getPositionX(event: PointerEvent) {
return event.clientX;
}
function getPositionY(event: MouseEvent | TouchEvent) {
return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientY : 'clientY' in event ? event.clientY : 0;
function getPositionY(event: PointerEvent) {
return event.clientY;
}
function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
function onHeaderPointerdown(evt: PointerEvent) {
capturePointer(evt);
// 右クリックはコンテキストメニューを開こうとした可能性が高いため無視
if ('button' in evt && evt.button === 2) return;
@@ -289,7 +310,9 @@ function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
}
// 上ハンドル掴み時
function onTopHandleMousedown(evt: MouseEvent | TouchEvent) {
function onTopHandlePointerdown(evt: PointerEvent) {
capturePointer(evt);
const main = rootEl.value;
// どういうわけかnullになることがある
if (main == null) return;
@@ -317,7 +340,9 @@ function onTopHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 右ハンドル掴み時
function onRightHandleMousedown(evt: MouseEvent | TouchEvent) {
function onRightHandlePointerdown(evt: PointerEvent) {
capturePointer(evt);
const main = rootEl.value;
if (main == null) return;
@@ -342,7 +367,9 @@ function onRightHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 下ハンドル掴み時
function onBottomHandleMousedown(evt: MouseEvent | TouchEvent) {
function onBottomHandlePointerdown(evt: PointerEvent) {
capturePointer(evt);
const main = rootEl.value;
if (main == null) return;
@@ -367,7 +394,9 @@ function onBottomHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 左ハンドル掴み時
function onLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
function onLeftHandlePointerdown(evt: PointerEvent) {
capturePointer(evt);
const main = rootEl.value;
if (main == null) return;
@@ -394,27 +423,27 @@ function onLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 左上ハンドル掴み時
function onTopLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
onTopHandleMousedown(evt);
onLeftHandleMousedown(evt);
function onTopLeftHandlePointerdown(evt: PointerEvent) {
onTopHandlePointerdown(evt);
onLeftHandlePointerdown(evt);
}
// 右上ハンドル掴み時
function onTopRightHandleMousedown(evt: MouseEvent | TouchEvent) {
onTopHandleMousedown(evt);
onRightHandleMousedown(evt);
function onTopRightHandlePointerdown(evt: PointerEvent) {
onTopHandlePointerdown(evt);
onRightHandlePointerdown(evt);
}
// 右下ハンドル掴み時
function onBottomRightHandleMousedown(evt: MouseEvent | TouchEvent) {
onBottomHandleMousedown(evt);
onRightHandleMousedown(evt);
function onBottomRightHandlePointerdown(evt: PointerEvent) {
onBottomHandlePointerdown(evt);
onRightHandlePointerdown(evt);
}
// 左下ハンドル掴み時
function onBottomLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
onBottomHandleMousedown(evt);
onLeftHandleMousedown(evt);
function onBottomLeftHandlePointerdown(evt: PointerEvent) {
onBottomHandlePointerdown(evt);
onLeftHandlePointerdown(evt);
}
// 高さを適用
@@ -566,6 +595,7 @@ defineExpose({
overflow: hidden;
text-overflow: ellipsis;
cursor: move;
touch-action: none;
}
.content {
@@ -579,6 +609,7 @@ $handleSize: 8px;
.handle {
position: absolute;
touch-action: none;
}
.handleTop {