feat(购票页): 添加动态路线箭头并优化代码格式

新增沿路线循环移动的动态指示箭头,自动适配路线总长度计算动画参数;修复文件开头多余的零宽空格,统一renderRouteOverlay函数内的代码缩进。
This commit is contained in:
2026-07-10 19:55:05 +08:00
parent 967c6740a3
commit 1bdf0ca5f2
+68 -32
View File
@@ -1,4 +1,4 @@
(() => {
(() => {
const $ = (sel) => document.querySelector(sel);
const fromEl = $('#from');
const toEl = $('#to');
@@ -438,46 +438,82 @@
}
function renderRouteOverlay() {
const svgEl = mapContainer.querySelector('svg');
if (!svgEl) return;
const svgEl = mapContainer.querySelector('svg');
if (!svgEl) return;
const existing = svgEl.querySelector('.route-overlay-group');
if (existing) existing.remove();
const existing = svgEl.querySelector('.route-overlay-group');
if (existing) existing.remove();
if (!Array.isArray(currentRoute) || currentRoute.length < 2) return;
if (!Array.isArray(currentRoute) || currentRoute.length < 2) return;
const points = currentRoute.map(getStationPoint).filter(Boolean);
if (points.length < 2) return;
const points = currentRoute.map(getStationPoint).filter(Boolean);
if (points.length < 2) return;
const ns = 'http://www.w3.org/2000/svg';
const group = document.createElementNS(ns, 'g');
group.setAttribute('class', 'route-overlay-group');
const ns = 'http://www.w3.org/2000/svg';
const group = document.createElementNS(ns, 'g');
group.setAttribute('class', 'route-overlay-group');
const pathData = points.map((pt, idx) => `${idx === 0 ? 'M' : 'L'} ${pt.x} ${pt.y}`).join(' ');
const pathData = points.map((pt, idx) => `${idx === 0 ? 'M' : 'L'} ${pt.x} ${pt.y}`).join(' ');
const glow = document.createElementNS(ns, 'path');
glow.setAttribute('d', pathData);
glow.setAttribute('fill', 'none');
glow.setAttribute('stroke', 'rgba(250, 204, 21, 0.38)');
glow.setAttribute('stroke-width', '18');
glow.setAttribute('stroke-linecap', 'round');
glow.setAttribute('stroke-linejoin', 'round');
const glow = document.createElementNS(ns, 'path');
glow.setAttribute('d', pathData);
glow.setAttribute('fill', 'none');
glow.setAttribute('stroke', 'rgba(250, 204, 21, 0.38)');
glow.setAttribute('stroke-width', '18');
glow.setAttribute('stroke-linecap', 'round');
glow.setAttribute('stroke-linejoin', 'round');
const main = document.createElementNS(ns, 'path');
main.setAttribute('d', pathData);
main.setAttribute('fill', 'none');
main.setAttribute('stroke', '#facc15');
main.setAttribute('stroke-width', '8');
main.setAttribute('stroke-linecap', 'round');
main.setAttribute('stroke-linejoin', 'round');
const main = document.createElementNS(ns, 'path');
main.setAttribute('d', pathData);
main.setAttribute('fill', 'none');
main.setAttribute('stroke', '#facc15');
main.setAttribute('stroke-width', '8');
main.setAttribute('stroke-linecap', 'round');
main.setAttribute('stroke-linejoin', 'round');
group.appendChild(glow);
group.appendChild(main);
group.appendChild(glow);
group.appendChild(main);
const firstStation = svgEl.querySelector('.map-station');
if (firstStation) svgEl.insertBefore(group, firstStation);
else svgEl.appendChild(group);
}
// Dynamic arrows pointing to the destination
let totalDist = 0;
for (let i = 0; i < points.length - 1; i++) {
const dx = points[i+1].x - points[i].x;
const dy = points[i+1].y - points[i].y;
totalDist += Math.sqrt(dx * dx + dy * dy);
}
const speed = 100; // pixels per second
const duration = Math.max(1, totalDist / speed);
const numArrows = Math.max(1, Math.floor(duration / 0.7)); // one arrow approx every 0.7s
const interval = duration / numArrows;
for (let i = 0; i < numArrows; i++) {
const arrowG = document.createElementNS(ns, 'g');
const arrowPath = document.createElementNS(ns, 'path');
// Chevron shape
arrowPath.setAttribute('d', 'M -6 -5 L 4 0 L -6 5');
arrowPath.setAttribute('fill', 'none');
arrowPath.setAttribute('stroke', '#854d0e'); // Dark brown/orange to contrast with yellow
arrowPath.setAttribute('stroke-width', '2.5');
arrowPath.setAttribute('stroke-linecap', 'round');
arrowPath.setAttribute('stroke-linejoin', 'round');
const animateMotion = document.createElementNS(ns, 'animateMotion');
animateMotion.setAttribute('dur', `${duration}s`);
animateMotion.setAttribute('repeatCount', 'indefinite');
animateMotion.setAttribute('path', pathData);
animateMotion.setAttribute('rotate', 'auto');
animateMotion.setAttribute('begin', `-${i * interval}s`);
arrowG.appendChild(arrowPath);
arrowG.appendChild(animateMotion);
group.appendChild(arrowG);
}
const firstStation = svgEl.querySelector('.map-station');
if (firstStation) svgEl.insertBefore(group, firstStation);
else svgEl.appendChild(group);
}
function updateSelectionUI(skipPreview = false) {
if (!(selection[0] && selection[1])) {