ctucx.git: trainsearch

web based trip-planner, fork of https://cyberchaos.dev/yuka/trainsearch

commit d199240e69d2638fa84f28db8f42b57c59598b4e
parent 124306bbe3b9fac35f73f41ec2d25dc8b13dab2f
Author: Yureka <yuka@yuka.dev>
Date: Wed, 18 Aug 2021 16:13:12 +0200

add canvas animations
1 file changed, 36 insertions(+), 5 deletions(-)
M
client/src/canvas.js
|
41
++++++++++++++++++++++++++++++++++++-----
diff --git a/client/src/canvas.js b/client/src/canvas.js
@@ -123,6 +123,8 @@ const updateTextCache = () => {
 	}
 };
 
+let lastAnimationUpdate = 0, firstDeparture = 0, scaleFactor = 0, lastArrival = 0;
+let animationInterval;
 const renderJourneys = () => {
 	ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
 	let x = canvasState.offsetX - canvasState.indexOffset * rectWidthWithPadding, y;

@@ -133,13 +135,40 @@ const renderJourneys = () => {
 
 	if (!visibleJourneys.length) return;
 
-	let firstDeparture = visibleJourneys[0].legs[0].plannedDeparture;
-	let lastArrival = Math.max.apply(Math,
+	const targetFirstDeparture = Number(visibleJourneys[0].legs[0].plannedDeparture);
+	const targetLastArrival = Math.max.apply(Math,
 		visibleJourneys.map(journey => journey.legs[journey.legs.length-1].plannedArrival)
 		.concat(visibleJourneys.map(journey => journey.legs[journey.legs.length-1].arrival)
 	));
 
-	let scaleFactor = 1/(lastArrival - firstDeparture) * (canvas.height - 64 * dpr) / dpr;
+	const now = new Date();
+	const factor = Math.min(.3, (now - lastAnimationUpdate) / 20);
+	if (!lastAnimationUpdate) {
+		firstDeparture = Number(targetFirstDeparture);
+		lastArrival = Number(targetLastArrival);
+	} else {
+		firstDeparture = firstDeparture + (targetFirstDeparture - firstDeparture) * factor;
+		lastArrival = lastArrival + (targetLastArrival - lastArrival) * factor;
+	}
+	const targetScaleFactor = 1/(lastArrival - firstDeparture) * (canvas.height - 64 * dpr) / dpr;
+	if (!lastAnimationUpdate) {
+		scaleFactor = targetScaleFactor;
+	} else {
+		scaleFactor = scaleFactor + (targetScaleFactor - scaleFactor) * factor;
+	}
+	lastAnimationUpdate = now;
+
+	if (Math.abs(scaleFactor - targetScaleFactor) > 1
+	  || Math.abs(firstDeparture - targetFirstDeparture) > 1
+	  || Math.abs(lastArrival - targetLastArrival) > 1
+	) {
+		if (!animationInterval) animationInterval = setInterval(() => renderJourneys(), 16.6);
+	} else {
+		if (animationInterval) {
+			clearInterval(animationInterval);
+			animationInterval = null;
+		}
+	}
 
 	let time = canvasState.journeys[0].legs[0].plannedDeparture;
 

@@ -149,7 +178,7 @@ const renderJourneys = () => {
 		let y = (time - firstDeparture) * scaleFactor + 32;
 		ctx.fillText(formatTime(time), (window.innerWidth / dpr) > 600 ? 30 : 10, y);
 		ctx.fillRect(0, y, canvas.width / dpr, 1);
-		time = new Date(Number(time) + Math.floor(120/scaleFactor));
+		time = new Date(Number(time) + 3600000);//Math.floor(120/scaleFactor));
 	}
 	ctx.fillStyle = '#fa5';
 	y = (new Date() / 1000 - firstDeparture) * scaleFactor + 32;

@@ -287,6 +316,7 @@ const resizeHandler = () => {
 	ctx.save();
 	ctx.scale(dpr, dpr);
 
+	lastAnimationUpdate = 0;
 	renderJourneys();
 };
 

@@ -306,6 +336,7 @@ const mouseUpHandler = (evt) => {
 	}
 
 	canvasState.dragging = false;
+	canvasState.isClick = false;
 };
 
 const mouseDownHandler = (evt) => {

@@ -321,7 +352,7 @@ const mouseMoveHandler = (evt) => {
 		evt.preventDefault();
 		let x = evt.x || evt.changedTouches[0].pageX;
 		canvasState.offsetX = canvasState.dragStartOffset - (canvasState.dragStartMouse - x);
-		if (Math.abs(canvasState.dragStartMouse - x) < 20) canvasState.isClick = false;
+		if (Math.abs(canvasState.dragStartMouse - x) > 20) canvasState.isClick = false;
 		renderJourneys();
 		return true;
 	}