Skip to content

Instantly share code, notes, and snippets.

@francoisgeorgy
Created November 26, 2024 12:49
Show Gist options
  • Select an option

  • Save francoisgeorgy/29d6cb334cdde9f3b3c91b87397f2c73 to your computer and use it in GitHub Desktop.

Select an option

Save francoisgeorgy/29d6cb334cdde9f3b3c91b87397f2c73 to your computer and use it in GitHub Desktop.
Smooth Full-Spectrum Color Picker with buttons
<div id="color-spectrum">
<canvas id="gradient-canvas"></canvas>
<div id="color-indicator"></div>
</div>
<div id="selected-color">Selected Color: <span id="color-display">N/A</span></div>
<div id="controls">
<button id="select-white">Select White</button>
<button id="select-black">Select Black</button>
<button id="select-random">Select Random</button>
</div>
const spectrum = document.getElementById('color-spectrum');
const canvas = document.getElementById('gradient-canvas');
const ctx = canvas.getContext('2d');
const indicator = document.getElementById('color-indicator');
const colorDisplay = document.getElementById('color-display');
// Buttons
const whiteButton = document.getElementById('select-white');
const blackButton = document.getElementById('select-black');
const randomButton = document.getElementById('select-random');
// Initialize canvas to cover the full viewport
function initializeCanvas() {
canvas.width = spectrum.offsetWidth;
canvas.height = spectrum.offsetHeight;
const width = canvas.width;
const height = canvas.height;
// Create an imageData object to directly manipulate pixels
const imageData = ctx.createImageData(width, height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const index = (y * width + x) * 4;
// Horizontal gradient: hue
const hue = (x / width) * 360;
const [r1, g1, b1] = hslToRgb(hue, 1, 0.5);
// Vertical gradient: brightness adjustment
const brightness = 1 - y / height;
// Apply brightness to the hue
imageData.data[index] = r1 * brightness; // Red
imageData.data[index + 1] = g1 * brightness; // Green
imageData.data[index + 2] = b1 * brightness; // Blue
imageData.data[index + 3] = 255; // Alpha
}
}
// Draw the processed image data onto the canvas
ctx.putImageData(imageData, 0, 0);
}
// Convert HSL to RGB
function hslToRgb(h, s, l) {
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
const m = l - c / 2;
let r = 0, g = 0, b = 0;
if (h >= 0 && h < 60) [r, g, b] = [c, x, 0];
else if (h >= 60 && h < 120) [r, g, b] = [x, c, 0];
else if (h >= 120 && h < 180) [r, g, b] = [0, c, x];
else if (h >= 180 && h < 240) [r, g, b] = [0, x, c];
else if (h >= 240 && h < 300) [r, g, b] = [x, 0, c];
else if (h >= 300 && h < 360) [r, g, b] = [c, 0, x];
return [
Math.round((r + m) * 255),
Math.round((g + m) * 255),
Math.round((b + m) * 255),
];
}
// Initialize the canvas on page load
initializeCanvas();
// Event listener to handle mouse movement
spectrum.addEventListener('mousemove', (event) => {
const { offsetX, offsetY } = event;
// Move the color indicator
indicator.style.left = `${offsetX}px`;
indicator.style.top = `${offsetY}px`;
// Get the pixel color under the cursor
const imageData = ctx.getImageData(offsetX, offsetY, 1, 1);
const [r, g, b] = imageData.data;
const color = `rgb(${r}, ${g}, ${b})`;
// Update the color display
updateSelectedColor(color);
});
// Button event handlers
whiteButton.addEventListener('click', () => {
updateSelectedColor('rgb(255, 255, 255)');
});
blackButton.addEventListener('click', () => {
updateSelectedColor('rgb(0, 0, 0)');
});
randomButton.addEventListener('click', () => {
const randomX = Math.floor(Math.random() * canvas.width);
const randomY = Math.floor(Math.random() * canvas.height);
const imageData = ctx.getImageData(randomX, randomY, 1, 1);
const [r, g, b] = imageData.data;
const color = `rgb(${r}, ${g}, ${b})`;
updateSelectedColor(color);
});
// Function to update the selected color
function updateSelectedColor(color) {
colorDisplay.textContent = color;
indicator.style.backgroundColor = color;
}
// Redraw canvas on window resize
window.addEventListener('resize', () => {
initializeCanvas();
});
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
cursor: crosshair;
}
#color-spectrum {
position: relative;
width: 100%;
height: 100%;
}
#gradient-canvas {
width: 100%;
height: 100%;
display: block;
}
#color-indicator {
position: absolute;
width: 20px;
height: 20px;
border: 2px solid white;
border-radius: 50%;
box-shadow: 0 0 5px black;
pointer-events: none;
transform: translate(-50%, -50%);
}
#controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10; /* Ensure buttons are above the canvas */
}
button {
padding: 10px 20px;
font-size: 14px;
font-family: Arial, sans-serif;
cursor: pointer;
border: 1px solid white; /* White border */
border-radius: 5px;
background-color: transparent; /* Transparent background */
color: white;
box-shadow: none;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
button:hover {
background-color: rgba(255, 255, 255, 0.2); /* Semi-opaque background */
box-shadow: 0 0 5px rgba(255, 255, 255, 0.5); /* Subtle glow on hover */
}
#selected-color {
position: absolute;
bottom: 80px;
left: 10px;
padding: 10px;
background: white;
border: 1px solid black;
font-family: Arial, sans-serif;
font-size: 14px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
z-index: 10; /* Ensure display is above the canvas */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment