commit 82b6a3d784c7de061dc353b207a9e48d27aee964
parent e6097aa9e6af51ce42a7393a710b303e3ea0b815
Author: Katja (ctucx) <git@ctu.cx>
Date: Sat, 19 Apr 2025 00:20:54 +0200
parent e6097aa9e6af51ce42a7393a710b303e3ea0b815
Author: Katja (ctucx) <git@ctu.cx>
Date: Sat, 19 Apr 2025 00:20:54 +0200
journeysCanvas: use lit's `ref` directive to bind to canvas
2 files changed, 83 insertions(+), 87 deletions(-)
M
|
168
+++++++++++++++++++++++++++++++++++++++----------------------------------------
diff --git a/src/journeysCanvas.js b/src/journeysCanvas.js @@ -1,4 +1,5 @@ import { html } from 'lit'; +import { ref, createRef } from 'lit/directives/ref.js'; import { BaseView } from './baseView.js'; import { CustomDate } from './helpers.js'; @@ -50,8 +51,7 @@ export class JourneysCanvas extends BaseView { constructor () { super(); - this.canvasElement = null; - this.canvasContext = null; + this.canvasRef = createRef(); this.canvasState = { handlersConnected: false, textCache: {}, @@ -79,7 +79,7 @@ export class JourneysCanvas extends BaseView { this.canvasState.offsetX = (window.innerWidth / this.canvasState.dpr) > 600 ? 140 : 80; }; - getCanvas = () => html`<canvas @mousedown=${this.mouseDownHandler} @touchstart=${this.mouseDownHandler}></canvas>`; + getCanvas = () => html`<canvas @mousedown=${this.mouseDownHandler} @touchstart=${this.mouseDownHandler} ${ref(this.canvasRef)}></canvas>`; connectCanvas = () => { if (!this.canvasState.handlersConnected) { @@ -91,16 +91,7 @@ export class JourneysCanvas extends BaseView { window.addEventListener('zoom', this.resizeHandler); this.canvasState.handlersConnected = true; } - - if (this.canvasElement === null) { - const canvasElement = this.renderRoot.querySelector('canvas'); - - if (canvasElement !== null) { - this.canvasElement = canvasElement; - this.canvasContext = this.canvasElement.getContext('2d'); - this.resizeHandler(); - } - } + this.resizeHandler(); }; disconnectCanvas = () => { @@ -111,12 +102,14 @@ export class JourneysCanvas extends BaseView { window.removeEventListener('resize', this.resizeHandler); window.removeEventListener('zoom', this.resizeHandler); this.canvasState.handlersConnected = false; - this.canvasElement = null; - this.canvasContext = null; }; resizeHandler = () => { - if (this.canvasContext === null) return true; + const canvasElement = this.canvasRef.value; + + if (canvasElement === undefined) return true; + + const canvasContext = canvasElement.getContext('2d'); this.canvasState.dpr = window.devicePixelRatio || 1; @@ -125,14 +118,14 @@ export class JourneysCanvas extends BaseView { this.canvasState.rectWidthWithPadding = this.canvasState.rectWidth + 2 * this.canvasState.padding; const rect = this.renderRoot.querySelector('header').getBoundingClientRect(); - this.canvasElement.width = window.innerWidth * this.canvasState.dpr; - this.canvasElement.height = (window.innerHeight - rect.height) * this.canvasState.dpr; - this.canvasElement.style.width = `${window.innerWidth}px`; - this.canvasElement.style.height = `${window.innerHeight - rect.height - 4}px`; + canvasElement.width = window.innerWidth * this.canvasState.dpr; + canvasElement.height = (window.innerHeight - rect.height) * this.canvasState.dpr; + canvasElement.style.width = `${window.innerWidth}px`; + canvasElement.style.height = `${window.innerHeight - rect.height - 4}px`; - this.canvasContext.restore(); - this.canvasContext.save(); - this.canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); + canvasContext.restore(); + canvasContext.save(); + canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); this.canvasState.lastAnimationUpdate = 0; this.renderCanvas(); @@ -253,35 +246,38 @@ export class JourneysCanvas extends BaseView { }; renderCanvas = () => { - if (this.canvasContext === null) return; + const canvasElement = this.canvasRef.value; + if (canvasElement === undefined) return true; + + const canvasContext = canvasElement.getContext('2d'); const drawButton = mode => { const buttonPath = new Path2D('M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z'); - this.canvasContext.fillStyle = '#fff'; - this.canvasContext.shadowColor = '#00000080'; - this.canvasContext.save(); - this.canvasContext.scale(3, 3); - if (mode === 'earlier') this.canvasContext.translate(x / 3 - 15, this.canvasElement.height / this.canvasState.dpr / 6 - 24); - if (mode === 'later') this.canvasContext.translate(x / 3 + 5, this.canvasElement.height / this.canvasState.dpr / 6); - if (mode === 'earlier') this.canvasContext.rotate(-Math.PI*1.5); - if (mode === 'later') this.canvasContext.rotate(Math.PI*1.5); - this.canvasContext.fill(buttonPath); - this.canvasContext.restore(); - this.canvasContext.beginPath(); - if (mode === 'earlier') this.canvasContext.arc(x - 80, this.canvasElement.height / this.canvasState.dpr / 2 - 35,50,0,2*Math.PI); - if (mode === 'later') this.canvasContext.arc(x + 50, this.canvasElement.height / this.canvasState.dpr / 2 - 35,50,0,2*Math.PI); - this.canvasContext.fillStyle = '#ffffff40'; - this.canvasContext.fill(); - this.canvasContext.strokeStyle = '#00000020'; - this.canvasContext.stroke(); + canvasContext.fillStyle = '#fff'; + canvasContext.shadowColor = '#00000080'; + canvasContext.save(); + canvasContext.scale(3, 3); + if (mode === 'earlier') canvasContext.translate(x / 3 - 15, canvasElement.height / this.canvasState.dpr / 6 - 24); + if (mode === 'later') canvasContext.translate(x / 3 + 5, canvasElement.height / this.canvasState.dpr / 6); + if (mode === 'earlier') canvasContext.rotate(-Math.PI*1.5); + if (mode === 'later') canvasContext.rotate(Math.PI*1.5); + canvasContext.fill(buttonPath); + canvasContext.restore(); + canvasContext.beginPath(); + if (mode === 'earlier') canvasContext.arc(x - 80, canvasElement.height / this.canvasState.dpr / 2 - 35,50,0,2*Math.PI); + if (mode === 'later') canvasContext.arc(x + 50, canvasElement.height / this.canvasState.dpr / 2 - 35,50,0,2*Math.PI); + canvasContext.fillStyle = '#ffffff40'; + canvasContext.fill(); + canvasContext.strokeStyle = '#00000020'; + canvasContext.stroke(); } - this.canvasContext.clearRect(0, 0, this.canvasElement.width / this.canvasState.dpr, this.canvasElement.height / this.canvasState.dpr); + canvasContext.clearRect(0, 0, canvasElement.width / this.canvasState.dpr, canvasElement.height / this.canvasState.dpr); let x = this.canvasState.offsetX - this.viewState.indexOffset * this.canvasState.rectWidthWithPadding, y; const firstVisibleJourney = Math.max(0, Math.floor((-x + this.canvasState.padding) / this.canvasState.rectWidthWithPadding)); - const numVisibleJourneys = Math.ceil(this.canvasElement.width / this.canvasState.dpr / this.canvasState.rectWidthWithPadding); + const numVisibleJourneys = Math.ceil(canvasElement.width / this.canvasState.dpr / this.canvasState.rectWidthWithPadding); const visibleJourneys = this.viewState.journeys.slice(firstVisibleJourney, firstVisibleJourney + numVisibleJourneys); if (!visibleJourneys.length) return; @@ -291,7 +287,7 @@ export class JourneysCanvas extends BaseView { visibleJourneys.map(journey => journey.legs[journey.legs.length-1].plannedArrival) .concat(visibleJourneys.map(journey => journey.legs[journey.legs.length-1].arrival) )); - const targetScaleFactor = 1 / (targetLastArrival - targetFirstDeparture) * (this.canvasElement.height - 64 * this.canvasState.dpr) / this.canvasState.dpr; + const targetScaleFactor = 1 / (targetLastArrival - targetFirstDeparture) * (canvasElement.height - 64 * this.canvasState.dpr) / this.canvasState.dpr; const now = new Date(); const factor = Math.min(.3, (now - this.canvasState.lastAnimationUpdate) / 20); @@ -322,18 +318,18 @@ export class JourneysCanvas extends BaseView { let time = this.viewState.journeys[0].legs[0].plannedDeparture; - this.canvasContext.font = `${(window.innerWidth / this.canvasState.dpr) > 600 ? 20 : 15}px sans-serif`; - this.canvasContext.fillStyle = '#aaa'; + canvasContext.font = `${(window.innerWidth / this.canvasState.dpr) > 600 ? 20 : 15}px sans-serif`; + canvasContext.fillStyle = '#aaa'; while (time < this.canvasState.lastArrival) { const y = (time - this.canvasState.firstDeparture) * this.canvasState.scaleFactor + 32; - this.canvasContext.fillText(time.formatTime(), (window.innerWidth / this.canvasState.dpr) > 600 ? 30 : 10, y); - this.canvasContext.fillRect(0, y, this.canvasElement.width / this.canvasState.dpr, 1); + canvasContext.fillText(time.formatTime(), (window.innerWidth / this.canvasState.dpr) > 600 ? 30 : 10, y); + canvasContext.fillRect(0, y, canvasElement.width / this.canvasState.dpr, 1); time = new CustomDate(Number(time) + 3600000);//Math.floor(120/scaleFactor)); } - this.canvasContext.fillStyle = '#fa5'; + canvasContext.fillStyle = '#fa5'; y = (new Date() - this.canvasState.firstDeparture) * this.canvasState.scaleFactor + 32; - this.canvasContext.fillRect(0, y-2, this.canvasElement.width / this.canvasState.dpr, 5); + canvasContext.fillRect(0, y-2, canvasElement.width / this.canvasState.dpr, 5); if (this.viewState.earlierRef) drawButton('earlier'); @@ -346,10 +342,10 @@ export class JourneysCanvas extends BaseView { y = (leg.plannedDeparture - this.canvasState.firstDeparture) * this.canvasState.scaleFactor + 32; - this.canvasContext.fillStyle = '#44444480'; - this.canvasContext.strokeStyle = '#ffffff80'; - this.canvasContext.fillRect(x - this.canvasState.padding, y, this.canvasState.rectWidth, duration); - this.canvasContext.strokeRect(x - this.canvasState.padding, y, this.canvasState.rectWidth, duration); + canvasContext.fillStyle = '#44444480'; + canvasContext.strokeStyle = '#ffffff80'; + canvasContext.fillRect(x - this.canvasState.padding, y, this.canvasState.rectWidth, duration); + canvasContext.strokeRect(x - this.canvasState.padding, y, this.canvasState.rectWidth, duration); } }); @@ -370,47 +366,47 @@ export class JourneysCanvas extends BaseView { y = ((leg.departure || leg.plannedDeparture) - this.canvasState.firstDeparture) * this.canvasState.scaleFactor + 32; - this.canvasContext.shadowColor = '#00000060'; - if (!this.settingsState.disableCanvasBlur) this.canvasContext.shadowBlur = 5; + canvasContext.shadowColor = '#00000060'; + if (!this.settingsState.disableCanvasBlur) canvasContext.shadowBlur = 5; if (leg.walking || leg.transfer) { - this.canvasContext.fillStyle = '#777'; - this.canvasContext.fillRect(x + this.canvasState.rectWidth / 2 - this.canvasState.rectWidth / 10, y, this.canvasState.rectWidth / 5, duration); + canvasContext.fillStyle = '#777'; + canvasContext.fillRect(x + this.canvasState.rectWidth / 2 - this.canvasState.rectWidth / 10, y, this.canvasState.rectWidth / 5, duration); } else { - this.canvasContext.fillStyle = this.getColor('fill', leg); - this.canvasContext.fillRect(x, y, this.canvasState.rectWidth, duration); - this.canvasContext.strokeStyle = this.getColor('text', leg); - this.canvasContext.strokeRect(x, y, this.canvasState.rectWidth, duration); + canvasContext.fillStyle = this.getColor('fill', leg); + canvasContext.fillRect(x, y, this.canvasState.rectWidth, duration); + canvasContext.strokeStyle = this.getColor('text', leg); + canvasContext.strokeRect(x, y, this.canvasState.rectWidth, duration); } - if (!this.settingsState.disableCanvasBlur) this.canvasContext.shadowBlur = 0; + if (!this.settingsState.disableCanvasBlur) canvasContext.shadowBlur = 0; let preRenderedText = this.getTextCache(formatLineDisplayName(leg.line), this.getColor('text', leg)); let offset = duration / 2; if ((offset + preRenderedText.height / this.canvasState.dpr) < duration - 5) { - this.canvasContext.scale(1 / this.canvasState.dpr, 1 / this.canvasState.dpr); - this.canvasContext.drawImage(preRenderedText, this.canvasState.dpr * (x + 5), Math.floor(this.canvasState.dpr * (y + offset) - preRenderedText.height / 2.3)); - this.canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); + canvasContext.scale(1 / this.canvasState.dpr, 1 / this.canvasState.dpr); + canvasContext.drawImage(preRenderedText, this.canvasState.dpr * (x + 5), Math.floor(this.canvasState.dpr * (y + offset) - preRenderedText.height / 2.3)); + canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); offset += preRenderedText.height / this.canvasState.dpr / 1.3 + 5; } this.getTrainTypeTexts(leg).forEach(typeText => { const preRenderedTypeText = this.getTextCache(typeText, '#555'); if ((offset + preRenderedText.height / this.canvasState.dpr) < duration - 5) { - this.canvasContext.scale(1 / this.canvasState.dpr, 1 / this.canvasState.dpr); - this.canvasContext.drawImage(preRenderedTypeText, this.canvasState.dpr * (x + 5), Math.floor(this.canvasState.dpr * (y + offset) - preRenderedTypeText.height / 2.3)); - this.canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); + canvasContext.scale(1 / this.canvasState.dpr, 1 / this.canvasState.dpr); + canvasContext.drawImage(preRenderedTypeText, this.canvasState.dpr * (x + 5), Math.floor(this.canvasState.dpr * (y + offset) - preRenderedTypeText.height / 2.3)); + canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); offset += preRenderedText.height / this.canvasState.dpr / 2; } }); if (leg.cancelled) { - this.canvasContext.beginPath(); - this.canvasContext.moveTo(x, y); - this.canvasContext.lineTo(x + this.canvasState.rectWidth, y + duration); - this.canvasContext.strokeStyle = this.getColor('cancelFill', leg); - this.canvasContext.lineWidth = 5; - this.canvasContext.stroke(); - this.canvasContext.lineWidth = 1; + canvasContext.beginPath(); + canvasContext.moveTo(x, y); + canvasContext.lineTo(x + this.canvasState.rectWidth, y + duration); + canvasContext.strokeStyle = this.getColor('cancelFill', leg); + canvasContext.lineWidth = 5; + canvasContext.stroke(); + canvasContext.lineWidth = 1; } /* draw journey start and end time */ @@ -420,22 +416,22 @@ export class JourneysCanvas extends BaseView { if (journey.legs.indexOf(leg) == 0) times.push([leg.arrival || leg.plannedArrival, y + duration + 7.5]); times.forEach(([time, y]) => { preRenderedText = this.getTextCache(time.formatTime(), '#fff', 15); - this.canvasContext.scale(1 / this.canvasState.dpr, 1 / this.canvasState.dpr); - this.canvasContext.drawImage(preRenderedText, Math.ceil(this.canvasState.dpr * (x + ((this.canvasState.rectWidth - preRenderedText.width/this.canvasState.dpr)) / 2)), this.canvasState.dpr * (y - 7.5)); - this.canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); + canvasContext.scale(1 / this.canvasState.dpr, 1 / this.canvasState.dpr); + canvasContext.drawImage(preRenderedText, Math.ceil(this.canvasState.dpr * (x + ((this.canvasState.rectWidth - preRenderedText.width/this.canvasState.dpr)) / 2)), this.canvasState.dpr * (y - 7.5)); + canvasContext.scale(this.canvasState.dpr, this.canvasState.dpr); }); if (leg.loadFactor && duration > 20) { - this.canvasContext.shadowColor = '#00000090'; - if (!this.settingsState.disableCanvasBlur) this.canvasContext.shadowBlur = 2; + canvasContext.shadowColor = '#00000090'; + if (!this.settingsState.disableCanvasBlur) canvasContext.shadowBlur = 2; [ "#777", "#aaa", "#aaa" ]; for (let i = 0; i < 3; i++) { - this.canvasContext.beginPath(); - this.canvasContext.fillStyle = this.getColor('loadFactor', leg.loadFactor, i); - this.canvasContext.arc(x + (i + 3) * this.canvasState.rectWidth / 8, y + duration - 9.5, 5, 0, 2 * Math.PI, false); - this.canvasContext.fill(); + canvasContext.beginPath(); + canvasContext.fillStyle = this.getColor('loadFactor', leg.loadFactor, i); + canvasContext.arc(x + (i + 3) * this.canvasState.rectWidth / 8, y + duration - 9.5, 5, 0, 2 * Math.PI, false); + canvasContext.fill(); } - if (!this.settingsState.disableCanvasBlur) this.canvasContext.shadowBlur = 0; + if (!this.settingsState.disableCanvasBlur) canvasContext.shadowBlur = 0; } x -= xOffset;
diff --git a/src/journeysView.js b/src/journeysView.js @@ -77,7 +77,7 @@ export class JourneysView extends JourneysCanvas { } if (this.mode === 'canvas') { - if (this.canvas === null) this.connectCanvas(); + this.connectCanvas(); if (previous.has('viewState') && this.viewState !== null) { this.renderCanvas(); await this.getCoachSequences();