mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-05 22:29:05 +00:00
Смена пространства выбрасывает слой, а не сверяет его поэлементно
#525 увёл оба списка сцены с `map()` на `repeat(items, (item) => item.id, …)`, чтобы Lit не переиспользовал узлы по позиции и не проигрывал анимацию двери, которой не было. Правка верная, но на плане с двумя сотнями маркеров она оказалась дорогой ровно там, где пользы не приносит: при смене пространства ключи не пересекаются вовсе, и `repeat` строит две карты ключей и обходит оба списка, чтобы затем всё равно выбросить всё и создать заново. Бисект по медиане `switchCycleMs` на `large-house`: 871,2 перед #525 → 953,5 после. На раннере эти 80 мс распадаются на шесть-девять дополнительных длинных задач, и `longTask.countP95` вышел за порог стабильного гейта. Теперь оба списка рендерятся как `keyed(space.id, repeat(…))`. Внешний ключ делает смену пространства: поддерево выбрасывается целиком, дифа нет. Внутренний остаётся, потому что состав списков едет и внутри пространства — у маркеров от призраков редактора и живого синка, у проёмов от записи с нерешённым хостом, которая живёт только в режиме plan, — а переходы на `.device-shell-frame`, `.op-leaf` и `.op-arc` никуда не делись. Замер после правки: `switchCycleMs` 889,1 против 936,0 на `main` и 871,2 до #525. Потолок карточки поднят на одну строку — на `import { keyed }`; переносить нечего, сам рендер не вырос ни на символ. Issue: #534 User-Visible: yes
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import { LitElement, html, svg, nothing, noChange, TemplateResult, PropertyValues, type PropertyDeclaration } from 'lit';
|
||||
import { cache as litCache } from 'lit/directives/cache.js';
|
||||
import { guard } from 'lit/directives/guard.js';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import './hp-dialog';
|
||||
import type { HpDialog } from './hp-dialog';
|
||||
@@ -11784,9 +11785,9 @@ export class HouseplanCard extends LitElement {
|
||||
view,
|
||||
(point) => this._scenePoint(point),
|
||||
)}
|
||||
${repeat(devs, (d) => d.id, (d) => this._renderDevice(
|
||||
${keyed(space.id, repeat(devs, (d) => d.id, (d) => this._renderDevice(
|
||||
d, view, showLqi, isoOverlays?.devices.get(d.id),
|
||||
))}
|
||||
)))}
|
||||
${this._renderVacuums(this._renderVacuumDevices, view, space.id)}
|
||||
${this._renderVacFit(view)}
|
||||
${this._renderOpeningLocks(view, isoOverlays?.locks)}
|
||||
@@ -12993,7 +12994,7 @@ export class HouseplanCard extends LitElement {
|
||||
const walls = this._spaceWalls;
|
||||
const openCuts = this._openCuts();
|
||||
const openingWallIndex = this._openingWallIndexFor(space, openCuts).value;
|
||||
return svg`<g class="openinglayer">${repeat(items, (o) => o.id, (o) => {
|
||||
return svg`<g class="openinglayer">${keyed(space.id, repeat(items, (o) => o.id, (o) => {
|
||||
if (o.orphanReason) return svg`<g class="opening orphan" data-hp="opening-orphan"
|
||||
data-id=${o.id} role="button" tabindex="0"
|
||||
aria-label=${this._t('opening.partition_orphan')}
|
||||
@@ -13045,7 +13046,7 @@ export class HouseplanCard extends LitElement {
|
||||
@pointerup=${(e: PointerEvent) => this._opPointerUp(e, o)}
|
||||
@pointercancel=${(e: PointerEvent) => this._opPointerUp(e, o)}></rect>
|
||||
</g>`;
|
||||
})}</g>`;
|
||||
}))}</g>`;
|
||||
}
|
||||
|
||||
/** Padlock badges for door-like openings with a lock entity. */
|
||||
|
||||
@@ -210,6 +210,9 @@ export const devicesStyles = css`
|
||||
background: transparent;
|
||||
box-shadow: var(--device-shell-shadow);
|
||||
/* no box-shadow here: cqw-sized, restarts on every container resize (#524) */
|
||||
/* these two are why the marker list keeps a per-item key under the
|
||||
space-level one (#525, #534): ghosts and live config sync shift
|
||||
positions inside one space — see plan.styles.ts for the full note */
|
||||
transition: border-color .15s, opacity .2s;
|
||||
pointer-events: auto;
|
||||
/* Normative production fallback: never add a per-marker backdrop blur. */
|
||||
|
||||
@@ -539,7 +539,17 @@ export const planStyles = css`
|
||||
Anything animated that is rendered from a list needs a key. The Glow
|
||||
spots learned it first (glow-scene.ts, opacity), the openings and the
|
||||
device markers (.device-shell-frame, box-shadow) with this issue.
|
||||
Witness: demo/smoke_space_switch_transitions.mjs. */
|
||||
|
||||
#534: the shape is now keyed(space.id, repeat(items, (o) => o.id, ...)),
|
||||
and BOTH halves are load-bearing. The outer key does the space switch:
|
||||
there the key sets are disjoint, so repeat would build two key maps and
|
||||
walk both lists only to throw everything away — 80 ms of it on a plan
|
||||
with two hundred markers. The inner key does everything inside one
|
||||
space, where the list composition still changes underneath: an opening
|
||||
with an unresolved host is listed ONLY in plan mode (_openingsR), so
|
||||
entering and leaving the editor shifts the positions of the rest.
|
||||
Removing either half brings the phantom animation back through a
|
||||
different door. Witnesses: demo/smoke_space_switch_transitions.mjs. */
|
||||
.op-leaf {
|
||||
transition: transform 0.6s ease;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user