876 lines
24 KiB
HTML
876 lines
24 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>颜色选择器</title>
|
|
<style>
|
|
body {
|
|
font-family:
|
|
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
"Helvetica Neue", Arial, sans-serif;
|
|
margin: 20px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
input {
|
|
font-family:
|
|
"SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas,
|
|
"Courier New", monospace;
|
|
}
|
|
|
|
.container {
|
|
max-width: 900px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
h1 {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.color-picker {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.main-content {
|
|
display: flex;
|
|
gap: 0;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.left-column {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 25px;
|
|
border-right: 1px solid #ccc;
|
|
}
|
|
|
|
.right-column {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 25px;
|
|
padding-top: 0;
|
|
}
|
|
|
|
.basic-color-controls {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.color-preview {
|
|
width: 100px;
|
|
height: 100px;
|
|
border: 1px solid #ccc;
|
|
margin-right: 15px;
|
|
}
|
|
|
|
.sliders {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
}
|
|
|
|
.slider-group {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.slider-label {
|
|
width: 30px;
|
|
margin-right: 10px;
|
|
font-weight: 500;
|
|
color: #555;
|
|
}
|
|
|
|
input[type="range"] {
|
|
width: 200px;
|
|
}
|
|
|
|
.color-values {
|
|
margin-top: 15px;
|
|
flex: 1;
|
|
padding-bottom: 15px;
|
|
border-bottom: 1px solid #ccc;
|
|
}
|
|
|
|
.color-values div {
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
/* 颜色历史记录样式 */
|
|
.color-history {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.history-color {
|
|
width: 25px;
|
|
height: 25px;
|
|
border: 1px solid #ccc;
|
|
cursor: pointer;
|
|
border-radius: 3px;
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.history-color:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
.history-section {
|
|
margin-top: 15px;
|
|
padding-top: 15px;
|
|
border-top: 1px solid #ccc;
|
|
}
|
|
|
|
.history-section h3 {
|
|
margin: 0 0 10px 0;
|
|
font-size: 0.9rem;
|
|
color: #666;
|
|
text-align: center;
|
|
}
|
|
|
|
/* 复制按钮样式 */
|
|
.copy-button {
|
|
background-color: #4caf50;
|
|
color: white;
|
|
padding: 5px 10px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin-left: 5px;
|
|
position: relative;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.copy-button:hover {
|
|
background-color: #3e8e41;
|
|
}
|
|
|
|
/* 复制成功对勾样式 */
|
|
.copy-success-icon {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: 16px;
|
|
display: none; /* 初始隐藏 */
|
|
}
|
|
|
|
/* 错误提示 */
|
|
.error-message {
|
|
color: red;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
/* 输入框样式 */
|
|
.input-container {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.input-container label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: 500;
|
|
color: #555;
|
|
}
|
|
|
|
.input-container input {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
margin-bottom: 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
box-sizing: border-box;
|
|
font-size: 14px;
|
|
transition: border-color 0.2s ease;
|
|
}
|
|
|
|
.input-container input:focus {
|
|
outline: none;
|
|
border-color: #4caf50;
|
|
}
|
|
|
|
.input-container button {
|
|
background-color: #4caf50;
|
|
color: white;
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin-bottom: 10px;
|
|
font-weight: 500;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.input-container button:hover {
|
|
background-color: #3e8e41;
|
|
}
|
|
|
|
#results {
|
|
margin-top: 20px;
|
|
border-top: 1px solid #ccc;
|
|
padding-top: 10px;
|
|
}
|
|
|
|
/* 高级颜色选择器样式 */
|
|
.advanced-color-picker {
|
|
display: flex;
|
|
margin-top: 15px;
|
|
padding-top: 15px;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.color-gradient {
|
|
width: 256px;
|
|
height: 256px;
|
|
background: linear-gradient(to right, white, red);
|
|
position: relative;
|
|
cursor: crosshair;
|
|
}
|
|
|
|
.color-gradient::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(to bottom, transparent, black);
|
|
}
|
|
|
|
.color-selector {
|
|
width: 12px;
|
|
height: 12px;
|
|
border: 1px solid black;
|
|
border-radius: 50%;
|
|
background-color: white;
|
|
position: absolute;
|
|
transform: translate(-50%, -50%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.hue-slider {
|
|
width: 24px;
|
|
height: 256px;
|
|
background: linear-gradient(
|
|
to bottom,
|
|
hsl(360, 100%, 50%),
|
|
hsl(300, 100%, 50%),
|
|
hsl(240, 100%, 50%),
|
|
hsl(180, 100%, 50%),
|
|
hsl(120, 100%, 50%),
|
|
hsl(60, 100%, 50%),
|
|
hsl(0, 100%, 50%)
|
|
);
|
|
margin-left: 20px;
|
|
cursor: row-resize;
|
|
position: relative;
|
|
}
|
|
.hue-selector {
|
|
width: 30px;
|
|
height: 4px;
|
|
background-color: transparent;
|
|
border: 3px solid black;
|
|
border-radius: 5px;
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.color-display {
|
|
width: 80px;
|
|
height: 80px;
|
|
border: 1px solid #ccc;
|
|
margin-left: 20px;
|
|
}
|
|
|
|
.color-info {
|
|
margin-left: 20px;
|
|
}
|
|
|
|
.copy-button.success {
|
|
background-color: #4caf50; /* Green */
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>颜色选择器</h1>
|
|
|
|
<div class="main-content">
|
|
<!-- 左列 -->
|
|
<div class="left-column">
|
|
<div class="basic-color-controls">
|
|
<div
|
|
id="color-preview"
|
|
class="color-preview"
|
|
style="background-color: #ffffff"
|
|
></div>
|
|
|
|
<div class="sliders">
|
|
<div class="slider-group">
|
|
<label for="red" class="slider-label">R:</label>
|
|
<input type="range" id="red" min="0" max="255" value="255" />
|
|
<span id="red-value">255</span>
|
|
</div>
|
|
<div class="slider-group">
|
|
<label for="green" class="slider-label">G:</label>
|
|
<input type="range" id="green" min="0" max="255" value="255" />
|
|
<span id="green-value">255</span>
|
|
</div>
|
|
<div class="slider-group">
|
|
<label for="blue" class="slider-label">B:</label>
|
|
<input type="range" id="blue" min="0" max="255" value="255" />
|
|
<span id="blue-value">255</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 高级颜色选择器 -->
|
|
<div class="advanced-color-picker">
|
|
<div class="color-gradient">
|
|
<div class="color-selector"></div>
|
|
</div>
|
|
<div class="hue-slider">
|
|
<div class="hue-selector"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 颜色历史记录 -->
|
|
<div class="history-section">
|
|
<h3>颜色历史记录</h3>
|
|
<div class="color-history"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 右列 -->
|
|
<div class="right-column">
|
|
<div class="color-values">
|
|
<div>
|
|
RGB: <span id="rgb-value">rgb(255, 255, 255)</span>
|
|
<button class="copy-button" data-target="rgb">
|
|
复制 <span class="copy-success-icon">✔</span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
Hex: <span id="hex-value">#ffffff</span>
|
|
<button class="copy-button" data-target="hex">
|
|
复制 <span class="copy-success-icon">✔</span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
HSL: <span id="hsl-value">hsl(0, 0%, 100%)</span>
|
|
<button class="copy-button" data-target="hsl">
|
|
复制 <span class="copy-success-icon">✔</span>
|
|
</button>
|
|
</div>
|
|
<div>
|
|
HSV: <span id="hsv-value">hsv(0, 0%, 100%)</span>
|
|
<button class="copy-button" data-target="hsv">
|
|
复制 <span class="copy-success-icon">✔</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 输入框 -->
|
|
<div class="input-container">
|
|
<label for="rgb-r">红色 (R):</label>
|
|
<input
|
|
type="number"
|
|
id="rgb-r"
|
|
min="0"
|
|
max="255"
|
|
value="255"
|
|
required
|
|
/>
|
|
|
|
<label for="rgb-g">绿色 (G):</label>
|
|
<input
|
|
type="number"
|
|
id="rgb-g"
|
|
min="0"
|
|
max="255"
|
|
value="255"
|
|
required
|
|
/>
|
|
|
|
<label for="rgb-b">蓝色 (B):</label>
|
|
<input
|
|
type="number"
|
|
id="rgb-b"
|
|
min="0"
|
|
max="255"
|
|
value="255"
|
|
required
|
|
/>
|
|
|
|
<button id="convert-rgb-button">转换 RGB</button>
|
|
|
|
<label for="hex-input">十六进制颜色值:</label>
|
|
<input type="text" id="hex-input" placeholder="例如: #ffffff" />
|
|
|
|
<button id="convert-hex-button">转换 Hex</button>
|
|
|
|
<div id="input-error" class="error-message"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const colorPreview = document.getElementById("color-preview");
|
|
const redSlider = document.getElementById("red");
|
|
const greenSlider = document.getElementById("green");
|
|
const blueSlider = document.getElementById("blue");
|
|
const redValue = document.getElementById("red-value");
|
|
const greenValue = document.getElementById("green-value");
|
|
const blueValue = document.getElementById("blue-value");
|
|
const rgbValue = document.getElementById("rgb-value");
|
|
const hexValue = document.getElementById("hex-value");
|
|
const hslValue = document.getElementById("hsl-value");
|
|
const hsvValue = document.getElementById("hsv-value");
|
|
const copyButtons = document.querySelectorAll(".copy-button");
|
|
const colorHistory = document.querySelector(".color-history");
|
|
|
|
const rgbRInput = document.getElementById("rgb-r");
|
|
const rgbGInput = document.getElementById("rgb-g");
|
|
const rgbBInput = document.getElementById("rgb-b");
|
|
const hexInput = document.getElementById("hex-input");
|
|
const convertRgbButton = document.getElementById("convert-rgb-button");
|
|
const convertHexButton = document.getElementById("convert-hex-button");
|
|
const inputError = document.getElementById("input-error");
|
|
|
|
// 高级颜色选择器元素
|
|
const colorGradient = document.querySelector(".color-gradient");
|
|
const colorSelector = document.querySelector(".color-selector");
|
|
const hueSlider = document.querySelector(".hue-slider");
|
|
const hueSelector = document.querySelector(".hue-selector");
|
|
|
|
let historyColors = []; // 存储颜色历史记录
|
|
let currentColor = { h: 0, s: 100, v: 100 }; // 存储当前颜色 HSV 值
|
|
|
|
// 更新颜色历史记录
|
|
function updateHistory(color) {
|
|
if (!historyColors.includes(color)) {
|
|
historyColors.unshift(color); // 添加到数组开头
|
|
if (historyColors.length > 10) {
|
|
historyColors.pop(); // 限制历史记录数量
|
|
}
|
|
renderHistory();
|
|
}
|
|
}
|
|
|
|
// 渲染颜色历史记录
|
|
function renderHistory() {
|
|
colorHistory.innerHTML = ""; // 清空历史记录
|
|
historyColors.forEach((color) => {
|
|
const historyDiv = document.createElement("div");
|
|
historyDiv.classList.add("history-color");
|
|
historyDiv.style.backgroundColor = color;
|
|
historyDiv.addEventListener("click", () => {
|
|
setColors(color);
|
|
});
|
|
colorHistory.appendChild(historyDiv);
|
|
});
|
|
}
|
|
|
|
// 将颜色值设置到所有控件
|
|
function setColors(color) {
|
|
const rgb = hexToRgb(color);
|
|
if (rgb) {
|
|
redSlider.value = rgb.r;
|
|
greenSlider.value = rgb.g;
|
|
blueSlider.value = rgb.b;
|
|
|
|
rgbRInput.value = rgb.r;
|
|
rgbGInput.value = rgb.g;
|
|
rgbBInput.value = rgb.b;
|
|
|
|
hexInput.value = color;
|
|
|
|
updateColor();
|
|
}
|
|
}
|
|
|
|
function updateColor() {
|
|
const red = parseInt(redSlider.value);
|
|
const green = parseInt(greenSlider.value);
|
|
const blue = parseInt(blueSlider.value);
|
|
|
|
redValue.textContent = red;
|
|
greenValue.textContent = green;
|
|
blueValue.textContent = blue;
|
|
|
|
rgbRInput.value = red;
|
|
rgbGInput.value = green;
|
|
rgbBInput.value = blue;
|
|
|
|
const rgb = `rgb(${red}, ${green}, ${blue})`;
|
|
rgbValue.textContent = rgb;
|
|
|
|
const hex = rgbToHex(red, green, blue);
|
|
hexValue.textContent = hex;
|
|
hexInput.value = hex;
|
|
|
|
const hsl = rgbToHsl(red, green, blue);
|
|
hslValue.textContent = `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
|
|
|
|
const hsv = rgbToHsv(red, green, blue);
|
|
hsvValue.textContent = `hsv(${hsv.h}, ${hsv.s}%, ${hsv.v}%)`;
|
|
|
|
colorPreview.style.backgroundColor = hex;
|
|
}
|
|
|
|
function rgbToHex(r, g, b) {
|
|
return (
|
|
"#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
|
);
|
|
}
|
|
|
|
function hexToRgb(hex) {
|
|
// Expand shorthand form (e.g. "03F") to full form (e.g. "03F")
|
|
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
hex = hex.replace(shorthandRegex, function (m, r, g, b) {
|
|
return r + r + g + g + b + b;
|
|
});
|
|
|
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
return result
|
|
? {
|
|
r: parseInt(result[1], 16),
|
|
g: parseInt(result[2], 16),
|
|
b: parseInt(result[3], 16),
|
|
}
|
|
: null;
|
|
}
|
|
|
|
function rgbToHsl(r, g, b) {
|
|
(r /= 255), (g /= 255), (b /= 255);
|
|
const max = Math.max(r, g, b),
|
|
min = Math.min(r, g, b);
|
|
let h,
|
|
s,
|
|
l = (max + min) / 2;
|
|
|
|
if (max == min) {
|
|
h = s = 0; // achromatic
|
|
} else {
|
|
const d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
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 {
|
|
h: Math.round(h * 360),
|
|
s: Math.round(s * 100),
|
|
l: Math.round(l * 100),
|
|
};
|
|
}
|
|
|
|
function rgbToHsv(r, g, b) {
|
|
r = r / 255;
|
|
g = g / 255;
|
|
b = b / 255;
|
|
|
|
let max = Math.max(r, g, b);
|
|
let min = Math.min(r, g, b);
|
|
let h,
|
|
s,
|
|
v = max;
|
|
|
|
let d = max - min;
|
|
s = max === 0 ? 0 : d / max;
|
|
|
|
if (max === min) {
|
|
h = 0; // achromatic
|
|
} else {
|
|
if (max === r) {
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
} else if (max === g) {
|
|
h = (b - r) / d + 2;
|
|
} else if (max === b) {
|
|
h = (r - g) / d + 4;
|
|
}
|
|
h /= 6;
|
|
}
|
|
|
|
return {
|
|
h: Math.round(h * 360),
|
|
s: Math.round(s * 100),
|
|
v: Math.round(v * 100),
|
|
};
|
|
}
|
|
|
|
function hsvToRgb(h, s, v) {
|
|
s = s / 100;
|
|
v = v / 100;
|
|
|
|
let r, g, b;
|
|
|
|
let i = Math.floor(h / 60);
|
|
let f = h / 60 - i;
|
|
let p = v * (1 - s);
|
|
let q = v * (1 - s * f);
|
|
let t = v * (1 - s * (1 - f));
|
|
|
|
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 {
|
|
r: Math.round(r * 255),
|
|
g: Math.round(g * 255),
|
|
b: Math.round(b * 255),
|
|
};
|
|
}
|
|
|
|
redSlider.addEventListener("input", updateColor);
|
|
greenSlider.addEventListener("input", updateColor);
|
|
blueSlider.addEventListener("input", updateColor);
|
|
|
|
// 转换 RGB
|
|
convertRgbButton.addEventListener("click", function () {
|
|
const r = parseInt(rgbRInput.value);
|
|
const g = parseInt(rgbGInput.value);
|
|
const b = parseInt(rgbBInput.value);
|
|
|
|
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
|
|
const color = rgbToHex(r, g, b);
|
|
inputError.textContent = "";
|
|
setColors(color);
|
|
updateHistory(color); // 在这里添加当前颜色到历史记录
|
|
} else {
|
|
inputError.textContent = "无效的 RGB 颜色值";
|
|
}
|
|
});
|
|
|
|
// 转换 Hex
|
|
convertHexButton.addEventListener("click", function () {
|
|
const input = hexInput.value.trim();
|
|
if (/^#([0-9a-f]{3}){1,2}$/i.test(input)) {
|
|
inputError.textContent = "";
|
|
setColors(input);
|
|
updateHistory(input); // 在这里添加当前颜色到历史记录
|
|
} else {
|
|
inputError.textContent = "无效的 Hex 颜色值";
|
|
}
|
|
});
|
|
|
|
// 复制到剪贴板 (兼容方案)
|
|
function copyToClipboard(text) {
|
|
const textarea = document.createElement("textarea");
|
|
textarea.value = text;
|
|
textarea.style.position = "fixed"; // 防止页面滚动
|
|
document.body.appendChild(textarea);
|
|
textarea.focus();
|
|
textarea.select();
|
|
|
|
try {
|
|
const successful = document.execCommand("copy");
|
|
if (!successful) {
|
|
console.error("复制失败,请手动复制");
|
|
alert("复制失败,请手动复制");
|
|
}
|
|
} catch (err) {
|
|
console.error("复制失败: ", err);
|
|
alert("复制失败,请手动复制");
|
|
}
|
|
|
|
document.body.removeChild(textarea);
|
|
}
|
|
|
|
// 显示复制成功消息
|
|
function showCopySuccess(button) {
|
|
const successIcon = button.querySelector(".copy-success-icon");
|
|
const originalText = button.textContent;
|
|
|
|
button.classList.add("success"); // Add success class
|
|
button.textContent = "完成"; // Change button text
|
|
|
|
setTimeout(() => {
|
|
button.textContent = "复制"; // Restore original text
|
|
button.classList.remove("success"); // Remove success class
|
|
}, 1500);
|
|
}
|
|
|
|
// 复制到剪贴板
|
|
copyButtons.forEach((button) => {
|
|
button.addEventListener("click", function () {
|
|
const target = this.dataset.target;
|
|
let text = "";
|
|
let colorToSave = hexValue.textContent; // Always save hex value
|
|
|
|
switch (target) {
|
|
case "rgb":
|
|
text = rgbValue.textContent;
|
|
break;
|
|
case "hex":
|
|
text = hexValue.textContent;
|
|
break;
|
|
case "hsl":
|
|
text = hslValue.textContent;
|
|
break;
|
|
case "hsv":
|
|
text = hsvValue.textContent;
|
|
break;
|
|
}
|
|
|
|
copyToClipboard(text);
|
|
updateHistory(colorToSave);
|
|
showCopySuccess(this); // Pass the button element
|
|
});
|
|
});
|
|
|
|
// 高级颜色选择器事件
|
|
colorGradient.addEventListener("mousedown", (e) => {
|
|
updateGradientColor(e);
|
|
|
|
function updateGradientColor(e) {
|
|
const rect = colorGradient.getBoundingClientRect();
|
|
const x = e.clientX - rect.left;
|
|
const y = e.clientY - rect.top;
|
|
|
|
// 限制选择器在渐变区域内
|
|
const boundedX = Math.max(0, Math.min(x, rect.width));
|
|
const boundedY = Math.max(0, Math.min(y, rect.height));
|
|
|
|
colorSelector.style.left = boundedX + "px";
|
|
colorSelector.style.top = boundedY + "px";
|
|
|
|
// 计算饱和度和亮度
|
|
const saturation = (boundedX / rect.width) * 100;
|
|
const value = (1 - boundedY / rect.height) * 100;
|
|
|
|
currentColor.s = saturation;
|
|
currentColor.v = value;
|
|
|
|
updateFromAdvancedColor(); // Update everything from advanced color
|
|
}
|
|
|
|
function stopUpdate() {
|
|
document.removeEventListener("mousemove", updateGradientColor);
|
|
document.removeEventListener("mouseup", stopUpdate);
|
|
}
|
|
|
|
document.addEventListener("mousemove", updateGradientColor);
|
|
document.addEventListener("mouseup", stopUpdate);
|
|
});
|
|
|
|
hueSlider.addEventListener("mousedown", (e) => {
|
|
updateHue(e);
|
|
|
|
function updateHue(e) {
|
|
const rect = hueSlider.getBoundingClientRect();
|
|
const y = e.clientY - rect.top;
|
|
|
|
// 限制选择器在渐变区域内
|
|
const boundedY = Math.max(0, Math.min(y, rect.height));
|
|
|
|
hueSelector.style.top = boundedY + "px";
|
|
|
|
// 计算色相
|
|
const hue = ((1 - boundedY / rect.height) * 360) % 360;
|
|
currentColor.h = hue;
|
|
|
|
updateFromAdvancedColor(); // Update everything from advanced color
|
|
}
|
|
|
|
function stopUpdate() {
|
|
document.removeEventListener("mousemove", updateHue);
|
|
document.removeEventListener("mouseup", stopUpdate);
|
|
}
|
|
|
|
document.addEventListener("mousemove", updateHue);
|
|
document.addEventListener("mouseup", stopUpdate);
|
|
});
|
|
|
|
// 更新所有颜色控件
|
|
function updateFromAdvancedColor() {
|
|
const rgb = hsvToRgb(currentColor.h, currentColor.s, currentColor.v);
|
|
|
|
// Update basic sliders
|
|
redSlider.value = rgb.r;
|
|
greenSlider.value = rgb.g;
|
|
blueSlider.value = rgb.b;
|
|
|
|
// Update input boxes
|
|
rgbRInput.value = rgb.r;
|
|
rgbGInput.value = rgb.g;
|
|
rgbBInput.value = rgb.b;
|
|
|
|
// Call the main updateColor function to update everything else
|
|
updateColor();
|
|
|
|
// Update gradient background
|
|
colorGradient.style.background = `linear-gradient(to right, white, hsl(${currentColor.h}, 100%, 50%))`;
|
|
}
|
|
|
|
renderHistory(); // 初始化颜色历史记录
|
|
updateColor(); // 初始化颜色显示
|
|
</script>
|
|
</body>
|
|
</html>
|