mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
fix(touch): separate pan, edge swipe and double fit (#691)
Issue: #691 User-Visible: yes
This commit is contained in:
+21
-15
@@ -263,7 +263,7 @@ produce a zero-sized SVG `viewBox`.
|
||||
* **Zoom out** — `MIN_ZOOM = 1/3`: you can see three times the content
|
||||
frame and no further. Empty space beyond that is not information.
|
||||
* **Discrete camera motion** (#82) — wheel, `−`/`+`, Fit all, the home arrow
|
||||
and the free-background double-click/tap in View or kiosk interpolate the
|
||||
and the plan-surface double-click/tap in View or kiosk interpolate the
|
||||
existing exact camera target for
|
||||
160–220 ms with `cubic-bezier(0.2, 0.7, 0.2, 1)`. Zoom is logarithmic and
|
||||
the world centre is linear; the final frame is the same clamped `viewBox`
|
||||
@@ -291,10 +291,12 @@ produce a zero-sized SVG `viewBox`.
|
||||
bails out on them), and a drawing tool that consumes the press —
|
||||
decor line/rect/ellipse/text — bails out too. Two fingers are always
|
||||
a pinch, never a pan. On a **kiosk** screen at swipe zoom (`≤ 1`,
|
||||
more than one space) a *horizontal* drag belongs to the floor swipe:
|
||||
the gesture is classified once, on the first movement past 8 px, and
|
||||
keeps that role until the finger lifts (`_panLock`), so the plan
|
||||
never slides under a swipe and a vertical drag still pans.
|
||||
more than one space), a floor swipe may start only inside the inner
|
||||
**48 CSS px** strip of the left/right stage edge that has a previous/next
|
||||
space. The first movement past the 8 px Manhattan boundary becomes a swipe
|
||||
only when it points inward and `|dx| > 1.5 × |dy|`; equality, the wrong
|
||||
direction, a start outside the strip and an edge without a neighbour all
|
||||
become ordinary pan. The decision remains final until release (`_panLock`).
|
||||
* **Room fit (#152).** A clean primary click/tap on the browser-painted room
|
||||
target fits that room's final floor plus its visible boundary wall body into
|
||||
the middle 80% of the stage. Devices, openings/actions and the HA Area link
|
||||
@@ -303,15 +305,19 @@ produce a zero-sized SVG `viewBox`.
|
||||
does not write the per-space zoom preference. Its session-only room intent
|
||||
is reapplied atomically on a stable stage resize, then cleared by manual
|
||||
camera input, Fit all/home, mode/space/projection changes, structural
|
||||
adoption, hidden state or disconnect. In kiosk, room-owned taps never enter
|
||||
the free-background double-tap sequence.
|
||||
* **Free-background fit (#449).** Two clean primary clicks/taps within 350 ms
|
||||
on the stage background invoke the same Fit all command in View and kiosk.
|
||||
Room, device, vacuum, opening, link and control paths disarm the sequence;
|
||||
so do pan, pinch, swipe, long press, cancellation, editor/mode/space/projection
|
||||
changes and lifecycle adoption. The new recognizer adds no timer or render on
|
||||
the first tap. Mouse, touch and pen sequences are separate; editors do not
|
||||
expose this shortcut.
|
||||
adoption, hidden state or disconnect. A pointer tap waits up to 350 ms before
|
||||
applying room fit: this lets a second clean plan tap select Fit all without
|
||||
first animating into the room. Keyboard activation remains immediate.
|
||||
* **Plan-surface fit (#449, #691).** Two clean primary clicks/taps within 350 ms
|
||||
on stage background, room fill or a non-interactive room label invoke the
|
||||
same Fit all command in View and kiosk. The two taps may use different plan
|
||||
surfaces or rooms, but must use the same pointer modality and space. A single
|
||||
room tap still fits that room once its 350 ms decision window expires; a
|
||||
single background tap stays passive. Device, vacuum, opening, stair, link and
|
||||
control paths cancel both the sequence and any pending room fit; so do pan,
|
||||
pinch, swipe, long press, cancellation, editor/mode/space/projection changes
|
||||
and lifecycle adoption. Mouse, touch and pen share this contract but do not
|
||||
pair with one another; editors do not expose the shortcut.
|
||||
* **The lock is final, at the release too** (audit DEV-1DA1-02). The
|
||||
release used to ask `swipeTarget()` again from the raw start→end
|
||||
vector, ignoring the lock — so a *curved* gesture (a short vertical
|
||||
@@ -321,7 +327,7 @@ produce a zero-sized SVG `viewBox`.
|
||||
'pan'` now means no floor change, whatever the overall vector ends up
|
||||
looking like; only a gesture locked as `swipe` may reach
|
||||
`swipeTarget()`, and it never pans on the way. A motionless tap locks
|
||||
nothing, so a clean free-background double-tap remains available.
|
||||
nothing, so a clean plan-surface double-tap remains available.
|
||||
* **"Home is that way" arrow** — when the content frame is entirely
|
||||
outside the current view, a small pointer appears at the view edge
|
||||
in the frame's direction. Clicking it fits the content. Cheap
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Touch navigation now keeps ordinary one-finger drags as pan and switches
|
||||
kiosk spaces only from a 48 px inner edge with an available neighbour. Two
|
||||
clean taps on background, a room fill or a passive room label fit the whole
|
||||
plan without first jumping into the room; a single room tap still fits after
|
||||
the 350 ms decision window, consistently for touch, mouse and pen
|
||||
([#691](https://github.com/Matysh/houseplan-card/issues/691)).
|
||||
|
||||
- Stair outlines, treads, trapezoids and direction arrows now share one 3.6 cm
|
||||
physical line weight. It scales with the plan instead of staying fixed in
|
||||
screen pixels, remains uniform when a stair is resized or rotated, and uses
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Навигация на сенсорных экранах теперь сохраняет обычный drag одним пальцем
|
||||
для панорамирования, а пространство в киоске переключается только жестом из
|
||||
внутренней 48-пиксельной зоны края, за которым есть соседнее пространство.
|
||||
Два чистых тапа по фону, заливке комнаты или пассивной подписи вписывают весь
|
||||
план без промежуточного прыжка в комнату; одиночный тап по комнате по-прежнему
|
||||
вписывает её после окна выбора 350 мс — одинаково для touch, мыши и пера
|
||||
([#691](https://github.com/Matysh/houseplan-card/issues/691)).
|
||||
|
||||
- Контур, ступени, трапеция и стрелка направления лестницы теперь используют
|
||||
единую физическую толщину 3,6 см. Она масштабируется вместе с планом, а не
|
||||
остаётся постоянной в экранных пикселях, не искажается при изменении размера
|
||||
|
||||
@@ -39,8 +39,12 @@ On phones, tablets, wall panels and HA Companion apps, the ordinary View must:
|
||||
- support convenient pan, pinch zoom and space switching;
|
||||
- treat a linked stair as a floor-navigation target only after a clean tap;
|
||||
pan, pinch, long press, swipe and cancellation tails must stay inert;
|
||||
- fit the whole plan after two clean taps on free scene background, while one
|
||||
tap on a room keeps the immediate room-fit action;
|
||||
- reserve floor switching for an inward horizontal gesture that starts in the
|
||||
48 CSS px strip of an edge with a neighbouring space; a drag elsewhere and
|
||||
an unavailable edge must pan the plan;
|
||||
- fit the whole plan after two clean taps on background, room fill or a passive
|
||||
room label. A single room tap fits only after the 350 ms second-tap window,
|
||||
with no intermediate camera motion; keyboard room activation stays immediate;
|
||||
- provide a touch path for essential information that desktop exposes through
|
||||
hover;
|
||||
- open and close View dialogs without clipping their essential content or
|
||||
|
||||
+10
-8
@@ -433,9 +433,9 @@ precise drawing, Resize, keyboard modifiers and double-click properties.
|
||||
|
||||
| Scenario | Mouse | Touch View | Touch editors | Keyboard |
|
||||
|---|---|---|---|---|
|
||||
| Zoom and pan | Wheel; drag empty space; `−`/`+`; double-click free background to Fit all | Pinch; drag; double-tap free background to Fit all | Available but precision is not guaranteed; no Fit-all double-tap | — |
|
||||
| Room | A clean click fits the room with 10% margins | One tap fits the room; repeated room taps never become Fit all | — | `Enter`/`Space` on a visible room label |
|
||||
| Change space | Click a tab | Tap; kiosk swipe at 1:1 | Tap a tab | — |
|
||||
| Zoom and pan | Wheel; drag empty space; `−`/`+`; double-click background/room to Fit all | Pinch; drag; double-tap background/room to Fit all | Available but precision is not guaranteed; no Fit-all double-tap | — |
|
||||
| Room | One clean click fits the room after the 350 ms double-click window | One clean tap fits the room after the 350 ms double-tap window | — | Immediate `Enter`/`Space` on a visible room label |
|
||||
| Change space | Click a tab | Tap; in kiosk at 1:1, swipe inward from the 48 px edge that has a neighbour | Tap a tab | — |
|
||||
| Device | Click/double-click per mode | Tap; safe actions equal desktop | Drag/properties are best effort | `Esc` closes the top surface |
|
||||
| Walls drawing or precise drag | Full contract | Not applicable | Best effort; use desktop for Resize and exact nodes | `Shift` changes magnet/angle; `Esc` finishes a Walls chain or cancels the current precise drag |
|
||||
| Editor history | Undo/Redo controls | Not applicable | Controls may work; no gesture guarantee | `Ctrl/Cmd+Z`, `Ctrl/Cmd+Shift+Z`, `Ctrl+Y` |
|
||||
@@ -443,7 +443,7 @@ precise drawing, Resize, keyboard modifiers and double-click properties.
|
||||
|
||||
Important details:
|
||||
|
||||
- wheel, `−`/`+`, **Fit all**, the return arrow and a free-background
|
||||
- wheel, `−`/`+`, **Fit all**, the return arrow and a plan-surface
|
||||
double-click/tap in View or kiosk use a short smooth camera transition;
|
||||
rapid wheel events update one destination instead of building a queue;
|
||||
- pinch and pan remain directly under the fingers; reduced motion makes every
|
||||
@@ -454,8 +454,10 @@ Important details:
|
||||
- each space keeps its own local View viewport. Editor pan/zoom is a working
|
||||
view and does not replace it;
|
||||
- a hint appears when objects lie far away from the main plan;
|
||||
- above 1:1, a horizontal kiosk gesture pans instead of changing space. Any
|
||||
manual kiosk operation pauses auto-cycle for 60 seconds;
|
||||
- a kiosk space swipe starts only in the inner 48 CSS px of an edge with a
|
||||
neighbouring space, points inward and stays strongly horizontal. Elsewhere,
|
||||
at an unavailable edge and above 1:1 the gesture pans. Any manual kiosk
|
||||
operation pauses auto-cycle for 60 seconds;
|
||||
- `Shift` keeps positional grid snapping. For room walls it locks the current
|
||||
segment to the nearest 45° direction; in Background it creates a square or
|
||||
circle, allows independent axes for ordinary decor, unlocks furniture
|
||||
@@ -1714,9 +1716,9 @@ cycle: 30
|
||||
| Card header | Hidden |
|
||||
| Editors | Unavailable |
|
||||
| Height | `100dvh` |
|
||||
| Swipe | Cycles spaces at 1:1 |
|
||||
| Swipe | At 1:1, switches to an existing neighbour from the inner 48 px edge |
|
||||
| Pinch/pan | Zooms and moves the plan |
|
||||
| Double-tap free background | Fits all content |
|
||||
| Double-tap background or room | Fits all content without an intermediate room jump |
|
||||
| Hold empty space for 3 seconds | Opens per-display icon/text sizing |
|
||||
| `cycle` | Automatically advances; any interaction pauses it for 60 seconds |
|
||||
|
||||
|
||||
+4
-3
@@ -95,13 +95,14 @@ support. User documentation recommends desktop for creation and maintenance.
|
||||
|
||||
## View — display and device interaction only
|
||||
|
||||
Allowed: pan/zoom (wheel, pinch, buttons, double-click/tap on free background
|
||||
to Fit all), switching spaces, device tap
|
||||
Allowed: pan/zoom (wheel, pinch, buttons, double-click/tap on background, room
|
||||
fill or passive room label to Fit all), switching spaces, device tap
|
||||
(info / more-info / toggle per settings), long-press → info card, lock-badge tap
|
||||
→ door/lock info card (openings themselves are inert in View; the card carries an
|
||||
explicit Unlock/Lock button — the only way to operate a lock from the card;
|
||||
plan-icon taps never toggle locks),
|
||||
room-card link icon → HA area, clean room click/tap → room fit, room hover
|
||||
room-card link icon → HA area, clean single room click/tap → delayed room fit
|
||||
(350 ms decision window; keyboard activation is immediate), room hover
|
||||
highlight, hover tooltips (name, clean-floor area, temperature, signal).
|
||||
|
||||
Room, device, opening and shared-control hover is a mouse-only transient layer:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"fixture": "synthetic-only",
|
||||
"chromium": "151.0.7922.34",
|
||||
"oxipng": null,
|
||||
"sourceFingerprint": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceFingerprint": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"captureScriptSha256": "e2d14d89de793011615662af7c83c433768d4761f6cec7f0b3860c797494299e",
|
||||
"command": "npm run build && node demo/docs/capture.mjs",
|
||||
"scenarios": {
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "de1ee062cfffa375f66ff74509ad9ddbf6aabc5c5440733fe1ff0ea5a40e96c7"
|
||||
},
|
||||
"view-touch": {
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "f9dd5b54f86b814c00bc84149af84485f052b103cdcc9263b5c50bf5d32c69d6"
|
||||
},
|
||||
"space-create": {
|
||||
@@ -37,7 +37,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "aab4811bba3afa6cc849cb9d3989d126b8c327b69a2f8f853e55fc077e0e9a1b"
|
||||
},
|
||||
"room-contour-close": {
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "96a7e691ad8167103199a58b48b31ae2fc4f03e9616b65a5096dc8d0b9b51da0"
|
||||
},
|
||||
"plan-context-tray": {
|
||||
@@ -59,7 +59,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "ebb89bacfecd16178534e1f02fed8bb7540072a691ec4d4b14407c0b8ec4455e"
|
||||
},
|
||||
"device-editor": {
|
||||
@@ -70,7 +70,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "b40f843d7bcab902a6f697cc76735e6d3351a95443fe11ba761f0e39613120bc"
|
||||
},
|
||||
"device-display-preview": {
|
||||
@@ -81,7 +81,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "835e9f1b9446072a2caa2421b06a669eac9e5c0e3e6cf94106dd6cc2dd0d8e2b"
|
||||
},
|
||||
"background-editor": {
|
||||
@@ -92,7 +92,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "98a8c735165912b49b4254d2f975a61891e5737b0a9c16c627f08d60853fa8a7"
|
||||
},
|
||||
"room-card": {
|
||||
@@ -103,7 +103,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "d47988b03e6a078508b27b5612b6eafe8ecc33c891b589ddda1cc7c71f43b7c8"
|
||||
},
|
||||
"device-info": {
|
||||
@@ -114,7 +114,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "a10e6044376b087ba6800a31a229e4166a6797820104860f85a4659d89ee8045"
|
||||
},
|
||||
"pdf-export": {
|
||||
@@ -125,7 +125,7 @@
|
||||
},
|
||||
"theme": "light",
|
||||
"language": "en",
|
||||
"sourceSha256": "fa82f6c7697f8d2ec7ab4fc2c255b9f6e5a16b3636f5823f85d93819a85d93d7",
|
||||
"sourceSha256": "4955edd1dd8d30dad8ae5b4eb12ba23943124646e541ea491287d01550c94870",
|
||||
"imageSha256": "788a1ff32d6ceb51c9fb47ea37922ab15aeddf3fdcf3686bfcb0709634c41f0e"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user