From 86ee03f4a653d595d97c1bc3ffa4ad215e87577f Mon Sep 17 00:00:00 2001 From: fukunee Date: Sat, 19 Sep 2026 14:13:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E9=80=89=E4=B8=AD=E4=BE=BF?= =?UTF-8?q?=E7=AD=BE=E7=9A=84=E9=BB=91=E8=BE=B9=E4=B8=BA=E5=B9=B2=E5=87=80?= =?UTF-8?q?=E7=9A=84=E5=A4=96=E6=8F=8F=E8=BE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/scene/CesiumGlobe.js | 78 ++++++++++++++++++++-------------------- src/styles.css | 23 ++++++------ 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/scene/CesiumGlobe.js b/src/scene/CesiumGlobe.js index afc5183..83e973d 100644 --- a/src/scene/CesiumGlobe.js +++ b/src/scene/CesiumGlobe.js @@ -53,8 +53,9 @@ function sphericalAreaKm2(polys) { } // 生成便签 canvas:黄底圆角 + 左侧箭头 + 黑色加粗文字(整张图,箭头尖端指向坐标) +// 选中态:先描粗边再填充,得到一条干净的 2px 外描边(四周预留 B 的 padding 防止裁切) function makeStickyNote(text, fontSize, selected = false) { - const padX = 8, padY = 5, radius = 7, arrowW = 10, arrowH = 14, S = 2; // S: 2x 超采样 + const padX = 8, padY = 5, radius = 7, arrowW = 10, arrowH = 14, S = 2, B = 2; // S: 2x 超采样,B: 边框预留 const font = `bold ${fontSize}px "Noto Serif SC", "Songti SC", "STSong", "SimSun", Georgia, serif`; const m = document.createElement('canvas'); const mctx = m.getContext('2d'); @@ -63,55 +64,54 @@ function makeStickyNote(text, fontSize, selected = false) { const bodyW = Math.max(1, Math.ceil(textW + padX * 2)); const w = arrowW + bodyW; const h = Math.ceil(fontSize + padY * 2); + const cw = w + B * 2; + const ch = h + B * 2; const c = document.createElement('canvas'); - c.width = w * S; - c.height = h * S; + c.width = cw * S; + c.height = ch * S; const ctx = c.getContext('2d'); ctx.scale(S, S); - // 阴影 - ctx.shadowColor = 'rgba(0, 0, 0, 0.20)'; - ctx.shadowBlur = 5; - ctx.shadowOffsetY = 2; + const ox = B, oy = B; // 形状整体内缩 B,给黑边留空间 - // 箭头 + 圆角矩形一体化路径(箭头尖端在左侧 x=0,指向坐标) + // 箭头 + 圆角矩形一体化路径(箭头尖端在 (ox, oy + h/2),指向坐标) ctx.beginPath(); - ctx.moveTo(0, h / 2); // 箭头尖端 - ctx.lineTo(arrowW, h / 2 - arrowH / 2); // 箭头上沿 - ctx.lineTo(arrowW, radius); // 左边缘上部 - ctx.arcTo(arrowW, 0, arrowW + radius, 0, radius); // 左上圆角 - ctx.lineTo(w - radius, 0); // 上边 - ctx.arcTo(w, 0, w, radius, radius); // 右上圆角 - ctx.lineTo(w, h - radius); // 右边 - ctx.arcTo(w, h, w - radius, h, radius); // 右下圆角 - ctx.lineTo(arrowW + radius, h); // 底边 - ctx.arcTo(arrowW, h, arrowW, h - radius, radius); // 左下圆角 - ctx.lineTo(arrowW, h / 2 + arrowH / 2); // 左边缘下部 - ctx.closePath(); // 闭合回箭头尖端 + ctx.moveTo(ox, oy + h / 2); + ctx.lineTo(ox + arrowW, oy + h / 2 - arrowH / 2); + ctx.lineTo(ox + arrowW, oy + radius); + ctx.arcTo(ox + arrowW, oy, ox + arrowW + radius, oy, radius); + ctx.lineTo(ox + w - radius, oy); + ctx.arcTo(ox + w, oy, ox + w, oy + radius, radius); + ctx.lineTo(ox + w, oy + h - radius); + ctx.arcTo(ox + w, oy + h, ox + w - radius, oy + h, radius); + ctx.lineTo(ox + arrowW + radius, oy + h); + ctx.arcTo(ox + arrowW, oy + h, ox + arrowW, oy + h - radius, radius); + ctx.lineTo(ox + arrowW, oy + h / 2 + arrowH / 2); + ctx.closePath(); + + if (selected) { + // 先描粗边,再填充盖住内侧一半 → 外露 2px 干净黑边 + ctx.strokeStyle = '#14100d'; + ctx.lineWidth = B * 2; // 4px,外露 2px + ctx.lineJoin = 'round'; + ctx.stroke(); + } else { + // 非选中:柔和的投影 + ctx.shadowColor = 'rgba(0, 0, 0, 0.20)'; + ctx.shadowBlur = 5; + ctx.shadowOffsetY = 2; + } ctx.fillStyle = '#e0c46a'; ctx.fill(); - // 选中时加黑边 - if (selected) { - ctx.shadowColor = 'transparent'; - ctx.shadowBlur = 0; - ctx.shadowOffsetY = 0; - ctx.strokeStyle = '#14100d'; - ctx.lineWidth = 3; - ctx.stroke(); - } - - // 黑色加粗文字(无阴影) - ctx.shadowColor = 'transparent'; - ctx.shadowBlur = 0; - ctx.shadowOffsetY = 0; + // 黑色加粗文字 ctx.fillStyle = '#14100d'; ctx.font = font; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; - ctx.fillText(text, arrowW + bodyW / 2, h / 2 + 1); + ctx.fillText(text, ox + arrowW + bodyW / 2, oy + h / 2 + 1); - return { canvas: c, scale: 1 / S, width: w, height: h }; + return { canvas: c, scale: 1 / S, width: cw, height: ch, tipX: B, tipY: B + h / 2 }; } export class CesiumGlobe { @@ -300,6 +300,8 @@ export class CesiumGlobe { el.style.height = `${m.noteNormal.height}px`; el.dataset.index = m.index; m.el = el; + m.tipX = m.noteNormal.tipX; // 箭头尖相对元素左上角的偏移 + m.tipY = m.noteNormal.tipY; m.normalUrl = m.noteNormal.canvas.toDataURL(); m.selectedUrl = m.noteSelected.canvas.toDataURL(); el.style.backgroundImage = `url("${m.normalUrl}")`; @@ -335,8 +337,8 @@ export class CesiumGlobe { el.style.display = 'block'; el.style.opacity = String(opacity); - el.style.left = `${win.x}px`; - el.style.top = `${win.y}px`; + el.style.left = `${win.x - m.tipX}px`; + el.style.top = `${win.y - m.tipY}px`; } } diff --git a/src/styles.css b/src/styles.css index c4e49ed..7211290 100644 --- a/src/styles.css +++ b/src/styles.css @@ -47,7 +47,6 @@ html, body { pointer-events: auto; background-repeat: no-repeat; background-size: 100% 100%; - transform: translateY(-50%); cursor: pointer; } @@ -66,27 +65,25 @@ html, body { background: #0c0906; opacity: 0; transition: opacity 0.18s ease; } -/* 加载界面 */ +/* 加载界面:OS1 3D 加载环 */ #loading { position: fixed; inset: 0; z-index: 1000; - display: flex; align-items: center; justify-content: center; + display: flex; flex-direction: column; align-items: center; justify-content: center; + gap: 26px; background: #14100d; transition: opacity 0.6s ease; } #loading.hidden { opacity: 0; pointer-events: none; } -.loading-box { display: flex; flex-direction: column; align-items: center; gap: 18px; } -.spinner { - width: 54px; height: 54px; - border: 3px solid rgba(201, 162, 39, 0.22); - border-top-color: #c9a227; - border-radius: 50%; - animation: spin 0.9s linear infinite; +#loading-canvas { + width: 500px; height: 500px; + max-width: 88vw; max-height: 88vw; } -@keyframes spin { to { transform: rotate(360deg); } } +#loading-canvas canvas { width: 100%; height: 100%; display: block; } .loading-text { font-family: var(--serif); - font-size: 14px; letter-spacing: 0.3em; - color: rgba(230, 216, 182, 0.7); + font-size: 13px; + letter-spacing: 0.2em; + color: rgba(224, 196, 106, 0.8); } /* 顶部标题 */