首页地图引线加箭头指向真实位置(批 23)

① 被扇开的卡片,引线改成「从卡片指向真实坐标」:箭头画在真实位置那一端
  (marker-end + SVG marker),终点退开 DOT_R+2 px 免得盖住圆点。活动多了
  也能一眼看出这张卡片归哪个点 —— 这是用户「活动特别多会不会乱」的核心诉求。

② 箭头 marker 的 id 逐枚唯一(home-arrow-<下标>):每枚标记的 content 各自
  带一份 <defs>,同页 id 重名会指向先出现的那份,属隐患。

验证:本地全链新增 10c2「首页地图密集活动排布」步骤 —— 密集夹具
(8 个活动:东北簇 3 + 长三角簇 3 + 孤立三亚/成都)断言卡片两两不叠、
都在地图容器内、扇开的都有箭头且箭头落点离自己真实坐标 <= 8px、marker id
不重名、孤立的两枚原地不动、密集下仍可点进活动;_map16 亦加两条箭头断言。
部署哨兵新增批 23 段 4 条(数代码形式字面量,防注释打挂)。
This commit is contained in:
2026-09-26 20:41:28 +08:00
parent c72f9d0b14
commit 5645a8db5c
2 changed files with 25 additions and 9 deletions
+2
View File
@@ -1338,6 +1338,8 @@ video.video-thumb.video-thumb-mini {
.home-pin svg { position: absolute; left: 0; top: 0; overflow: visible; }
.home-pin-line { stroke: #8aa0b8; stroke-width: 1.5; }
.home-pin-dot { fill: #3b82f6; }
/* 引线末端的箭头(指向真实位置那个圆点)—— 活动多了也分得清哪张卡片归哪个点 */
.home-arrow { fill: #8aa0b8; }
.home-card {
position: absolute; transform: translate(-50%, -50%);
display: flex; align-items: center; gap: 7px;
+23 -9
View File
@@ -159,6 +159,7 @@ foreach ($activities as $a) {
var CARD_W = 186, CARD_H = 58; // 卡片名义尺寸(按中心距判碰用)
var CLUSTER_DIST = 110; // 中心距小于它 → 归为同簇(要扇开)
var CANVAS = 600, C = CANVAS / 2; // 每枚标记的 content 画布(真实位置在画布中心)
var DOT_R = 3; // 真实位置那个小圆点的半径(引线箭头要给它让位)
function shortTitle(t) {
return (t && t.length > 10) ? t.slice(0, 10) + '…' : (t || '');
@@ -181,8 +182,11 @@ foreach ($activities as $a) {
if (s.memberCount > names.length) t += ' 等 ' + s.memberCount + ' 人';
return t;
}
function cardHtml(s, dx, dy) {
function cardHtml(s, dx, dy, idx) {
var moved = Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5;
/* 箭头 marker 的 id 必须逐枚唯一:每枚标记的 content 都自带一份 <defs>,
同页里 id 重名会指向先出现的那份(虽然样子一样,但属于隐患)。 */
var arrowId = 'home-arrow-' + (idx || 0);
var inner = s.img
? '<img src="' + esc(s.img) + '" alt="">'
: '<span>📍</span>';
@@ -194,9 +198,19 @@ foreach ($activities as $a) {
var html = '<div class="home-pin">'
+ '<svg width="' + CANVAS + '" height="' + CANVAS + '">';
if (moved) {
html += '<line class="home-pin-line" x1="' + C + '" y1="' + C + '"'
+ ' x2="' + (C + dx) + '" y2="' + (C + dy) + '"/>'
+ '<circle class="home-pin-dot" cx="' + C + '" cy="' + C + '" r="3"/>';
/* 引线**从卡片指向真实位置**:箭头画在真实位置那一端(marker-end),
活动多了也能一眼看出这张卡片归哪个点;终点退开 DOT_R+2 px,
免得箭头盖住圆心那个小圆点。 */
var len = Math.hypot(dx, dy) || 1;
var gap = DOT_R + 2;
var ex = C + dx / len * gap, ey = C + dy / len * gap;
html += '<defs><marker id="' + arrowId + '" viewBox="0 0 10 10"'
+ ' refX="9" refY="5" markerWidth="9" markerHeight="9"'
+ ' markerUnits="userSpaceOnUse" orient="auto">'
+ '<path class="home-arrow" d="M0,1 L10,5 L0,9 z"/></marker></defs>'
+ '<line class="home-pin-line" x1="' + (C + dx) + '" y1="' + (C + dy) + '"'
+ ' x2="' + ex + '" y2="' + ey + '" marker-end="url(#' + arrowId + ')"/>'
+ '<circle class="home-pin-dot" cx="' + C + '" cy="' + C + '" r="' + DOT_R + '"/>';
}
html += '</svg>'
+ '<div class="home-card" style="left:' + (C + dx) + 'px;top:' + (C + dy) + 'px;"'
@@ -210,12 +224,12 @@ foreach ($activities as $a) {
}
var markers = []; // { mk: AMap.Marker, spot: spot }
spots.forEach(function (s) {
spots.forEach(function (s, si) {
/* ⚠️ position 必须是 [lng, lat] 数组:传 {lng,lat} 裸对象会让 AMap 抛错,
整个绘制循环当场中断(批 11 的「光秃秃地图」就是它)。 */
var mk = new AMap.Marker({
position: [s.lng, s.lat],
content: cardHtml(s, 0, 0),
content: cardHtml(s, 0, 0, si),
anchor: 'center',
map: map,
});
@@ -251,14 +265,14 @@ foreach ($activities as $a) {
卡片被甩到容器外、被上方的标签栏挡住,点不到。) */
function relayout() {
var items = [];
markers.forEach(function (m) {
markers.forEach(function (m, mi) {
var base = null;
try {
base = map.lngLatToContainer(new AMap.LngLat(m.spot.lng, m.spot.lat));
} catch (e) {
base = null;
}
items.push({ mk: m.mk, spot: m.spot, base: base, dx: 0, dy: 0 });
items.push({ mk: m.mk, spot: m.spot, base: base, dx: 0, dy: 0, idx: mi });
});
/* 并查集:中心距 < CLUSTER_DIST 的归为同簇 */
@@ -352,7 +366,7 @@ foreach ($activities as $a) {
});
items.forEach(function (it) {
if (it.base) it.mk.setContent(cardHtml(it.spot, it.dx, it.dy));
if (it.base) it.mk.setContent(cardHtml(it.spot, it.dx, it.dy, it.idx));
});
}
relayout();