From dcef3ad427a57b1de7c4bc502229fce9a8f83e40 Mon Sep 17 00:00:00 2001 From: y2keui Date: Sat, 6 Jun 2026 22:47:19 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B0=A8=EB=9F=89=20=EB=A7=88=EC=BB=A4=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=9E=AC=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/public/app.js | 110 ++++++++++++++- apps/web/public/styles.css | 267 ++++++++++++++++++------------------- 2 files changed, 232 insertions(+), 145 deletions(-) diff --git a/apps/web/public/app.js b/apps/web/public/app.js index 39238bf..c551f74 100644 --- a/apps/web/public/app.js +++ b/apps/web/public/app.js @@ -926,9 +926,9 @@ function updateLeafletMarker(location) { const icon = L.divIcon({ className: `leaflet-truck-marker truck-marker ${status.key} ${vehicleType.className} ${selectedClass}`, html: createTruckMarkerHtml(location, vehicleType, markerHeading), - iconSize: [106, 82], - iconAnchor: [53, 78], - popupAnchor: [0, -66] + iconSize: [96, 78], + iconAnchor: [48, 74], + popupAnchor: [0, -58] }); let marker = existingMarker; @@ -970,10 +970,13 @@ function createTruckMarkerHtml(location, vehicleType = getVehicleType(location), const label = location.vehicleNo ? location.vehicleNo.slice(-4) : location.driverId.slice(0, 4); const rotation = normalizeMarkerRotation(heading); return ` - - - - + + ${escapeHtml(vehicleType.shortLabel)} @@ -982,6 +985,99 @@ function createTruckMarkerHtml(location, vehicleType = getVehicleType(location), `; } +function createVehicleMarkerSvg(typeClass) { + if (typeClass === "type-trailer") { + return ` + + `; + } + + if (typeClass === "type-container") { + return ` + + `; + } + + if (typeClass === "type-box") { + return ` + + `; + } + + if (typeClass === "type-van") { + return ` + + `; + } + + if (typeClass === "type-cold") { + return ` + + `; + } + + if (typeClass === "type-wing") { + return ` + + `; + } + + return ` + + `; +} + function resolveMarkerHeading(location, fromPosition, targetPosition) { const routeHeading = getRouteStartHeading(location); if (routeHeading !== null) return routeHeading; diff --git a/apps/web/public/styles.css b/apps/web/public/styles.css index 2c3a7d2..198def8 100644 --- a/apps/web/public/styles.css +++ b/apps/web/public/styles.css @@ -1052,27 +1052,22 @@ button:hover { .truck-marker { --truck-color: var(--blue); --truck-cab: #1556b8; - --truck-accent: rgba(255, 255, 255, 0.2); + --truck-secondary: #93c5fd; --marker-status: var(--green); - --marker-outline: #ffffff; + --marker-arrow: #111827; --truck-heading: 0deg; - --body-width: 50px; - --body-height: 28px; - --cab-width: 22px; position: relative; display: grid; place-items: center; - width: 106px; - min-width: 106px; - height: 82px; - min-height: 82px; + width: 96px; + min-width: 96px; + height: 78px; + min-height: 78px; border: 0; padding: 0; background: transparent; color: #ffffff; - filter: - drop-shadow(0 2px 0 rgba(255, 255, 255, 0.92)) - drop-shadow(0 10px 16px rgba(16, 32, 48, 0.34)); + filter: drop-shadow(0 7px 11px rgba(15, 23, 42, 0.28)); font-family: inherit; cursor: pointer; transition: filter 0.18s ease, transform 0.18s ease; @@ -1081,189 +1076,206 @@ button:hover { .truck-marker:hover, .truck-marker.is-selected { z-index: 8; - filter: - drop-shadow(0 2px 0 rgba(255, 255, 255, 0.96)) - drop-shadow(0 13px 20px rgba(16, 32, 48, 0.44)); + filter: drop-shadow(0 10px 15px rgba(15, 23, 42, 0.38)); transform: translateY(-1px); } .truck-marker.stale { - --truck-color: var(--amber); - --truck-cab: #8f4307; --marker-status: var(--amber); } .truck-marker.offline { - --truck-color: var(--red); - --truck-cab: #8f1d15; --marker-status: var(--red); + opacity: 0.9; } .truck-marker.type-trailer { --truck-color: #7c3aed; --truck-cab: #4c1d95; - --body-width: 70px; - --body-height: 27px; + --truck-secondary: #c4b5fd; } .truck-marker.type-container { --truck-color: #f97316; --truck-cab: #c2410c; - --body-width: 66px; - --body-height: 27px; - --truck-accent: repeating-linear-gradient( - 90deg, - rgba(255, 255, 255, 0.32) 0 2px, - transparent 2px 9px - ); + --truck-secondary: #fed7aa; } -.truck-marker.type-box, -.truck-marker.type-wing { +.truck-marker.type-box { --truck-color: #059669; --truck-cab: #047857; - --body-width: 56px; - --body-height: 28px; + --truck-secondary: #a7f3d0; } .truck-marker.type-van { --truck-color: #0ea5e9; --truck-cab: #0369a1; - --body-width: 40px; - --body-height: 24px; - --cab-width: 25px; + --truck-secondary: #bae6fd; } .truck-marker.type-cold { --truck-color: #0891b2; --truck-cab: #155e75; - --body-width: 55px; - --body-height: 28px; - --truck-accent: linear-gradient(135deg, rgba(255, 255, 255, 0.28), transparent 45%); + --truck-secondary: #cffafe; } -.truck-marker.stale { - --truck-color: var(--amber); - --truck-cab: #8f4307; - --marker-status: var(--amber); -} - -.truck-marker.offline { - --truck-color: var(--red); - --truck-cab: #8f1d15; - --marker-status: var(--red); +.truck-marker.type-wing { + --truck-color: #16a34a; + --truck-cab: #15803d; + --truck-secondary: #bbf7d0; } .truck-marker::after { content: ""; position: absolute; - top: 6px; - right: 14px; + top: 25px; + right: 7px; z-index: 4; - width: 12px; - height: 12px; + width: 10px; + height: 10px; border: 2px solid #ffffff; border-radius: 50%; background: var(--marker-status); - box-shadow: 0 2px 7px rgba(15, 23, 42, 0.32); + box-shadow: 0 2px 7px rgba(15, 23, 42, 0.34); pointer-events: none; } -.truck-marker__halo { +.truck-marker__direction { position: absolute; - top: 5px; + top: 4px; left: 50%; - z-index: 0; - width: 98px; - height: 50px; - border: 2px solid rgba(15, 23, 42, 0.12); - border-radius: 999px; - background: - radial-gradient(circle at 48% 46%, rgba(255, 255, 255, 0.98) 0 54%, rgba(255, 255, 255, 0.82) 55% 100%); - box-shadow: - 0 0 0 2px rgba(255, 255, 255, 0.72), - 0 8px 18px rgba(15, 23, 42, 0.24); - transform: translateX(-50%); + z-index: 4; + width: 24px; + height: 24px; + transform: translateX(-50%) rotate(var(--truck-heading)); + transform-origin: 50% 50%; + transition: transform 0.35s ease; pointer-events: none; } -.truck-marker.is-selected .truck-marker__halo { - border-color: rgba(31, 111, 235, 0.78); - box-shadow: - 0 0 0 3px rgba(255, 255, 255, 0.86), - 0 0 0 6px rgba(31, 111, 235, 0.22), - 0 10px 22px rgba(15, 23, 42, 0.28); +.truck-marker__direction-icon { + display: block; + width: 100%; + height: 100%; + overflow: visible; + filter: drop-shadow(0 2px 4px rgba(15, 23, 42, 0.35)); +} + +.truck-marker__direction-icon path { + fill: var(--marker-arrow); + stroke: #ffffff; + stroke-linejoin: round; + stroke-width: 3.2; +} + +.truck-marker.is-selected .truck-marker__direction { + --marker-arrow: var(--blue); } .truck-marker__vehicle { - position: relative; - z-index: 2; - display: inline-flex; - align-items: flex-end; - justify-content: center; - margin-bottom: 12px; - padding-bottom: 8px; - transform: rotate(var(--truck-heading)); - transform-origin: 50% 44%; - transition: transform 0.35s ease; -} - -.truck-marker__vehicle::before, -.truck-marker__vehicle::after { - content: ""; position: absolute; - bottom: 1px; - width: 8px; - height: 8px; - border: 2px solid var(--marker-outline); - border-radius: 50%; - background: #17212b; - box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.14); + top: 18px; + left: 50%; + z-index: 2; + display: block; + width: 86px; + height: 46px; + transform: translateX(-50%); } .truck-marker__vehicle::before { - left: 12px; + content: ""; + position: absolute; + inset: 5px 2px 0; + z-index: -1; + border: 1px solid rgba(15, 23, 42, 0.11); + border-radius: 14px; + background: rgba(255, 255, 255, 0.94); + box-shadow: + 0 0 0 2px rgba(255, 255, 255, 0.86), + 0 6px 13px rgba(15, 23, 42, 0.22); } -.truck-marker__vehicle::after { - right: 13px; +.truck-marker.is-selected .truck-marker__vehicle::before { + border-color: rgba(31, 111, 235, 0.72); + box-shadow: + 0 0 0 3px rgba(255, 255, 255, 0.92), + 0 0 0 6px rgba(31, 111, 235, 0.2), + 0 8px 17px rgba(15, 23, 42, 0.27); } -.truck-marker__body { - position: relative; +.truck-marker__svg { display: block; - width: var(--body-width); - height: var(--body-height); - border: 3px solid var(--marker-outline); - border-right: 0; - border-radius: 7px 0 0 6px; - background: - var(--truck-accent), - var(--truck-color); - box-shadow: inset 0 -5px 0 rgba(15, 23, 42, 0.13); + width: 100%; + height: 100%; + overflow: visible; } -.truck-marker.type-van .truck-marker__body { - border-radius: 9px 0 0 7px; +.truck-marker__svg .vehicle-body, +.truck-marker__svg .vehicle-cab, +.truck-marker__svg .vehicle-trailer, +.truck-marker__svg .vehicle-wing { + fill: var(--truck-color); + stroke: #ffffff; + stroke-linejoin: round; + stroke-width: 3; +} + +.truck-marker__svg .vehicle-cab { + fill: var(--truck-cab); +} + +.truck-marker__svg .vehicle-wing { + fill: var(--truck-secondary); + stroke: #ffffff; +} + +.truck-marker__svg .vehicle-window { + fill: #e0f2fe; + stroke: #ffffff; + stroke-linejoin: round; + stroke-width: 2; +} + +.truck-marker__svg .vehicle-wheel { + fill: #111827; + stroke: #ffffff; + stroke-width: 3; +} + +.truck-marker__svg .vehicle-detail, +.truck-marker__svg .vehicle-container-lines, +.truck-marker__svg .vehicle-hitch, +.truck-marker__svg .vehicle-snow { + fill: none; + stroke: rgba(255, 255, 255, 0.78); + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 2.4; +} + +.truck-marker__svg .vehicle-snow { + stroke: #ffffff; + stroke-width: 2.2; } .truck-marker__label { position: absolute; left: 50%; - bottom: 2px; + bottom: 0; z-index: 3; display: inline-flex; align-items: center; gap: 4px; - max-width: 98px; - min-height: 21px; - padding: 2px 7px; - border: 2px solid rgba(255, 255, 255, 0.9); + max-width: 88px; + min-height: 19px; + padding: 2px 7px 3px; + border: 1px solid rgba(255, 255, 255, 0.9); border-radius: 999px; background: rgba(15, 23, 42, 0.94); - box-shadow: 0 5px 12px rgba(15, 23, 42, 0.3); + box-shadow: 0 4px 10px rgba(15, 23, 42, 0.28); color: #ffffff; - font-size: 11px; + font-size: 10px; font-weight: 900; line-height: 1.1; transform: translateX(-50%); @@ -1281,27 +1293,6 @@ button:hover { text-overflow: ellipsis; } -.truck-marker__cab { - position: relative; - width: var(--cab-width); - height: calc(var(--body-height) - 3px); - border: 3px solid var(--marker-outline); - border-radius: 0 8px 6px 0; - background: var(--truck-cab); - box-shadow: inset 0 -4px 0 rgba(15, 23, 42, 0.14); -} - -.truck-marker__cab::before { - content: ""; - position: absolute; - top: 3px; - right: 3px; - width: 7px; - height: 5px; - border-radius: 1px; - background: rgba(255, 255, 255, 0.72); -} - .leaflet-truck-marker { border: 0; background: transparent;