fix: 修复灯带顺序控件不能很好地被控制。
This commit is contained in:
parent
a905c98823
commit
09799cb2d5
@ -1,14 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
batch,
|
batch,
|
||||||
Component,
|
Component,
|
||||||
createContext,
|
|
||||||
createEffect,
|
createEffect,
|
||||||
createMemo,
|
createMemo,
|
||||||
createSignal,
|
createSignal,
|
||||||
For,
|
For,
|
||||||
Index,
|
Index,
|
||||||
JSX,
|
JSX,
|
||||||
on,
|
Match,
|
||||||
|
onCleanup,
|
||||||
|
onMount,
|
||||||
|
Switch,
|
||||||
untrack,
|
untrack,
|
||||||
useContext,
|
useContext,
|
||||||
} from 'solid-js';
|
} from 'solid-js';
|
||||||
@ -21,18 +23,24 @@ import background from '../assets/transparent-grid-background.svg?url';
|
|||||||
const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper }> = (
|
const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper }> = (
|
||||||
props,
|
props,
|
||||||
) => {
|
) => {
|
||||||
const [fullLeds, setFullLeds] = createSignal<Array<string | null>>([]);
|
const [leds, setLeds] = createSignal<Array<string | null>>([]);
|
||||||
const [dragging, setDragging] = createSignal<boolean>(false);
|
const [dragging, setDragging] = createSignal<boolean>(false);
|
||||||
const [dragStart, setDragStart] = createSignal<{ x: number; y: number } | null>(null);
|
const [dragStart, setDragStart] = createSignal<{ x: number; y: number } | null>(null);
|
||||||
const [dragCurr, setDragCurr] = createSignal<{ x: number; y: number } | null>(null);
|
const [dragCurr, setDragCurr] = createSignal<{ x: number; y: number } | null>(null);
|
||||||
const [dragStartIndex, setDragStartIndex] = createSignal<number>(0);
|
const [dragStartIndex, setDragStartIndex] = createSignal<number>(0);
|
||||||
const [cellWidth, setCellWidth] = createSignal<number>(0);
|
const [cellWidth, setCellWidth] = createSignal<number>(0);
|
||||||
const [, { setSelectedStripPart }] = useContext(LedStripConfigurationContext);
|
const [, { setSelectedStripPart }] = useContext(LedStripConfigurationContext);
|
||||||
|
const [rootWidth, setRootWidth] = createSignal<number>(0);
|
||||||
|
|
||||||
|
let root: HTMLDivElement;
|
||||||
|
|
||||||
const move = (targetStart: number) => {
|
const move = (targetStart: number) => {
|
||||||
if (targetStart === props.mapper.start) {
|
if (targetStart === props.mapper.start) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log(
|
||||||
|
`moving strip part ${props.strip.display_id} ${props.strip.border} from ${props.mapper.start} to ${targetStart}`,
|
||||||
|
);
|
||||||
invoke('move_strip_part', {
|
invoke('move_strip_part', {
|
||||||
displayId: props.strip.display_id,
|
displayId: props.strip.display_id,
|
||||||
border: props.strip.border,
|
border: props.strip.border,
|
||||||
@ -43,40 +51,67 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper
|
|||||||
// reset translateX on config updated
|
// reset translateX on config updated
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const indexDiff = props.mapper.start - dragStartIndex();
|
const indexDiff = props.mapper.start - dragStartIndex();
|
||||||
untrack(() => {
|
const start = untrack(dragStart);
|
||||||
if (!dragStart() || !dragCurr()) {
|
const curr = untrack(dragCurr);
|
||||||
return;
|
const _dragging = untrack(dragging);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (start === null || curr === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_dragging && indexDiff !== 0) {
|
||||||
const compensation = indexDiff * cellWidth();
|
const compensation = indexDiff * cellWidth();
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setDragStartIndex(props.mapper.start);
|
setDragStartIndex(props.mapper.start);
|
||||||
setDragStart({
|
setDragStart({
|
||||||
x: dragStart()!.x + compensation,
|
x: start.x + compensation,
|
||||||
y: dragCurr()!.y,
|
y: curr.y,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
batch(() => {
|
||||||
|
setDragStartIndex(props.mapper.start);
|
||||||
|
setDragStart(null);
|
||||||
|
setDragCurr(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onPointerDown = (ev: PointerEvent) => {
|
const onPointerDown = (ev: PointerEvent) => {
|
||||||
if (ev.button !== 0) {
|
if (ev.button !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDragging(true);
|
batch(() => {
|
||||||
setDragStart({ x: ev.clientX, y: ev.clientY });
|
setDragging(true);
|
||||||
setDragCurr({ x: ev.clientX, y: ev.clientY });
|
if (dragStart() === null) {
|
||||||
setDragStartIndex(props.mapper.start);
|
setDragStart({ x: ev.clientX, y: ev.clientY });
|
||||||
|
}
|
||||||
|
setDragCurr({ x: ev.clientX, y: ev.clientY });
|
||||||
|
setDragStartIndex(props.mapper.start);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerUp = () => (ev: PointerEvent) => {
|
const onPointerUp = (ev: PointerEvent) => {
|
||||||
if (ev.button !== 0) {
|
if (ev.button !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dragging() === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setDragging(false);
|
setDragging(false);
|
||||||
|
const diff = ev.clientX - dragStart()!.x;
|
||||||
|
const moved = Math.round(diff / cellWidth());
|
||||||
|
if (moved === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
move(props.mapper.start + moved);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerMove = (ev: PointerEvent) => {
|
const onPointerMove = (ev: PointerEvent) => {
|
||||||
|
if (dragging() === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedStripPart({
|
setSelectedStripPart({
|
||||||
displayId: props.strip.display_id,
|
displayId: props.strip.display_id,
|
||||||
border: props.strip.border,
|
border: props.strip.border,
|
||||||
@ -89,22 +124,26 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDragCurr({ x: ev.clientX, y: ev.clientY });
|
setDragCurr({ x: ev.clientX, y: ev.clientY });
|
||||||
|
|
||||||
const cellWidth =
|
|
||||||
(ev.currentTarget as HTMLDivElement).clientWidth / ledStripStore.totalLedCount;
|
|
||||||
const diff = ev.clientX - dragStart()!.x;
|
|
||||||
const moved = Math.round(diff / cellWidth);
|
|
||||||
if (moved === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setCellWidth(cellWidth);
|
|
||||||
move(props.mapper.start + moved);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerLeave = () => {
|
const onPointerLeave = () => {
|
||||||
setSelectedStripPart(null);
|
setSelectedStripPart(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
onMount(() => {
|
||||||
|
window.addEventListener('pointermove', onPointerMove);
|
||||||
|
window.addEventListener('pointerleave', onPointerLeave);
|
||||||
|
window.addEventListener('pointerup', onPointerUp);
|
||||||
|
});
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
window.removeEventListener('pointermove', onPointerMove);
|
||||||
|
window.removeEventListener('pointerleave', onPointerLeave);
|
||||||
|
window.removeEventListener('pointerup', onPointerUp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const reverse = () => {
|
const reverse = () => {
|
||||||
invoke('reverse_led_strip_part', {
|
invoke('reverse_led_strip_part', {
|
||||||
displayId: props.strip.display_id,
|
displayId: props.strip.display_id,
|
||||||
@ -112,59 +151,104 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper
|
|||||||
}).catch((err) => console.error(err));
|
}).catch((err) => console.error(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
// update fullLeds
|
const setColor = (fullIndex: number, colorsIndex: number, fullLeds: string[]) => {
|
||||||
createEffect(() => {
|
|
||||||
const fullLeds = new Array(ledStripStore.totalLedCount).fill(null);
|
|
||||||
const colors = ledStripStore.colors;
|
const colors = ledStripStore.colors;
|
||||||
|
let c1 = `rgb(${Math.floor(colors[colorsIndex * 3] * 0.8)}, ${Math.floor(
|
||||||
|
colors[colorsIndex * 3 + 1] * 0.8,
|
||||||
|
)}, ${Math.floor(colors[colorsIndex * 3 + 2] * 0.8)})`;
|
||||||
|
let c2 = `rgb(${Math.min(Math.floor(colors[colorsIndex * 3] * 1.2), 255)}, ${Math.min(
|
||||||
|
Math.floor(colors[colorsIndex * 3 + 1] * 1.2),
|
||||||
|
255,
|
||||||
|
)}, ${Math.min(Math.floor(colors[colorsIndex * 3 + 2] * 1.2), 255)})`;
|
||||||
|
|
||||||
const { start, end, pos } = props.mapper;
|
if (fullLeds.length <= fullIndex) {
|
||||||
const isForward = start < end;
|
console.error('out of range', fullIndex, fullLeds.length);
|
||||||
const step = isForward ? 1 : -1;
|
return;
|
||||||
for (let i = start, j = pos; i !== end; i += step, j++) {
|
|
||||||
let c1 = `rgb(${Math.floor(colors[j * 3] * 0.8)}, ${Math.floor(
|
|
||||||
colors[j * 3 + 1] * 0.8,
|
|
||||||
)}, ${Math.floor(colors[j * 3 + 2] * 0.8)})`;
|
|
||||||
let c2 = `rgb(${Math.min(Math.floor(colors[j * 3] * 1.2), 255)}, ${Math.min(
|
|
||||||
Math.floor(colors[j * 3 + 1] * 1.2),
|
|
||||||
255,
|
|
||||||
)}, ${Math.min(Math.floor(colors[j * 3 + 2] * 1.2), 255)})`;
|
|
||||||
|
|
||||||
fullLeds[i] = `linear-gradient(70deg, ${c1} 10%, ${c2})`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFullLeds(fullLeds);
|
fullLeds[fullIndex] = `linear-gradient(70deg, ${c1} 10%, ${c2})`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// update fullLeds
|
||||||
|
createEffect(() => {
|
||||||
|
const { start, end, pos } = props.mapper;
|
||||||
|
|
||||||
|
const leds = new Array(Math.abs(start - end)).fill(null);
|
||||||
|
|
||||||
|
if (start < end) {
|
||||||
|
for (let i = 0, j = pos; i < leds.length; i++, j++) {
|
||||||
|
setColor(i, j, leds);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = leds.length - 1, j = pos; i >= 0; i--, j++) {
|
||||||
|
setColor(i, j, leds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLeds(leds);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update rootWidth
|
||||||
|
createEffect(() => {
|
||||||
|
let observer: ResizeObserver;
|
||||||
|
onMount(() => {
|
||||||
|
observer = new ResizeObserver(() => {
|
||||||
|
setRootWidth(root.clientWidth);
|
||||||
|
});
|
||||||
|
observer.observe(root);
|
||||||
|
});
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
observer?.unobserve(root);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// update cellWidth
|
||||||
|
createEffect(() => {
|
||||||
|
const cellWidth = rootWidth() / ledStripStore.totalLedCount;
|
||||||
|
setCellWidth(cellWidth);
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = createMemo<JSX.CSSProperties>(() => {
|
const style = createMemo<JSX.CSSProperties>(() => {
|
||||||
return {
|
return {
|
||||||
transform: `translateX(${(dragCurr()?.x ?? 0) - (dragStart()?.x ?? 0)}px)`,
|
transform: `translateX(${
|
||||||
|
(dragCurr()?.x ?? 0) -
|
||||||
|
(dragStart()?.x ?? 0) +
|
||||||
|
cellWidth() * Math.min(props.mapper.start, props.mapper.end)
|
||||||
|
}px)`,
|
||||||
|
width: `${cellWidth() * leds().length}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="flex h-2 m-2 select-none cursor-ew-resize focus:cursor-ew-resize"
|
class="flex mx-2 select-none cursor-ew-resize focus:cursor-ew-resize"
|
||||||
style={style()}
|
|
||||||
onPointerMove={onPointerMove}
|
|
||||||
onPointerDown={onPointerDown}
|
onPointerDown={onPointerDown}
|
||||||
onPointerUp={onPointerUp}
|
|
||||||
onPointerLeave={onPointerLeave}
|
|
||||||
ondblclick={reverse}
|
ondblclick={reverse}
|
||||||
|
ref={root!}
|
||||||
>
|
>
|
||||||
<For each={fullLeds()}>
|
<div
|
||||||
{(it) => (
|
style={style()}
|
||||||
<div
|
class="rounded-full border border-white flex h-3"
|
||||||
class="flex-auto flex h-full w-full justify-center items-center relative"
|
classList={{
|
||||||
title={it ?? ''}
|
'bg-gradient-to-b from-yellow-500/60 to-orange-300/60': dragging(),
|
||||||
>
|
'bg-gradient-to-b from-white/50 to-stone-500/40': !dragging(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<For each={leds()}>
|
||||||
|
{(it) => (
|
||||||
<div
|
<div
|
||||||
class="absolute top-1/2 -translate-y-1/2 h-2.5 w-2.5 rounded-full ring-1 ring-stone-100"
|
class="flex-auto flex h-full w-full justify-center items-center relative"
|
||||||
classList={{ 'ring-stone-300/50': !it }}
|
title={it ?? ''}
|
||||||
style={{ background: it ?? 'transparent' }}
|
>
|
||||||
/>
|
<div
|
||||||
</div>
|
class="absolute top-1/2 -translate-y-1/2 h-2.5 w-2.5 rounded-full ring-1 ring-stone-100"
|
||||||
)}
|
classList={{ 'ring-stone-300/50': !it }}
|
||||||
</For>
|
style={{ background: it ?? 'transparent' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -220,7 +304,11 @@ export const LedStripPartsSorter: Component = () => {
|
|||||||
<SorterResult />
|
<SorterResult />
|
||||||
<Index each={ledStripStore.strips}>
|
<Index each={ledStripStore.strips}>
|
||||||
{(strip, index) => (
|
{(strip, index) => (
|
||||||
<SorterItem strip={strip()} mapper={ledStripStore.mappers[index]} />
|
<Switch>
|
||||||
|
<Match when={strip().len > 0}>
|
||||||
|
<SorterItem strip={strip()} mapper={ledStripStore.mappers[index]} />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
)}
|
)}
|
||||||
</Index>
|
</Index>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,15 @@ export const [ledStripStore, setLedStripStore] = createStore({
|
|||||||
colors: new Uint8ClampedArray(),
|
colors: new Uint8ClampedArray(),
|
||||||
sortedColors: new Uint8ClampedArray(),
|
sortedColors: new Uint8ClampedArray(),
|
||||||
get totalLedCount() {
|
get totalLedCount() {
|
||||||
return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end)));
|
return Math.max(
|
||||||
|
0,
|
||||||
|
...ledStripStore.mappers.map((m) => {
|
||||||
|
if (m.start === m.end) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return Math.max(m.start, m.end);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user