| | |
| | | </div> |
| | | </div> |
| | | `, |
| | | props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding'], |
| | | props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding', 'traceOverlay'], |
| | | data() { |
| | | return { |
| | | map: [], |
| | |
| | | hoverRaf: null, |
| | | objectsContainer: null, |
| | | objectsContainer2: null, |
| | | traceOverlayContainer: null, |
| | | tracePulseTween: null, |
| | | tracksContainer: null, |
| | | tracksGraphics: null, |
| | | shelvesContainer: null, |
| | |
| | | if (this.shelfCullRaf) { cancelAnimationFrame(this.shelfCullRaf); this.shelfCullRaf = null; } |
| | | if (this.resizeDebounceTimer) { clearTimeout(this.resizeDebounceTimer); this.resizeDebounceTimer = null; } |
| | | if (window.gsap && this.pixiApp && this.pixiApp.stage) { window.gsap.killTweensOf(this.pixiApp.stage.position); } |
| | | this.clearTraceOverlay(); |
| | | if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); } |
| | | if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; } |
| | | window.removeEventListener('resize', this.scheduleResizeToContainer); |
| | |
| | | window.gsap.fromTo(sprite, { alpha: 1 }, { alpha: 0.2, yoyo: true, repeat: 6, duration: 0.15 }); |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | traceOverlay: { |
| | | deep: true, |
| | | handler() { |
| | | this.renderTraceOverlay(); |
| | | } |
| | | } |
| | | }, |
| | |
| | | this.graphicsRgvTrack = this.createTrackTexture(25, 25, 10); |
| | | this.objectsContainer = new PIXI.Container(); |
| | | this.objectsContainer2 = new PIXI.Container(); |
| | | this.traceOverlayContainer = new PIXI.Container(); |
| | | this.tracksContainer = new PIXI.ParticleContainer(10000, { scale: true, position: true, rotation: false, uvs: false, alpha: false }); |
| | | this.tracksGraphics = new PIXI.Graphics(); |
| | | this.shelvesContainer = new PIXI.Container(); |
| | |
| | | this.mapRoot.addChild(this.shelvesContainer); |
| | | this.mapRoot.addChild(this.objectsContainer); |
| | | this.mapRoot.addChild(this.objectsContainer2); |
| | | this.mapRoot.addChild(this.traceOverlayContainer); |
| | | this.pixiApp.renderer.roundPixels = true; |
| | | this.hoveredShelfCell = null; |
| | | this.hoverPointer = { x: 0, y: 0 }; |
| | |
| | | changeFloor(lev) { |
| | | this.currentLev = lev; |
| | | this.clearLoopStationHighlight(); |
| | | this.clearTraceOverlay(); |
| | | this.isSwitchingFloor = true; |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | |
| | | }, |
| | | createMapData(map) { |
| | | this.clearLoopStationHighlight(); |
| | | this.clearTraceOverlay(); |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | | this.mapRowOffsets = []; |
| | |
| | | this.applyMapTransform(true); |
| | | this.map = map; |
| | | this.isSwitchingFloor = false; |
| | | this.renderTraceOverlay(); |
| | | }, |
| | | initWidth(map) { |
| | | let maxRow = map.length; |
| | |
| | | this.hoverLoopNo = null; |
| | | this.hoverLoopStationIdSet = new Set(); |
| | | }, |
| | | clearTraceOverlay() { |
| | | if (window.gsap && this.tracePulseTween) { |
| | | try { this.tracePulseTween.kill(); } catch (e) {} |
| | | } |
| | | this.tracePulseTween = null; |
| | | if (!this.traceOverlayContainer) { return; } |
| | | const children = this.traceOverlayContainer.removeChildren(); |
| | | children.forEach((child) => { |
| | | if (child && typeof child.destroy === 'function') { |
| | | child.destroy({ children: true, texture: false, baseTexture: false }); |
| | | } |
| | | }); |
| | | }, |
| | | normalizeTraceOverlay(trace) { |
| | | if (!trace) { return null; } |
| | | const taskNo = parseInt(trace.taskNo, 10); |
| | | return { |
| | | taskNo: isNaN(taskNo) ? null : taskNo, |
| | | status: trace.status || '', |
| | | currentStationId: this.parseStationTaskNo(trace.currentStationId), |
| | | finalTargetStationId: this.parseStationTaskNo(trace.finalTargetStationId), |
| | | blockedStationId: this.parseStationTaskNo(trace.blockedStationId), |
| | | passedStationIds: this.normalizeTraceStationIds(trace.passedStationIds), |
| | | pendingStationIds: this.normalizeTraceStationIds(trace.pendingStationIds), |
| | | latestAppendedPath: this.normalizeTraceStationIds(trace.latestAppendedPath) |
| | | }; |
| | | }, |
| | | normalizeTraceStationIds(list) { |
| | | if (!Array.isArray(list)) { return []; } |
| | | const result = []; |
| | | list.forEach((item) => { |
| | | const stationId = parseInt(item, 10); |
| | | if (!isNaN(stationId)) { result.push(stationId); } |
| | | }); |
| | | return result; |
| | | }, |
| | | getStationCenter(stationId) { |
| | | if (stationId == null || !this.pixiStaMap) { return null; } |
| | | const sprite = this.pixiStaMap.get(parseInt(stationId, 10)); |
| | | if (!sprite) { return null; } |
| | | return { |
| | | x: sprite.x + sprite.width / 2, |
| | | y: sprite.y + sprite.height / 2 |
| | | }; |
| | | }, |
| | | drawTracePairs(graphics, stationIds, color, width, alpha) { |
| | | if (!graphics || !Array.isArray(stationIds) || stationIds.length < 2) { return; } |
| | | graphics.lineStyle({ width: width, color: color, alpha: alpha, cap: PIXI.LINE_CAP.ROUND, join: PIXI.LINE_JOIN.ROUND }); |
| | | for (let i = 1; i < stationIds.length; i++) { |
| | | const prev = this.getStationCenter(stationIds[i - 1]); |
| | | const curr = this.getStationCenter(stationIds[i]); |
| | | if (!prev || !curr) { continue; } |
| | | graphics.moveTo(prev.x, prev.y); |
| | | graphics.lineTo(curr.x, curr.y); |
| | | } |
| | | }, |
| | | drawTraceMarker(container, stationId, options) { |
| | | const point = this.getStationCenter(stationId); |
| | | if (!container || !point) { return null; } |
| | | const marker = new PIXI.Container(); |
| | | const ring = new PIXI.Graphics(); |
| | | const fill = new PIXI.Graphics(); |
| | | const radius = options && options.radius ? options.radius : 18; |
| | | const color = options && options.color != null ? options.color : 0x1d4ed8; |
| | | ring.lineStyle(3, color, 0.95); |
| | | ring.drawCircle(0, 0, radius); |
| | | fill.beginFill(color, 0.18); |
| | | fill.drawCircle(0, 0, Math.max(6, radius * 0.42)); |
| | | fill.endFill(); |
| | | marker.addChild(ring); |
| | | marker.addChild(fill); |
| | | marker.position.set(point.x, point.y); |
| | | container.addChild(marker); |
| | | return marker; |
| | | }, |
| | | buildPendingTraceSequence(trace) { |
| | | const pending = this.normalizeTraceStationIds(trace && trace.pendingStationIds); |
| | | const currentStationId = this.parseStationTaskNo(trace && trace.currentStationId); |
| | | if (pending.length === 0) { |
| | | return currentStationId > 0 ? [currentStationId] : []; |
| | | } |
| | | if (currentStationId > 0 && pending[0] !== currentStationId) { |
| | | return [currentStationId].concat(pending); |
| | | } |
| | | return pending; |
| | | }, |
| | | renderTraceOverlay() { |
| | | if (!this.traceOverlayContainer) { return; } |
| | | this.clearTraceOverlay(); |
| | | const trace = this.normalizeTraceOverlay(this.traceOverlay); |
| | | if (!trace || !this.pixiStaMap || this.pixiStaMap.size === 0) { return; } |
| | | |
| | | const graphics = new PIXI.Graphics(); |
| | | this.drawTracePairs(graphics, trace.passedStationIds, 0x2563eb, 5, 0.95); |
| | | this.drawTracePairs(graphics, this.buildPendingTraceSequence(trace), 0xf97316, 4, 0.9); |
| | | this.drawTracePairs(graphics, trace.latestAppendedPath, 0xfacc15, 7, 0.88); |
| | | this.traceOverlayContainer.addChild(graphics); |
| | | |
| | | const currentMarker = this.drawTraceMarker(this.traceOverlayContainer, trace.currentStationId, { |
| | | color: 0x2563eb, |
| | | radius: 20 |
| | | }); |
| | | if (currentMarker && window.gsap) { |
| | | this.tracePulseTween = window.gsap.to(currentMarker.scale, { |
| | | x: 1.18, |
| | | y: 1.18, |
| | | duration: 0.55, |
| | | repeat: -1, |
| | | yoyo: true, |
| | | ease: 'sine.inOut' |
| | | }); |
| | | } |
| | | |
| | | if (trace.blockedStationId > 0) { |
| | | const blockedMarker = this.drawTraceMarker(this.traceOverlayContainer, trace.blockedStationId, { |
| | | color: 0xdc2626, |
| | | radius: 22 |
| | | }); |
| | | if (blockedMarker) { |
| | | blockedMarker.alpha = 0.95; |
| | | } |
| | | } |
| | | }, |
| | | handleLoopCardEnter(loopItem) { |
| | | if (!loopItem) { return; } |
| | | this.hoverLoopNo = loopItem.loopNo; |
| | |
| | | } |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |