style: 优化 TVM 购票地图,缩小元素尺寸,增加切换动画,适配移动端
This commit is contained in:
+59
-45
@@ -92,44 +92,44 @@
|
||||
|
||||
let markerSvg = '';
|
||||
if (isTransfer) {
|
||||
markerSvg += `<circle cx="${x}" cy="${y}" r="15" fill="#ffffff" stroke="#1f2937" stroke-width="2.5" />`;
|
||||
markerSvg += pieSvg(x, y, 11.5, uniqueColors);
|
||||
markerSvg += `<circle class="node-core" cx="${x}" cy="${y}" r="6.5" fill="#ffffff" stroke="#334155" stroke-width="2.25" />`;
|
||||
markerSvg += `<circle cx="${x}" cy="${y}" r="12" fill="#ffffff" stroke="#1f2937" stroke-width="2" />`;
|
||||
markerSvg += pieSvg(x, y, 9, uniqueColors);
|
||||
markerSvg += `<circle class="node-core" cx="${x}" cy="${y}" r="5" fill="#ffffff" stroke="#334155" stroke-width="2" />`;
|
||||
} else {
|
||||
const color = forceColor || uniqueColors[0] || '#93a2b7';
|
||||
markerSvg += `<circle cx="${x}" cy="${y}" r="12.5" fill="${color}" stroke="${color}" stroke-width="4" />`;
|
||||
markerSvg += `<circle class="node-core" cx="${x}" cy="${y}" r="7" fill="#ffffff" stroke="${color}" stroke-width="2.25" />`;
|
||||
markerSvg += `<circle cx="${x}" cy="${y}" r="10" fill="${color}" stroke="${color}" stroke-width="2.5" />`;
|
||||
markerSvg += `<circle class="node-core" cx="${x}" cy="${y}" r="5" fill="#ffffff" stroke="${color}" stroke-width="2" />`;
|
||||
}
|
||||
|
||||
const preferTop = i % 2 === 0;
|
||||
let labelSvg = '';
|
||||
const codeFontSize = 9;
|
||||
const nameFontSize = 13;
|
||||
const enFontSize = 8.5;
|
||||
const codeW = primaryCode.length * 6 + 10;
|
||||
const codeFontSize = 8;
|
||||
const nameFontSize = 11.5;
|
||||
const enFontSize = 7.5;
|
||||
const codeW = primaryCode.length * 5.5 + 8;
|
||||
let textContent = '';
|
||||
|
||||
if (preferTop) {
|
||||
const bottomY = y - 22;
|
||||
const bottomY = y - 18;
|
||||
const enY = bottomY;
|
||||
const nameY = showEn ? bottomY - 12 : bottomY;
|
||||
const codeY = nameY - 18;
|
||||
textContent += `<rect x="${x - codeW / 2}" y="${codeY - 8}" width="${codeW}" height="12" rx="6" fill="#ffffff" stroke="#cbd5e1" stroke-width="1.5" />`;
|
||||
textContent += `<text x="${x}" y="${codeY + 1}" text-anchor="middle" fill="#64748b" font-size="${codeFontSize}" font-weight="700" letter-spacing="0.02em">${primaryCode}</text>`;
|
||||
textContent += `<text x="${x}" y="${nameY}" text-anchor="middle" fill="#0f172a" font-size="${nameFontSize}" font-weight="800" stroke="#ffffff" stroke-width="4" stroke-linejoin="round" paint-order="stroke fill">${name}</text>`;
|
||||
const nameY = showEn ? bottomY - 10 : bottomY;
|
||||
const codeY = nameY - 13;
|
||||
textContent += `<rect x="${x - codeW / 2}" y="${codeY - 7}" width="${codeW}" height="10" rx="5" fill="#ffffff" stroke="#cbd5e1" stroke-width="1" />`;
|
||||
textContent += `<text x="${x}" y="${codeY + 1.5}" text-anchor="middle" fill="#64748b" font-size="${codeFontSize}" font-weight="700" letter-spacing="0.02em">${primaryCode}</text>`;
|
||||
textContent += `<text x="${x}" y="${nameY}" text-anchor="middle" fill="#0f172a" font-size="${nameFontSize}" font-weight="800" stroke="#ffffff" stroke-width="3" stroke-linejoin="round" paint-order="stroke fill">${name}</text>`;
|
||||
if (showEn) {
|
||||
textContent += `<text x="${x}" y="${enY}" text-anchor="middle" fill="#64748b" font-size="${enFontSize}" font-weight="700" stroke="#ffffff" stroke-width="3" stroke-linejoin="round" paint-order="stroke fill">${enName}</text>`;
|
||||
textContent += `<text x="${x}" y="${enY}" text-anchor="middle" fill="#64748b" font-size="${enFontSize}" font-weight="700" stroke="#ffffff" stroke-width="2.5" stroke-linejoin="round" paint-order="stroke fill">${enName}</text>`;
|
||||
}
|
||||
} else {
|
||||
const topY = y + 34;
|
||||
const topY = y + 26;
|
||||
const codeY = topY;
|
||||
const nameY = topY + 18;
|
||||
const enY = nameY + 12;
|
||||
textContent += `<rect x="${x - codeW / 2}" y="${codeY - 8}" width="${codeW}" height="12" rx="6" fill="#ffffff" stroke="#cbd5e1" stroke-width="1.5" />`;
|
||||
textContent += `<text x="${x}" y="${codeY + 1}" text-anchor="middle" fill="#64748b" font-size="${codeFontSize}" font-weight="700" letter-spacing="0.02em">${primaryCode}</text>`;
|
||||
textContent += `<text x="${x}" y="${nameY}" text-anchor="middle" fill="#0f172a" font-size="${nameFontSize}" font-weight="800" stroke="#ffffff" stroke-width="4" stroke-linejoin="round" paint-order="stroke fill">${name}</text>`;
|
||||
const nameY = topY + 13;
|
||||
const enY = nameY + 10;
|
||||
textContent += `<rect x="${x - codeW / 2}" y="${codeY - 7}" width="${codeW}" height="10" rx="5" fill="#ffffff" stroke="#cbd5e1" stroke-width="1" />`;
|
||||
textContent += `<text x="${x}" y="${codeY + 1.5}" text-anchor="middle" fill="#64748b" font-size="${codeFontSize}" font-weight="700" letter-spacing="0.02em">${primaryCode}</text>`;
|
||||
textContent += `<text x="${x}" y="${nameY}" text-anchor="middle" fill="#0f172a" font-size="${nameFontSize}" font-weight="800" stroke="#ffffff" stroke-width="3" stroke-linejoin="round" paint-order="stroke fill">${name}</text>`;
|
||||
if (showEn) {
|
||||
textContent += `<text x="${x}" y="${enY}" text-anchor="middle" fill="#64748b" font-size="${enFontSize}" font-weight="700" stroke="#ffffff" stroke-width="3" stroke-linejoin="round" paint-order="stroke fill">${enName}</text>`;
|
||||
textContent += `<text x="${x}" y="${enY}" text-anchor="middle" fill="#64748b" font-size="${enFontSize}" font-weight="700" stroke="#ffffff" stroke-width="2.5" stroke-linejoin="round" paint-order="stroke fill">${enName}</text>`;
|
||||
}
|
||||
}
|
||||
labelSvg += textContent;
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
return `
|
||||
<g class="${classList}" data-code="${primaryCode}" data-canonical="${canonical}" onclick="handleStationClick('${primaryCode}')" style="cursor:pointer">
|
||||
<rect x="${x - 20}" y="${y - 20}" width="40" height="40" fill="transparent" />
|
||||
<rect x="${x - 16}" y="${y - 16}" width="32" height="32" fill="transparent" />
|
||||
${markerSvg}
|
||||
${labelSvg}
|
||||
</g>`;
|
||||
@@ -159,7 +159,7 @@
|
||||
const bg = isActive ? li.color : '#f8fafc';
|
||||
const fg = isActive ? getContrastColor(li.color) : '#334155';
|
||||
const border = isActive ? li.color : '#cbd5e1';
|
||||
html += `<button class="tvm-line-btn" style="padding: 8px 16px; border: 2px solid ${border}; background: ${bg}; color: ${fg}; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 14px; transition: all 0.2s;" onclick="selectTvmLine(${i})">${li.name}</button>`;
|
||||
html += `<button class="tvm-line-btn" style="padding: 6px 12px; border: 1.5px solid ${border}; background: ${bg}; color: ${fg}; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 13px; transition: all 0.25s ease;" onclick="selectTvmLine(${i})">${li.name}</button>`;
|
||||
}
|
||||
container.innerHTML = html;
|
||||
}
|
||||
@@ -179,9 +179,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const startX = 80;
|
||||
const gapX = 90;
|
||||
const y = 80;
|
||||
const startX = 40;
|
||||
const gapX = 65;
|
||||
const y = 60;
|
||||
const pts = [];
|
||||
|
||||
for (let i = 0; i < li.stops.length; i++) {
|
||||
@@ -201,16 +201,17 @@
|
||||
if (pts.length >= 2) {
|
||||
let d = `M ${pts[0].x} ${y}`;
|
||||
for(let i=1; i<pts.length; i++) d += ` L ${pts[i].x} ${y}`;
|
||||
svgContent += `<path d="${d}" fill="none" stroke="${li.color}" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" />`;
|
||||
svgContent += `<path d="${d}" fill="none" stroke="${li.color}" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" />`;
|
||||
}
|
||||
|
||||
for (let i = 0; i < pts.length; i++) {
|
||||
svgContent += renderStationNode(pts[i].x, pts[i].y, pts[i].code, i, li.color);
|
||||
}
|
||||
|
||||
const width = startX + pts.length * gapX + 80;
|
||||
const height = 160;
|
||||
container.innerHTML = `<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="display:block; margin:auto;">${svgContent}</svg>`;
|
||||
const width = startX + pts.length * gapX + 40;
|
||||
const height = 120;
|
||||
// 添加 tvm-animate 类实现动画过渡
|
||||
container.innerHTML = `<svg class="tvm-animate" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="display:block;">${svgContent}</svg>`;
|
||||
}
|
||||
|
||||
function renderTvmRoute() {
|
||||
@@ -236,9 +237,9 @@
|
||||
container.style.display = 'flex';
|
||||
if (title) title.style.display = 'block';
|
||||
|
||||
const startX = 80;
|
||||
const gapX = 90;
|
||||
const y = 80;
|
||||
const startX = 40;
|
||||
const gapX = 65;
|
||||
const y = 60;
|
||||
|
||||
let svgContent = '';
|
||||
|
||||
@@ -265,7 +266,7 @@
|
||||
}
|
||||
|
||||
for (const seg of segments) {
|
||||
svgContent += `<line x1="${seg.x1}" y1="${y}" x2="${seg.x2}" y2="${y}" stroke="${seg.color}" stroke-width="8" stroke-linecap="round" />`;
|
||||
svgContent += `<line x1="${seg.x1}" y1="${y}" x2="${seg.x2}" y2="${y}" stroke="${seg.color}" stroke-width="6" stroke-linecap="round" />`;
|
||||
}
|
||||
|
||||
const pathD = `M ${startX} ${y} L ${startX + (routeCodes.length - 1) * gapX} ${y}`;
|
||||
@@ -277,7 +278,7 @@
|
||||
for (let i = 0; i < numArrows; i++) {
|
||||
svgContent += `
|
||||
<g>
|
||||
<path d="M -6 -5 L 4 0 L -6 5" fill="none" stroke="#854d0e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M -5 -4 L 3 0 L -5 4" fill="none" stroke="#854d0e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<animateMotion dur="${duration}s" repeatCount="indefinite" path="${pathD}" rotate="auto" begin="-${i * interval}s" calcMode="linear" />
|
||||
</g>
|
||||
`;
|
||||
@@ -287,9 +288,9 @@
|
||||
svgContent += renderStationNode(startX + i * gapX, y, routeCodes[i], i);
|
||||
}
|
||||
|
||||
const width = startX + routeCodes.length * gapX + 80;
|
||||
const height = 160;
|
||||
container.innerHTML = `<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="display:block; margin:auto;">${svgContent}</svg>`;
|
||||
const width = startX + routeCodes.length * gapX + 40;
|
||||
const height = 120;
|
||||
container.innerHTML = `<svg class="tvm-animate" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="display:block;">${svgContent}</svg>`;
|
||||
}
|
||||
|
||||
async function renderSystemMap() {
|
||||
@@ -383,12 +384,25 @@
|
||||
if (uniq.length >= 2) stationTransfer.add(c);
|
||||
}
|
||||
|
||||
const fadeStyle = `<style>
|
||||
@keyframes tvmFadeIn {
|
||||
from { opacity: 0; transform: translateX(-10px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
.tvm-animate { animation: tvmFadeIn 0.3s ease-out forwards; }
|
||||
.tvm-scroll-container { scroll-behavior: smooth; -webkit-overflow-scrolling: touch; }
|
||||
.tvm-scroll-container::-webkit-scrollbar { height: 4px; }
|
||||
.tvm-scroll-container::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 2px; }
|
||||
.tvm-scroll-container::-webkit-scrollbar-track { background: transparent; }
|
||||
</style>`;
|
||||
|
||||
mapContainer.innerHTML = `
|
||||
<div class="tvm-layout" style="display: flex; flex-direction: column; gap: 20px; width: 100%;">
|
||||
<div class="tvm-lines" id="tvmLines" style="display: flex; flex-wrap: wrap; gap: 8px;"></div>
|
||||
<div class="tvm-single-line" id="tvmSingleLine" style="overflow-x: auto; padding: 10px 0; min-height: 160px; display: flex; align-items: center; justify-content: flex-start;"></div>
|
||||
<div class="tvm-route-title" style="font-weight: bold; margin-bottom: -10px; color: #475569; display: none; font-size: 16px;" id="tvmRouteTitle">已选路径</div>
|
||||
<div class="tvm-route" id="tvmRoute" style="overflow-x: auto; padding: 10px 0; border-top: 2px dashed #cbd5e1; min-height: 160px; display: none; align-items: center; justify-content: flex-start;"></div>
|
||||
${fadeStyle}
|
||||
<div class="tvm-layout" style="display: flex; flex-direction: column; gap: 16px; width: 100%;">
|
||||
<div class="tvm-lines" id="tvmLines" style="display: flex; flex-wrap: wrap; gap: 6px;"></div>
|
||||
<div class="tvm-single-line tvm-scroll-container" id="tvmSingleLine" style="overflow-x: auto; padding: 10px 0; min-height: 120px; display: flex; align-items: center; justify-content: flex-start;"></div>
|
||||
<div class="tvm-route-title" style="font-weight: bold; margin-bottom: -12px; color: #475569; display: none; font-size: 14px;" id="tvmRouteTitle">已选路径</div>
|
||||
<div class="tvm-route tvm-scroll-container" id="tvmRoute" style="overflow-x: auto; padding: 10px 0; border-top: 1.5px dashed #cbd5e1; min-height: 120px; display: none; align-items: center; justify-content: flex-start;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user