修色板被裁切 + 让人看得出能选全色域

用户两个反馈:
① 色板直接被挡住了一半
② 它不是完全的颜色,好像只有蓝色

① 根因:色板用 position:absolute,而它在
   <details class="tax-form-box"> 里面,那个容器有 overflow:hidden。
   色相条正好在面板最底部 —— 被裁掉了,所以看不见。
   改 position:fixed,直接相对视口定位,任何祖先 overflow 都管不到。
   位置改由 JS 按触发按钮坐标算,并处理三种情况:
     右边超出→左移;下方不够→翻上方;都放不下→贴视口底边
   另加 resize/scroll 监听,避免滚动后色板漂走。

② 不是 bug,是 ① 的连带后果:
   HSV 色板本来就只显示当前色相的深浅变化,换颜色靠色相条。
   色相条被裁掉后,用户只看到一片蓝,自然以为只有蓝色。
   修好裁切后已实测 6 个色相各不相同:
     0°红 60°黄 120°绿 180°青 240°蓝 300°品红
   同时把色相条从 14px 加高到 18px 并加内描边,
   让它一眼就能认出是光谱条,而不是一条灰线。

验证(用真实后台容器结构复刻测试页,details 带 overflow:hidden):
  面板四边 elementFromPoint 都能命中自己 → 未被任何容器裁切
  色相条可见、高 18px;面板总高 367px
  滚到页面底部再开面板仍完整可见
  6 个色相取样互不相同,色板能覆盖全色域
  实际取色 #0ce779 正常
  4 个路由 200
