From 377a1cc6c10b9439eb36d28c45dc6da0c62d6e73 Mon Sep 17 00:00:00 2001 From: Skyler Lehmkuhl Date: Sun, 5 Jan 2025 19:20:45 -0500 Subject: [PATCH] Add eyedropper tool --- src/assets/eyedropper.svg | 47 +++++++++++++++++++++++++++++++++++++++ src/main.js | 32 +++++++++++++++++++++++++- src/utils.js | 37 ++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/assets/eyedropper.svg diff --git a/src/assets/eyedropper.svg b/src/assets/eyedropper.svg new file mode 100644 index 0000000..12e0c59 --- /dev/null +++ b/src/assets/eyedropper.svg @@ -0,0 +1,47 @@ + + + + + + + + diff --git a/src/main.js b/src/main.js index 072a373..98817a7 100644 --- a/src/main.js +++ b/src/main.js @@ -39,6 +39,7 @@ import { rotateAroundPoint, getRotatedBoundingBox, rotateAroundPointIncremental, + rgbToHsv, } from "./utils.js"; import { backgroundColor, @@ -272,6 +273,16 @@ let tools = { }, }, }, + eyedropper: { + icon: "/assets/eyedropper.svg", + properties: { + dropperColor: { + type: "enum", + options: ["Fill color", "Stroke color"], + label: "Color" + } + } + } }; let mouseEvent; @@ -294,6 +305,7 @@ let context = { fillShape: false, strokeShape: true, fillGaps: 5, + dropperColor: "Fill color", dragging: false, selectionRect: undefined, selection: [], @@ -4687,6 +4699,21 @@ function stage() { // } } break; + case "eyedropper": + const ctx = stage.getContext("2d") + const imageData = ctx.getImageData(mouse.x, mouse.y, 1, 1); // Get pixel at (x, y) + const data = imageData.data; // The pixel data is in the `data` array + const hsv = rgbToHsv(...data) + if (context.dropperColor == "Fill color") { + for (let el of document.querySelectorAll(".color-field.fill")) { + el.setColor(hsv, 'ff') + } + } else { + for (let el of document.querySelectorAll(".color-field.stroke")) { + el.setColor(hsv, 'ff') + } + } + break; default: break; } @@ -5118,10 +5145,13 @@ function toolbar() { let strokeColor = document.createElement("div"); fillColor.className = "color-field"; strokeColor.className = "color-field"; + fillColor.classList.add("fill") + strokeColor.classList.add("stroke") fillColor.setColor = (hsv, alpha) => { + console.log(hsv) const rgb = hsvToRgb(...hsv) - console.log(alpha) const color = rgbToHex(rgb.r, rgb.g, rgb.b) + alpha + console.log(color) fillColor.style.setProperty("--color", color); fillColor.color = color; fillColor.hsv = hsv diff --git a/src/utils.js b/src/utils.js index 448fdc6..419c5fd 100644 --- a/src/utils.js +++ b/src/utils.js @@ -542,6 +542,42 @@ const rgbToHex = (r, g, b) => { return `#${(1 << 24 | (r << 16) | (g << 8) | b).toString(16).slice(1).toUpperCase()}`; }; +function rgbToHsv(r, g, b) { + r /= 255; + g /= 255; + b /= 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const delta = max - min; + let h = 0; + let s = 0; + let v = max; + + if (delta !== 0) { + s = delta / max; + + if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else { + h = 4 + (r - g) / delta; + } + + h *= 60; + + if (h < 0) { + h += 360; + } + } + + // Normalize hue to be between 0 and 1 + h /= 360; + + return [h, s, v]; // Return as array [h, s, v] +} + function clamp(n) { // Clamps a value between 0 and 1 return Math.min(Math.max(n,0),1) @@ -885,6 +921,7 @@ export { hexToHsl, hexToHsv, rgbToHex, + rgbToHsv, drawCheckerboardBackground, clamp, signedAngleBetweenVectors,