89 lines
2.1 KiB
TypeScript
89 lines
2.1 KiB
TypeScript
import { useEffect, type RefObject } from "react";
|
|
import { oklch, formatHex, inGamut, type Oklch } from "culori";
|
|
|
|
interface UseOklchCanvasOptions {
|
|
useP3: boolean;
|
|
channel: "l" | "c" | "h";
|
|
max: number;
|
|
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
color: Oklch;
|
|
}
|
|
|
|
export function useOklchCanvas({ useP3, channel, max, canvasRef, color }: UseOklchCanvasOptions) {
|
|
useEffect(() => {
|
|
const colorGamut = useP3 ? "p3" : "rgb";
|
|
const canvas = canvasRef.current;
|
|
if (!canvas) return;
|
|
|
|
const ctx = canvas.getContext("2d")!;
|
|
const width = canvas.width;
|
|
const height = canvas.height;
|
|
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
const imageData = ctx.createImageData(width, height);
|
|
const data = imageData.data;
|
|
|
|
for (let x = 0; x < width; x++) {
|
|
let value: number;
|
|
const ratio = x / width;
|
|
|
|
switch (channel) {
|
|
case "l":
|
|
value = ratio;
|
|
break;
|
|
case "c":
|
|
value = ratio * max;
|
|
break;
|
|
case "h":
|
|
value = ratio * max;
|
|
break;
|
|
default:
|
|
value = ratio;
|
|
}
|
|
|
|
try {
|
|
const testColor = oklch({
|
|
mode: "oklch",
|
|
l: channel === "l" ? value : color.l,
|
|
c: channel === "c" ? value : color.c,
|
|
h: channel === "h" ? value : color.h
|
|
});
|
|
|
|
if (testColor && inGamut(colorGamut)(testColor)) {
|
|
const hex = formatHex(testColor);
|
|
const r = parseInt(hex.slice(1, 3), 16);
|
|
const g = parseInt(hex.slice(3, 5), 16);
|
|
const b = parseInt(hex.slice(5, 7), 16);
|
|
|
|
for (let y = 0; y < height; y++) {
|
|
const index = (y * width + x) * 4;
|
|
data[index] = r;
|
|
data[index + 1] = g;
|
|
data[index + 2] = b;
|
|
data[index + 3] = 255;
|
|
}
|
|
} else {
|
|
for (let y = 0; y < height; y++) {
|
|
const index = (y * width + x) * 4;
|
|
data[index] = 0;
|
|
data[index + 1] = 0;
|
|
data[index + 2] = 0;
|
|
data[index + 3] = 0;
|
|
}
|
|
}
|
|
} catch {
|
|
for (let y = 0; y < height; y++) {
|
|
const index = (y * width + x) * 4;
|
|
data[index] = 0;
|
|
data[index + 1] = 0;
|
|
data[index + 2] = 0;
|
|
data[index + 3] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.putImageData(imageData, 0, 0);
|
|
}, [channel, color.l, color.c, color.h, canvasRef, useP3]);
|
|
}
|