Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 40 additions & 41 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,48 @@ export type ColorWheelProps = Omit<ComponentProps<'div'>, 'color'> & {
color?: {hue: number, saturation: number, value: number};
defaultColor?: {hue: number, saturation: number, value: number};
onChange?: (colors: { hue: number; saturation: number; value: number }[]) => void;
value?: number
}

export const ColorWheel = ({ radius, harmony: harmonyName, color, defaultColor, onChange, ...props }: ColorWheelProps) => {
export const ColorWheel = ({ radius, harmony: harmonyName, color, defaultColor, onChange, value = 1.0, ...props }: ColorWheelProps) => {
const ref = useRef<HTMLCanvasElement>(null);
const [position, setPosition] = useState(defaultColor ? hsv2xy(defaultColor.hue, defaultColor.saturation, defaultColor.value, radius) : hsv2xy(0, 1, 1, radius));
const [position, setPosition] = useState(defaultColor ? hsv2xy(defaultColor.hue, defaultColor.saturation, defaultColor.value, radius) : hsv2xy(0, 1, value, radius));
const harmony = useMemo(() => harmonies[harmonyName], [harmonies, harmonyName]);

const drawCircle = useCallback((ctx: CanvasRenderingContext2D) => {
let image = ctx.createImageData(2 * radius, 2 * radius);
let data = image.data;

for (let x = -radius; x < radius; x++) {
for (let y = -radius; y < radius; y++) {
let [r, phi] = xy2polar(x, y);

let deg = rad2deg(phi);

// Figure out the starting index of this pixel in the image data array.
let rowLength = 2 * radius;
let adjustedX = x + radius; // convert x from [-50, 50] to [0, 100] (the coordinates of the image data array)
let adjustedY = y + radius; // convert y from [-50, 50] to [0, 100] (the coordinates of the image data array)
let pixelWidth = 4; // each pixel requires 4 slots in the data array
let index = (adjustedX + adjustedY * rowLength) * pixelWidth;

let hue = deg;
let saturation = r / radius;

let [red, green, blue] = hsv2rgb(hue, saturation, value);
let alpha = 255;

data[index] = red;
data[index + 1] = green;
data[index + 2] = blue;
data[index + 3] = alpha;
}
}

ctx.putImageData(image, 0, 0);
}, [radius, value]);


useEffect(() => {
if (!ref.current) return;
const ctx = ref.current.getContext('2d');
Expand All @@ -27,7 +62,7 @@ export const ColorWheel = ({ radius, harmony: harmonyName, color, defaultColor,
ctx.canvas.height = radius * 2

drawCircle(ctx);
}, []);
}, [drawCircle]);

useEffect(() => {
if (color) {
Expand Down Expand Up @@ -56,7 +91,6 @@ export const ColorWheel = ({ radius, harmony: harmonyName, color, defaultColor,

const hue = rad2deg(phi);
const saturation = r / radius;
const value = 1.0;

const colors = harmony.map(harmonyHue => {
let newHue = (hue + harmonyHue) % 360;
Expand All @@ -69,44 +103,9 @@ export const ColorWheel = ({ radius, harmony: harmonyName, color, defaultColor,
onChange?.([{ hue, saturation, value }, ...colors]);

return colors;
}, [position, harmony, polar2xy, onChange, xy2polar, rad2deg, radius]);


const drawCircle = useCallback((ctx: CanvasRenderingContext2D) => {
let image = ctx.createImageData(2 * radius, 2 * radius);
let data = image.data;

for (let x = -radius; x < radius; x++) {
for (let y = -radius; y < radius; y++) {
let [r, phi] = xy2polar(x, y);

let deg = rad2deg(phi);

// Figure out the starting index of this pixel in the image data array.
let rowLength = 2 * radius;
let adjustedX = x + radius; // convert x from [-50, 50] to [0, 100] (the coordinates of the image data array)
let adjustedY = y + radius; // convert y from [-50, 50] to [0, 100] (the coordinates of the image data array)
let pixelWidth = 4; // each pixel requires 4 slots in the data array
let index = (adjustedX + adjustedY * rowLength) * pixelWidth;

let hue = deg;
let saturation = r / radius;
let value = 1.0;

let [red, green, blue] = hsv2rgb(hue, saturation, value);
let alpha = 255;

data[index] = red;
data[index + 1] = green;
data[index + 2] = blue;
data[index + 3] = alpha;
}
}

ctx.putImageData(image, 0, 0);
}, [radius]);
}, [position, harmony, polar2xy, onChange, xy2polar, rad2deg, radius, value]);

const [r, g, b] = useMemo(() => xy2rgb(position.x, position.y, radius), [position, radius]);
const [r, g, b] = useMemo(() => xy2rgb(position.x, position.y, radius, value), [position, radius, value]);

return (
<div
Expand Down
3 changes: 1 addition & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,14 @@ export const hsv2rgb = (hue: number, saturation: number, value: number): [number
return [255 * r, 255 * g, 255 * b];
}

export const xy2rgb = (x: number, y: number, radius: number) => {
export const xy2rgb = (x: number, y: number, radius: number, value: number = 1.0) => {
x -= radius;
y -= radius;

const [r, phi] = xy2polar(x, y);

const hue = rad2deg(phi);
const saturation = r / radius;
const value = 1.0;

return hsv2rgb(hue, saturation, value);
}
Expand Down