From fae18295d285a72766001389f5aef531c04825bd Mon Sep 17 00:00:00 2001 From: acheronfail Date: Wed, 24 Jun 2026 21:35:59 +0930 Subject: [PATCH] app: performance improvements to trimming --- src/components/Details.svelte | 19 +---- src/components/Details.ts | 17 +++++ src/components/TrimSlider.svelte | 125 ++++++++++++++++++++++++------- 3 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 src/components/Details.ts diff --git a/src/components/Details.svelte b/src/components/Details.svelte index 833d0b1..4b97a9c 100644 --- a/src/components/Details.svelte +++ b/src/components/Details.svelte @@ -19,9 +19,10 @@ import settings, { type ZBatterySpecs } from '../lib/settings.svelte'; import Button from './Button.svelte'; import { ChartColours } from './Chart'; - import { empty, State } from '../lib/parse/types'; + import { empty } from '../lib/parse/types'; import { formatFloat, formatInt, formatTime } from '../lib/misc'; import { globalState } from '../lib/global.svelte'; + import { getStateColor } from './Details'; let { data = empty, stats, batterySpecs, units, hasAdcTelemetry }: Props = $props(); @@ -31,22 +32,6 @@ let cellVoltsLow = $derived(voltsPerCell && batterySpecs.cellMinVolt && voltsPerCell < batterySpecs.cellMinVolt); let formatSpeed = $derived((x: number) => (Number.isNaN(x) ? '??' : globalState.mapSpeed(x).toFixed(1))); - const getStateColor = (state: string): string | undefined => { - switch (state.toLowerCase()) { - case 'riding': - return 'yellowgreen'; - case State.Startup: - return 'grey'; - case State.StopHalf: - case State.Quickstop: - case State.Wheelslip: - return 'orange'; - case State.StopFull: - case State.StopAngle: - return 'red'; - } - }; - const itemClass = 'text-xs wide:text-sm relative bg-slate-900 text-slate-100 flex justify-around items-center h-full w-full'; diff --git a/src/components/Details.ts b/src/components/Details.ts new file mode 100644 index 0000000..7db9327 --- /dev/null +++ b/src/components/Details.ts @@ -0,0 +1,17 @@ +import { State } from '../lib/parse/types'; + +export const getStateColor = (state: string): string | undefined => { + switch (state.toLowerCase()) { + case 'riding': + return 'yellowgreen'; + case State.Startup: + return 'grey'; + case State.StopHalf: + case State.Quickstop: + case State.Wheelslip: + return 'orange'; + case State.StopFull: + case State.StopAngle: + return 'red'; + } +}; diff --git a/src/components/TrimSlider.svelte b/src/components/TrimSlider.svelte index fbbdcf5..7d82c2c 100644 --- a/src/components/TrimSlider.svelte +++ b/src/components/TrimSlider.svelte @@ -3,6 +3,7 @@ import { formatTime } from '../lib/misc'; import { globalState } from '../lib/global.svelte'; import { Units, type RowWithIndex } from '../lib/parse/types'; + import { getStateColor } from './Details'; let { open = $bindable(false), @@ -23,23 +24,40 @@ let container = $state(null); let dragging = $state<'start' | 'end' | null>(null); - let startIndex = $derived(Math.min(trimStart, trimEnd)); - let endIndex = $derived(Math.max(trimStart, trimEnd)); + // use draft values while the modal is open + let draftStart = $state(0); + let draftEnd = $state(0); + + $effect(() => { + if (open) { + draftStart = trimStart; + draftEnd = trimEnd; + } + }); + + let startIndex = $derived(Math.min(draftStart, draftEnd)); + let endIndex = $derived(Math.max(draftStart, draftEnd)); let startRow = $derived(rows[clampIndex(startIndex)]); let endRow = $derived(rows[clampIndex(endIndex)]); let distanceLabel = $derived(units === Units.Metric ? 'km' : 'mi'); + let speedLabel = $derived(units === Units.Metric ? 'km/h' : 'mph'); let formatDistance = (distance: number) => { const formatted = globalState.mapSpeed(distance); return Number.isNaN(formatted) ? '??' : formatted.toFixed(1); }; + let formatSpeed = (speed: number) => { + const formatted = globalState.mapSpeed(speed); + return Number.isNaN(formatted) ? '??' : formatted.toFixed(1); + }; + let formatVoltage = (v: number) => (Number.isFinite(v) ? v.toFixed(1) : '??'); function clampIndex(i: number) { return Math.max(0, Math.min(rowsLength - 1, i)); } function reset() { - trimStart = 0; - trimEnd = rowsLength ? rowsLength - 1 : 0; + draftStart = 0; + draftEnd = rowsLength ? rowsLength - 1 : 0; } function close() { @@ -55,8 +73,8 @@ } function selectNearestHandle(index: number) { - const startDist = Math.abs(index - trimStart); - const endDist = Math.abs(index - trimEnd); + const startDist = Math.abs(index - draftStart); + const endDist = Math.abs(index - draftEnd); return startDist <= endDist ? 'start' : 'end'; } @@ -64,9 +82,9 @@ const idx = posToIndex(e.clientX); const nearestHandle = selectNearestHandle(idx); if (nearestHandle === 'start') { - trimStart = idx; + draftStart = idx; } else { - trimEnd = idx; + draftEnd = idx; } dragging = nearestHandle; (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); @@ -76,28 +94,33 @@ e.stopPropagation(); (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); dragging = 'start'; - trimStart = posToIndex(e.clientX); + draftStart = posToIndex(e.clientX); } function onPointerDownEnd(e: PointerEvent) { e.stopPropagation(); (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); dragging = 'end'; - trimEnd = posToIndex(e.clientX); + draftEnd = posToIndex(e.clientX); } function onPointerMove(e: PointerEvent) { if (!dragging) return; const idx = posToIndex(e.clientX); if (dragging === 'start') { - trimStart = idx; + draftStart = idx; } else { - trimEnd = idx; + draftEnd = idx; } } function onPointerUp() { dragging = null; + + // apply draft values and perform trim on pointer up... if this is too expensive + // for mobile devices, we could do then when the trim modal closes instead. + trimStart = draftStart; + trimEnd = draftEnd; } function onKeyDown(e: KeyboardEvent) { @@ -140,16 +163,62 @@
-
Start
-
Log index: {startIndex + 1}
-
Time: {formatTime(startRow?.time ?? 0)}
-
Distance: {formatDistance(startRow?.distance ?? 0)} {distanceLabel}
+
Start
+
+
Log index
+
{startIndex + 1}
+
+
+
Ride Time
+
{formatTime(startRow?.time ?? 0)}
+
+
+
Distance
+
{formatDistance(startRow?.distance ?? 0)} {distanceLabel}
+
+
+
Speed
+
{formatSpeed(startRow?.speed ?? NaN)} {speedLabel}
+
+
+
Voltage
+
{formatVoltage(startRow?.voltage ?? NaN)} V
+
+
+
State
+
+ {(startRow?.state ?? 'unknown').toUpperCase()} +
+
-
End
-
Log index: {endIndex + 1}
-
Time: {formatTime(endRow?.time ?? 0)}
-
Distance: {formatDistance(endRow?.distance ?? 0)} {distanceLabel}
+
End
+
+
Log index
+
{endIndex + 1}
+
+
+
Ride Time
+
{formatTime(endRow?.time ?? 0)}
+
+
+
Distance
+
{formatDistance(endRow?.distance ?? 0)} {distanceLabel}
+
+
+
Speed
+
{formatSpeed(endRow?.speed ?? NaN)} {speedLabel}
+
+
+
Voltage
+
{formatVoltage(endRow?.voltage ?? NaN)} V
+
+
+
State
+
+ {(endRow?.state ?? 'unknown').toUpperCase()} +
+
@@ -162,23 +231,23 @@
@@ -189,7 +258,7 @@ aria-valuemax={rowsLength - 1} aria-valuenow={startIndex} class="absolute top-0 bottom-0 w-4 -translate-x-1/2 bg-white/90 rounded-full shadow-lg cursor-grab" - style="left: {(trimStart / Math.max(1, rowsLength - 1)) * 100}%" + style="left: {(draftStart / Math.max(1, rowsLength - 1)) * 100}%" onpointerdown={onPointerDownStart} tabindex="0" > @@ -202,7 +271,7 @@ aria-valuemax={rowsLength - 1} aria-valuenow={endIndex} class="absolute top-0 bottom-0 w-4 -translate-x-1/2 bg-white/90 rounded-full shadow-lg cursor-grab" - style="left: {(trimEnd / Math.max(1, rowsLength - 1)) * 100}%" + style="left: {(draftEnd / Math.max(1, rowsLength - 1)) * 100}%" onpointerdown={onPointerDownEnd} tabindex="0" >