mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
hapticks 3 3 3 3 3 3
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
} from '../../controls/inputs/driveIntent.js';
|
||||
import { useSettingsNamespace } from '../../settings/index.js';
|
||||
import { INPUT_SETTINGS_DEFAULTS } from '../../settings/namespaces.js';
|
||||
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
|
||||
import FloatingJoystick from './FloatingJoystick.jsx';
|
||||
import {
|
||||
DRIVE_PAD_REPEAT_MS,
|
||||
@@ -134,8 +135,12 @@ export default function ControlPadPanel({ compact = false, disabled = false }) {
|
||||
|
||||
const handleSpeedModeChange = useCallback(
|
||||
(nextMode) => {
|
||||
if (nextMode === speedModeRef.current) return;
|
||||
setSpeedMode(nextMode);
|
||||
speedModeRef.current = nextMode;
|
||||
// Confirm the accepted mode transition here so tapping an already-active
|
||||
// mode cannot produce feedback for a state change that did not happen.
|
||||
triggerTouchHaptic('button');
|
||||
setCameraPrecisionMode(nextMode === 'precision');
|
||||
if (activeCellRef.current) {
|
||||
sendDriveCell(activeCellRef.current, 'speed', nextMode);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Scope: Owns pointer tracking, fixed overlay positioning, and active 3x3 drive-zone feedback.
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
|
||||
|
||||
const PAD_MARGIN = 12;
|
||||
const CELL_COUNT = 3;
|
||||
@@ -146,6 +147,10 @@ export default function FloatingJoystick({
|
||||
const cell = cellFromPointer(currentPad, event.clientX, event.clientY);
|
||||
if (activeCellIdRef.current !== cell.id) {
|
||||
activeCellIdRef.current = cell.id;
|
||||
// Pulse only when the thumb crosses a real 3x3 cell boundary so
|
||||
// continuous pointermove traffic cannot buzz constantly. The shared
|
||||
// pointerup listener provides a Safari-compatible gesture-end pulse.
|
||||
triggerTouchHaptic('drive');
|
||||
onCellChange?.(cell);
|
||||
}
|
||||
setActivePad({
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Mobile Aux Button
|
||||
// Purpose: Defines the Mobile Aux Button module and the local helpers/components used in this file.
|
||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
|
||||
|
||||
export default function MobileAuxButton({ id, label, values, color, disabled, onPress, onRelease }) {
|
||||
return (
|
||||
<button
|
||||
@@ -10,7 +12,12 @@ export default function MobileAuxButton({ id, label, values, color, disabled, on
|
||||
event.preventDefault();
|
||||
onPress(id, values);
|
||||
}}
|
||||
onPointerUp={() => onRelease(id)}
|
||||
onPointerUp={() => {
|
||||
onRelease(id);
|
||||
// A completed held action is the semantic confirmation point. Keeping
|
||||
// feedback beside release avoids buzzing for cancelled aux gestures.
|
||||
triggerTouchHaptic('button');
|
||||
}}
|
||||
onPointerLeave={() => onRelease(id)}
|
||||
onPointerCancel={() => onRelease(id)}
|
||||
onContextMenu={(event) => event.preventDefault()}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// Purpose: Provides the mobile-only camera tilt slider that supports simultaneous two-finger mobile driving.
|
||||
// Scope: Owns pointer tracking and visual slider state for the compact vertical mobile camera control.
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import { triggerTouchHaptic } from '../../lib/touchHaptics.js';
|
||||
|
||||
const HAPTIC_MIN_ANGLE_DELTA_DEGREES = 2;
|
||||
|
||||
function clampCameraAngle(value, min, max) {
|
||||
if (!Number.isFinite(value)) return min;
|
||||
@@ -23,6 +26,7 @@ export default function VerticalCameraTilt({
|
||||
}) {
|
||||
const trackRef = useRef(null);
|
||||
const pointerIdRef = useRef(null);
|
||||
const lastHapticValueRef = useRef(null);
|
||||
|
||||
const valuePercent = useMemo(() => {
|
||||
if (max === min) return 50;
|
||||
@@ -47,6 +51,17 @@ export default function VerticalCameraTilt({
|
||||
const sendPointerValue = useCallback(
|
||||
(event) => {
|
||||
const next = valueFromPointer(event);
|
||||
|
||||
/*
|
||||
Camera commands must retain the configured 0.5 or 0.1 degree precision,
|
||||
but physical feedback does not need to mirror every servo step. Require
|
||||
two degrees of accumulated travel so the ticks describe slider position
|
||||
without allowing pointer event frequency to control their cadence.
|
||||
*/
|
||||
if (Math.abs(next - lastHapticValueRef.current) >= HAPTIC_MIN_ANGLE_DELTA_DEGREES) {
|
||||
triggerTouchHaptic('camera');
|
||||
lastHapticValueRef.current = next;
|
||||
}
|
||||
onChange?.(next);
|
||||
},
|
||||
[onChange, valueFromPointer],
|
||||
@@ -65,10 +80,13 @@ export default function VerticalCameraTilt({
|
||||
*/
|
||||
event.preventDefault();
|
||||
pointerIdRef.current = event.pointerId;
|
||||
// Seed the slider value so its first position update is not mistaken for
|
||||
// two degrees of travel before the finger has actually moved that far.
|
||||
lastHapticValueRef.current = value;
|
||||
trackRef.current?.setPointerCapture?.(event.pointerId);
|
||||
sendPointerValue(event);
|
||||
},
|
||||
[disabled, sendPointerValue],
|
||||
[disabled, sendPointerValue, value],
|
||||
);
|
||||
|
||||
const handlePointerMove = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user