From 83ab2ae7605de432c8a94be6d90daa1dfa8902e2 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:04:58 +0900 Subject: bluebey-studio: Shift+ドラッグで基準点を動かせるようにする MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通常ドラッグはぶるべーを移動(基準点は固定)、Shift+ドラッグはカメラの基準点を移動する。スマホの1本指/2本指の操作に合わせた。 --- public/bluebey-studio/src/main.js | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'public/bluebey-studio/src') diff --git a/public/bluebey-studio/src/main.js b/public/bluebey-studio/src/main.js index e6bc785..ac8c04b 100644 --- a/public/bluebey-studio/src/main.js +++ b/public/bluebey-studio/src/main.js @@ -2321,9 +2321,10 @@ async function init() { // --- the mouse on the character ---------------------------------------- // Dragging the model slides it across the view; holding Shift and dragging - // turns it around. Bone posing lives in the panel, so the mouse never grabs a - // bone (see `Rig` in src/rig.js). On a small screen the panel is a sheet over - // the view, so a tap on the view first just dismisses the sheet. + // moves the camera's reference point instead (the phone's two-finger drag). + // Bone posing lives in the panel, so the mouse never grabs a bone (see `Rig` + // in src/rig.js). On a small screen the panel is a sheet over the view, so a + // tap on the view first just dismisses the sheet. const dragRay = new THREE.Raycaster(); const dragNdc = new THREE.Vector2(); const dragHitAt = (event) => { @@ -2355,14 +2356,12 @@ async function init() { } if (!dragHitAt(event)) return; const root = state.pose.root ?? [0, 0, 0]; - const master = state.pose.bones?.master ?? [0, 0, 0]; modelDrag = { pointerId: event.pointerId, - spin: event.shiftKey, + pan: event.shiftKey, x: event.clientX, y: event.clientY, root: [...root], - masterY: master[1] ?? 0, }; view.controls.enabled = false; stage.style.cursor = 'grabbing'; @@ -2373,30 +2372,41 @@ async function init() { if (!modelDrag || event.pointerId !== modelDrag.pointerId) return; const dx = event.clientX - modelDrag.x; const dy = event.clientY - modelDrag.y; - if (modelDrag.spin) { - const bones = { ...(state.pose.bones ?? {}) }; - const master = bones.master ?? [0, 0, 0]; - bones.master = [master[0], round2(modelDrag.masterY + dx * 0.7), master[2]]; - state.pose.bones = bones; + const k = dragScale(); + if (modelDrag.pan) { + // Shift+drag: move the camera's reference point (the same effect as the + // two-finger drag on a phone). The character stays where it is. + const right = new THREE.Vector3(1, 0, 0).applyQuaternion(view.camera.quaternion); + const up = new THREE.Vector3(0, 1, 0).applyQuaternion(view.camera.quaternion); + const move = new THREE.Vector3() + .addScaledVector(right, -dx * k) + .addScaledVector(up, dy * k); + view.target.add(move); + for (const controls of Object.values(view.controlsMap ?? {})) controls.target.add(move); + for (const cam of Object.values(view.cameras ?? {})) cam.position.add(move); + view.captureInto(state.view); } else { - const k = dragScale(); + // Plain drag: slide the character, leaving the camera's reference point + // alone (the one-finger drag on a phone). state.pose.root = [ round2(modelDrag.root[0] + dx * k), round2(modelDrag.root[1] - dy * k), modelDrag.root[2], ]; + app.panel?.onRigChanged?.(); } - app.panel?.onRigChanged?.(); app.needsRender = true; }); const endModelDrag = (event) => { if (!modelDrag || (event && event.pointerId !== modelDrag.pointerId)) return; + const wasPan = modelDrag.pan === true; modelDrag = null; view.controls.enabled = true; stage.style.cursor = ''; if (event) { try { canvas.releasePointerCapture(event.pointerId); } catch { /* ignore */ } } - capturePose(); + if (wasPan) app.actions.syncPanel?.(); + else capturePose(); app.panel?.onRigChanged?.(); app.needsRender = true; }; -- cgit v1.3.1