This commit is contained in:
2026-11-04 19:01:04 +08:00
parent 338a35cacd
commit a2a3306169
3 changed files with 68 additions and 19 deletions
+48 -7
View File
@@ -847,17 +847,58 @@
// ---- 打开 / 关闭 ---- // ---- 打开 / 关闭 ----
function openPanel() { function openPanel() {
panel.hidden = false; panel.hidden = false;
// 面板可能超出视口底部,翻到上方
var r = trigger.getBoundingClientRect();
panel.classList.remove('cp-up');
var need = panel.offsetHeight + 8;
if (r.bottom + need > window.innerHeight && r.top > need) {
panel.classList.add('cp-up');
}
drawSV(); moveCursor(); drawSV(); moveCursor();
positionPanel();
} }
/* 色板是 position:fixed,位置必须自己算。
*
* 🔴 为什么不用 absolute 贴按钮:后台这个表单在
* <details class="tax-form-box"> 里,那个容器有 overflow:hidden,
* absolute 会被裁掉下半部分 —— 色相条正好在底部,连带看不见,
* 用户就以为"色板只有蓝色"。fixed 不受任何祖先 overflow 影响。
*
* 定位规则:
* ① 默认贴在触发按钮下方,左边缘对齐按钮
* ② 右边超出视口就往左挪
* ③ 下方空间不够就翻到按钮上方
* ④ 都放不下(屏幕太矮)就贴住视口上下边,保证能看全 */
function positionPanel() {
if (panel.hidden) { return; }
var r = trigger.getBoundingClientRect();
var pw = panel.offsetWidth;
var ph = panel.offsetHeight;
var vw = window.innerWidth;
var vh = window.innerHeight;
var gap = 8;
var margin = 10;
// 水平:默认左对齐按钮
var left = r.left;
if (left + pw + margin > vw) { left = vw - pw - margin; }
if (left < margin) { left = margin; }
// 垂直:优先下方
var top = r.bottom + gap;
if (top + ph + margin > vh) {
var above = r.top - gap - ph;
if (above >= margin) {
top = above; // 放上面
} else {
top = Math.max(margin, vh - ph - margin); // 都放不下:贴住底部
}
}
panel.style.left = Math.round(left) + 'px';
panel.style.top = Math.round(top) + 'px';
}
function closePanel() { panel.hidden = true; } function closePanel() { panel.hidden = true; }
// 窗口尺寸变化 / 滚动时重新定位,避免色板"漂走"
window.addEventListener('resize', positionPanel);
window.addEventListener('scroll', positionPanel, true);
trigger.addEventListener('click', function (e) { trigger.addEventListener('click', function (e) {
e.stopPropagation(); e.stopPropagation();
if (panel.hidden) { openPanel(); } else { closePanel(); } if (panel.hidden) { openPanel(); } else { closePanel(); }
+19 -11
View File
@@ -389,7 +389,12 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
/* ---------- 自绘色板 ---------- /* ---------- 自绘色板 ----------
* 用户 2026-11-04:「需要一个完整的色板,右侧那些默认颜色就不要了。」 * 用户 2026-11-04:「需要一个完整的色板,右侧那些默认颜色就不要了。」
* ⇒ 面板做大(宽 372),色板画布 340×200;右侧预设圆点已删除。 */ *
* 🔴 色板必须 position:fixed —— 后台这个表单在 <details class="tax-form-box">
* 里面,而它有 overflow:hidden。用 absolute 会被裁掉下半部分,
* 连带色相条一起看不见,用户就以为"只有蓝色"(只能看到当前色相)。
* fixed 直接相对视口定位,任何祖先的 overflow 都管不到它。
* 位置由 JS 按触发按钮的坐标算出来写进 style。 */
.color-trigger{ .color-trigger{
display:inline-flex; align-items:center; gap:9px; display:inline-flex; align-items:center; gap:9px;
padding:5px 12px 5px 6px; height:34px; padding:5px 12px 5px 6px; height:34px;
@@ -408,13 +413,12 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
.color-trigger-caret{font-size:10px; color:var(--muted); margin-left:1px} .color-trigger-caret{font-size:10px; color:var(--muted); margin-left:1px}
.cp{ .cp{
position:absolute; top:calc(100% + 8px); left:0; z-index:80; position:fixed; z-index:9999;
width:372px; padding:14px; width:372px; padding:14px;
background:var(--card); border:1px solid var(--line-strong); background:var(--card); border:1px solid var(--line-strong);
border-radius:14px; box-shadow:0 12px 36px rgba(16,24,40,.20); border-radius:14px; box-shadow:0 12px 36px rgba(16,24,40,.24);
user-select:none; box-sizing:border-box; user-select:none; box-sizing:border-box;
} }
.cp.cp-up{top:auto; bottom:calc(100% + 8px)}
.cp-hd{ .cp-hd{
font-size:12.5px; font-weight:700; color:var(--muted); font-size:12.5px; font-weight:700; color:var(--muted);
letter-spacing:.3px; margin:0 0 10px; letter-spacing:.3px; margin:0 0 10px;
@@ -430,11 +434,15 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
border:2px solid #fff; border-radius:50%; pointer-events:none; border:2px solid #fff; border-radius:50%; pointer-events:none;
box-shadow:0 0 0 1px rgba(0,0,0,.4), 0 1px 3px rgba(0,0,0,.3); box-shadow:0 0 0 1px rgba(0,0,0,.4), 0 1px 3px rgba(0,0,0,.3);
} }
/* 两条滑条:色相 + 明度 */ /* 两条滑条:色相 + 明度
.cp-slider{margin:11px 0 0} * 🔴 色相条必须**看得出是光谱** —— 用户反馈「好像只有蓝色」,
* 是因为之前色相条被 overflow 裁掉了,只看到上面那片蓝色的 HSV 板。
* 所以这里把色相条做高(18px)、加圆角描边,一眼就知道"能选所有颜色"。 */
.cp-slider{margin:12px 0 0}
.cp-slider input[type=range]{ .cp-slider input[type=range]{
-webkit-appearance:none; appearance:none; width:100%; height:14px; border-radius:7px; -webkit-appearance:none; appearance:none; width:100%; height:18px; border-radius:9px;
outline:none; cursor:pointer; display:block; outline:none; cursor:pointer; display:block;
box-shadow:inset 0 0 0 1px rgba(0,0,0,.10);
} }
.cp-hue input[type=range]{ .cp-hue input[type=range]{
background:linear-gradient(to right, background:linear-gradient(to right,
@@ -442,13 +450,13 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
} }
.cp-val input[type=range]{background:linear-gradient(to right, #000, #fff)} .cp-val input[type=range]{background:linear-gradient(to right, #000, #fff)}
.cp-slider input[type=range]::-webkit-slider-thumb{ .cp-slider input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none; width:18px; height:18px; border-radius:50%; -webkit-appearance:none; width:20px; height:20px; border-radius:50%;
background:#fff; border:2px solid #fff; background:#fff; border:2px solid #fff;
box-shadow:0 0 0 1px rgba(0,0,0,.35), 0 1px 4px rgba(0,0,0,.25); cursor:pointer; box-shadow:0 0 0 1px rgba(0,0,0,.45), 0 1px 4px rgba(0,0,0,.3); cursor:pointer;
} }
.cp-slider input[type=range]::-moz-range-thumb{ .cp-slider input[type=range]::-moz-range-thumb{
width:16px; height:16px; border-radius:50%; background:#fff; width:18px; height:18px; border-radius:50%; background:#fff;
border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.35); cursor:pointer; border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.45); cursor:pointer;
} }
.cp-foot{display:flex; align-items:center; gap:8px; margin-top:13px} .cp-foot{display:flex; align-items:center; gap:8px; margin-top:13px}
.cp-preview{ .cp-preview{
+1 -1
View File
@@ -8,7 +8,7 @@ if (!defined('FB_ROOT')) {
define('FB_ROOT', dirname(__DIR__)); define('FB_ROOT', dirname(__DIR__));
// 静态资源版本号:改 assets/ 下的 css/js 后把这个数字 +1, // 静态资源版本号:改 assets/ 下的 css/js 后把这个数字 +1,
// 让浏览器与 CDN 缓存失效。全站只此一处,别再往页面里写死数字。 // 让浏览器与 CDN 缓存失效。全站只此一处,别再往页面里写死数字。
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 43); } if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 45); }
} }
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------