mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2025-12-11 13:26:16 +00:00
feat: color picker for hsv settings
This commit is contained in:
@@ -1,6 +1,85 @@
|
||||
import type { Action } from "svelte/action";
|
||||
import { changes, ChangeType, settings } from "$lib/undo-redo";
|
||||
|
||||
/**
|
||||
* https://gist.github.com/mjackson/5311256
|
||||
*/
|
||||
function rgbToHsv(r: number, g: number, b: number): [number, number, number] {
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h = 0;
|
||||
const v = max;
|
||||
|
||||
const d = max - min;
|
||||
const s = max == 0 ? 0 : d / max;
|
||||
|
||||
if (max == min) {
|
||||
h = 0; // achromatic
|
||||
} else {
|
||||
switch (max) {
|
||||
case r:
|
||||
h = (g - b) / d + (g < b ? 6 : 0);
|
||||
break;
|
||||
case g:
|
||||
h = (b - r) / d + 2;
|
||||
break;
|
||||
case b:
|
||||
h = (r - g) / d + 4;
|
||||
break;
|
||||
}
|
||||
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return [Math.floor(h * 0xffff), Math.floor(s * 0xff), Math.floor(v * 0xff)];
|
||||
}
|
||||
|
||||
/**
|
||||
* https://gist.github.com/mjackson/5311256
|
||||
*/
|
||||
function hsvToRgb(h: number, s: number, v: number): [number, number, number] {
|
||||
h /= 0xffff;
|
||||
s /= 0xff;
|
||||
v /= 0xff;
|
||||
|
||||
let r = 0;
|
||||
let g = 0;
|
||||
let b = 0;
|
||||
|
||||
const i = Math.floor(h * 6);
|
||||
const f = h * 6 - i;
|
||||
const p = v * (1 - s);
|
||||
const q = v * (1 - f * s);
|
||||
const t = v * (1 - (1 - f) * s);
|
||||
|
||||
switch (i % 6) {
|
||||
case 0:
|
||||
(r = v), (g = t), (b = p);
|
||||
break;
|
||||
case 1:
|
||||
(r = q), (g = v), (b = p);
|
||||
break;
|
||||
case 2:
|
||||
(r = p), (g = v), (b = t);
|
||||
break;
|
||||
case 3:
|
||||
(r = p), (g = q), (b = v);
|
||||
break;
|
||||
case 4:
|
||||
(r = t), (g = p), (b = v);
|
||||
break;
|
||||
case 5:
|
||||
(r = v), (g = p), (b = q);
|
||||
break;
|
||||
}
|
||||
|
||||
return [Math.floor(r * 0xff), Math.floor(g * 0xff), Math.floor(b * 0xff)];
|
||||
}
|
||||
|
||||
export const setting: Action<
|
||||
HTMLInputElement | HTMLSelectElement,
|
||||
{ id: number; inverse?: number; scale?: number }
|
||||
@@ -9,7 +88,12 @@ export const setting: Action<
|
||||
{ id, inverse, scale },
|
||||
) {
|
||||
node.setAttribute("disabled", "");
|
||||
const type = node.getAttribute("type") as "number" | "checkbox" | "range";
|
||||
const type = node.getAttribute("type") as
|
||||
| "number"
|
||||
| "checkbox"
|
||||
| "range"
|
||||
| "color";
|
||||
const isColor = type === "color";
|
||||
const isNumeric =
|
||||
type === "number" || type === "range" || node instanceof HTMLSelectElement;
|
||||
const min = node.hasAttribute("min")
|
||||
@@ -30,6 +114,13 @@ export const setting: Action<
|
||||
? scale * value
|
||||
: value
|
||||
).toString();
|
||||
} else if (isColor) {
|
||||
const rgb = hsvToRgb(
|
||||
settings[id]!.value,
|
||||
settings[id + 1]!.value,
|
||||
settings[id + 2]!.value,
|
||||
);
|
||||
node.value = `#${rgb.map((c) => c.toString(16).padStart(2, "0")).join("")}`;
|
||||
} else {
|
||||
node.checked = value !== 0;
|
||||
}
|
||||
@@ -58,6 +149,22 @@ export const setting: Action<
|
||||
? value / scale
|
||||
: value,
|
||||
);
|
||||
} else if (isColor) {
|
||||
const r = parseInt(node.value.slice(1, 3), 16);
|
||||
const g = parseInt(node.value.slice(3, 5), 16);
|
||||
const b = parseInt(node.value.slice(5, 7), 16);
|
||||
const hsv = rgbToHsv(r, g, b);
|
||||
changes.update((changes) => {
|
||||
changes.push(
|
||||
hsv.map((value, i) => ({
|
||||
type: ChangeType.Setting,
|
||||
id: id + i,
|
||||
setting: value,
|
||||
})),
|
||||
);
|
||||
return changes;
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
value = node.checked ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -117,43 +117,49 @@
|
||||
{/if}
|
||||
{#each category.items as item}
|
||||
{#if item.name !== "enable"}
|
||||
<label
|
||||
>{#if item.enum}
|
||||
<select use:setting={{ id: item.id }}>
|
||||
{#each item.enum as name, value}
|
||||
<option {value}>{titlecase(name)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if item.range[0] === 0 && item.range[1] === 1}
|
||||
<input type="checkbox" use:setting={{ id: item.id }} />
|
||||
{:else}
|
||||
<span class="unit"
|
||||
><input
|
||||
type="number"
|
||||
min={settingValue(item.range[0], item)}
|
||||
max={settingValue(item.range[1], item)}
|
||||
step={item.inverse !== undefined ||
|
||||
item.scale !== undefined ||
|
||||
item.step === undefined
|
||||
? undefined
|
||||
: settingValue(item.step, item)}
|
||||
use:setting={{
|
||||
id: item.id,
|
||||
inverse: item.inverse,
|
||||
scale: item.scale,
|
||||
}}
|
||||
/>{item.unit}</span
|
||||
>
|
||||
{/if}
|
||||
{#if item.description}
|
||||
<span
|
||||
>{titlecase(item.name)}
|
||||
<p>{item.description}</p></span
|
||||
>
|
||||
{:else}
|
||||
{titlecase(item.name)}
|
||||
{/if}
|
||||
</label>
|
||||
{#if item.unit === "H"}
|
||||
<label
|
||||
><input type="color" use:setting={{ id: item.id }} /> Color</label
|
||||
>
|
||||
{:else if item.unit !== "S" && item.unit !== "B"}
|
||||
<label
|
||||
>{#if item.enum}
|
||||
<select use:setting={{ id: item.id }}>
|
||||
{#each item.enum as name, value}
|
||||
<option {value}>{titlecase(name)}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if item.range[0] === 0 && item.range[1] === 1}
|
||||
<input type="checkbox" use:setting={{ id: item.id }} />
|
||||
{:else}
|
||||
<span class="unit"
|
||||
><input
|
||||
type="number"
|
||||
min={settingValue(item.range[0], item)}
|
||||
max={settingValue(item.range[1], item)}
|
||||
step={item.inverse !== undefined ||
|
||||
item.scale !== undefined ||
|
||||
item.step === undefined
|
||||
? undefined
|
||||
: settingValue(item.step, item)}
|
||||
use:setting={{
|
||||
id: item.id,
|
||||
inverse: item.inverse,
|
||||
scale: item.scale,
|
||||
}}
|
||||
/>{item.unit}</span
|
||||
>
|
||||
{/if}
|
||||
{#if item.description}
|
||||
<span
|
||||
>{titlecase(item.name)}
|
||||
<p>{item.description}</p></span
|
||||
>
|
||||
{:else}
|
||||
{titlecase(item.name)}
|
||||
{/if}
|
||||
</label>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
</fieldset>
|
||||
|
||||
Reference in New Issue
Block a user