forward/back navigation through travel forecast with timing
This commit is contained in:
parent
f27db7c66e
commit
81378359c9
|
@ -10,7 +10,7 @@ This project aims to bring back the feel of the 90's with a weather forecast tha
|
||||||
|
|
||||||
This project is based on the work of [Mike Battaglia](https://github.com/vbguyny/ws4kp). It was forked from his work in August 2020.
|
This project is based on the work of [Mike Battaglia](https://github.com/vbguyny/ws4kp). It was forked from his work in August 2020.
|
||||||
|
|
||||||
* Mike Battaglia For the original project and all of the code which draws the weather displays. This code remains largely intact and was a huge amount of work to get exactly right.
|
* Mike Battaglia for the original project and all of the code which draws the weather displays. This code remains largely intact and was a huge amount of work to get exactly right. He's also responsible for all of the background graphics including the maps used in the application.
|
||||||
* The team at [TWCClassics](https://twcclassics.com/) for several resources.
|
* The team at [TWCClassics](https://twcclassics.com/) for several resources.
|
||||||
* A [font](https://twcclassics.com/downloads.html) set used on the original WeatherStar 4000
|
* A [font](https://twcclassics.com/downloads.html) set used on the original WeatherStar 4000
|
||||||
* [Icon](https://twcclassics.com/downloads.html) sets
|
* [Icon](https://twcclassics.com/downloads.html) sets
|
||||||
|
|
|
@ -15,6 +15,7 @@ class LatestObservations extends WeatherDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData(weatherParameters) {
|
async getData(weatherParameters) {
|
||||||
|
super.getData();
|
||||||
// calculate distance to each station
|
// calculate distance to each station
|
||||||
const stationsByDistance = Object.keys(_StationInfo).map(key => {
|
const stationsByDistance = Object.keys(_StationInfo).map(key => {
|
||||||
const station = _StationInfo[key];
|
const station = _StationInfo[key];
|
||||||
|
|
|
@ -9,15 +9,30 @@ class TravelForecast extends WeatherDisplay {
|
||||||
// pre-load background image (returns promise)
|
// pre-load background image (returns promise)
|
||||||
this.backgroundImage = utils.image.load('images/BackGround6_1.png');
|
this.backgroundImage = utils.image.load('images/BackGround6_1.png');
|
||||||
|
|
||||||
|
// height of one city in the travel forecast
|
||||||
|
this.cityHeight = 72;
|
||||||
|
|
||||||
|
// set up the timing
|
||||||
|
this.timing.baseDelay = 20;
|
||||||
|
// page sizes are 4 cities, calculate the number of pages necessary plus overflow
|
||||||
|
const pagesFloat = _TravelCities.length/4;
|
||||||
|
const pages = Math.floor(pagesFloat) - 1; // first page is already displayed
|
||||||
|
const extra = pages%1;
|
||||||
|
const timingStep = this.cityHeight*4;
|
||||||
|
this.timing.delay = [150];
|
||||||
|
// add additional pages
|
||||||
|
for (let i = 0; i < pages; i++) this.timing.delay.push(timingStep);
|
||||||
|
// add the extra (not exactly 4 pages portion)
|
||||||
|
if (extra !== 0) this.timing.delay.push(Math.round(this.extra*this.cityHeight));
|
||||||
|
// add the final 3 second delay
|
||||||
|
this.timing.delay.push(150);
|
||||||
|
|
||||||
// get the data
|
// get the data
|
||||||
this.getData(weatherParameters);
|
this.getData(weatherParameters);
|
||||||
|
|
||||||
// scrolling tracking
|
|
||||||
this.scrollCount = 0;
|
|
||||||
this.endDelay = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
|
super.getData();
|
||||||
const forecastPromises = _TravelCities.map(async city => {
|
const forecastPromises = _TravelCities.map(async city => {
|
||||||
try {
|
try {
|
||||||
// get point then forecast
|
// get point then forecast
|
||||||
|
@ -70,7 +85,7 @@ class TravelForecast extends WeatherDisplay {
|
||||||
this.longContext.clearRect(0,0,this.longCanvas.width,this.longCanvas.height);
|
this.longContext.clearRect(0,0,this.longCanvas.width,this.longCanvas.height);
|
||||||
|
|
||||||
// draw the "long" canvas with all cities
|
// draw the "long" canvas with all cities
|
||||||
draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, 1728);
|
draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, _TravelCities.length*this.cityHeight);
|
||||||
|
|
||||||
for (let i = 0; i <= 4; i++) {
|
for (let i = 0; i <= 4; i++) {
|
||||||
const y = i * 346;
|
const y = i * 346;
|
||||||
|
@ -79,7 +94,7 @@ class TravelForecast extends WeatherDisplay {
|
||||||
|
|
||||||
await Promise.all(cities.map(async (city, index) => {
|
await Promise.all(cities.map(async (city, index) => {
|
||||||
// calculate base y value
|
// calculate base y value
|
||||||
const y = 50+72*index;
|
const y = 50+this.cityHeight*index;
|
||||||
|
|
||||||
// city name
|
// city name
|
||||||
draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2);
|
draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2);
|
||||||
|
@ -134,34 +149,30 @@ class TravelForecast extends WeatherDisplay {
|
||||||
// copy the scrolled portion of the canvas for the initial run before the scrolling starts
|
// copy the scrolled portion of the canvas for the initial run before the scrolling starts
|
||||||
this.context.drawImage(this.longCanvas, 0, 0, 640, 289, 0, 110, 640, 289);
|
this.context.drawImage(this.longCanvas, 0, 0, 640, 289, 0, 110, 640, 289);
|
||||||
|
|
||||||
// set up scrolling one time
|
|
||||||
if (!this.scrollInterval) {
|
|
||||||
this.scrollInterval = window.setInterval(() => {
|
|
||||||
if (this.isActive()) {
|
|
||||||
// get a fresh canvas
|
|
||||||
const longCanvas = this.getLongCanvas();
|
|
||||||
// increment scrolling
|
|
||||||
this.scrollCount++;
|
|
||||||
// wait 3 seconds at begining
|
|
||||||
if (this.scrollCount < 150) return;
|
|
||||||
// calculate scroll offset and don't go past end of canvas
|
|
||||||
const offsetY = Math.min(longCanvas.height-289, (this.scrollCount-150));
|
|
||||||
// copy the scrolled portion of the canvas
|
|
||||||
this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289);
|
|
||||||
// track end of scrolling for 3 seconds
|
|
||||||
if (offsetY >= longCanvas.height-289) this.endDelay++;
|
|
||||||
// TODO: report playback done
|
|
||||||
} else {
|
|
||||||
// reset scroll to top of image
|
|
||||||
this.scrollCount = 0;
|
|
||||||
this.endDelay = 0;
|
|
||||||
}
|
|
||||||
}, 20);
|
|
||||||
}
|
|
||||||
this.finishDraw();
|
this.finishDraw();
|
||||||
this.setStatus(STATUS.loaded);
|
this.setStatus(STATUS.loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// screen index change callback just runs the base count callback
|
||||||
|
screenIndexChange() {
|
||||||
|
this.baseCountChange(this.navBaseCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// base count change callback
|
||||||
|
baseCountChange(count) {
|
||||||
|
// get a fresh canvas
|
||||||
|
const longCanvas = this.getLongCanvas();
|
||||||
|
|
||||||
|
// calculate scroll offset and don't go past end
|
||||||
|
let offsetY = Math.min(longCanvas.height-289, (count-this.timing.delay[0]));
|
||||||
|
|
||||||
|
// don't let offset go negative
|
||||||
|
if (offsetY < 0) offsetY = 0;
|
||||||
|
|
||||||
|
// copy the scrolled portion of the canvas
|
||||||
|
this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289);
|
||||||
|
}
|
||||||
|
|
||||||
getTravelCitiesDayName(cities) {
|
getTravelCitiesDayName(cities) {
|
||||||
const {DateTime} = luxon;
|
const {DateTime} = luxon;
|
||||||
// effectively returns early on the first found date
|
// effectively returns early on the first found date
|
||||||
|
|
|
@ -63,6 +63,22 @@ class WeatherDisplay {
|
||||||
this.data = undefined;
|
this.data = undefined;
|
||||||
// set status
|
// set status
|
||||||
this.setStatus(STATUS.loading);
|
this.setStatus(STATUS.loading);
|
||||||
|
|
||||||
|
// set up the timing delays
|
||||||
|
if (Array.isArray(this.timing.delay) && typeof this.timing.delay[0] === 'number') {
|
||||||
|
// array is defined as how long each screen should be displayed. This needs to be converted into total time for use here
|
||||||
|
if (!this.timing.fullDelay) {
|
||||||
|
let sum = 0;
|
||||||
|
this.timing.fullDelay = this.timing.delay.map(val => {
|
||||||
|
const calc = sum + val;
|
||||||
|
sum += val;
|
||||||
|
return calc;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update total screens
|
||||||
|
if (Array.isArray(this.timing.delay)) this.timing.totalScreens = this.timing.delay.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawCanvas() {
|
drawCanvas() {
|
||||||
|
@ -216,6 +232,9 @@ class WeatherDisplay {
|
||||||
// reset timing
|
// reset timing
|
||||||
this.startNavCount(navigation.isPlaying());
|
this.startNavCount(navigation.isPlaying());
|
||||||
|
|
||||||
|
// if there was a command the canvas has already been drawn
|
||||||
|
if (navCmd) return;
|
||||||
|
|
||||||
// refresh the canvas (incase the screen index changed)
|
// refresh the canvas (incase the screen index changed)
|
||||||
if (navCmd) this.drawCanvas();
|
if (navCmd) this.drawCanvas();
|
||||||
}
|
}
|
||||||
|
@ -244,8 +263,8 @@ class WeatherDisplay {
|
||||||
// increment the base count
|
// increment the base count
|
||||||
this.navBaseCount++;
|
this.navBaseCount++;
|
||||||
|
|
||||||
// update total screens
|
// call base count change if available for this function
|
||||||
if (Array.isArray(this.timing.delay)) this.timing.totalScreens = this.timing.delay.length;
|
if (this.baseCountChange) this.baseCountChange(this.navBaseCount);
|
||||||
|
|
||||||
// determine type of timing
|
// determine type of timing
|
||||||
// simple delay
|
// simple delay
|
||||||
|
@ -253,6 +272,16 @@ class WeatherDisplay {
|
||||||
this.navNext();
|
this.navNext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// array of timing integers
|
||||||
|
if (Array.isArray(this.timing.delay) && typeof this.timing.delay[0] === 'number') {
|
||||||
|
// scan the array for a matching number and calculate new screen index from the number
|
||||||
|
const timingMatch = this.timing.fullDelay.indexOf(this.navBaseCount);
|
||||||
|
// if not found return
|
||||||
|
if (timingMatch < 0) return;
|
||||||
|
// navigate to the next screen
|
||||||
|
this.navNext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// navigate to next screen
|
// navigate to next screen
|
||||||
|
@ -260,20 +289,24 @@ class WeatherDisplay {
|
||||||
// check for special 'first frame' command
|
// check for special 'first frame' command
|
||||||
if (command === navigation.msg.command.firstFrame) {
|
if (command === navigation.msg.command.firstFrame) {
|
||||||
this.resetNavBaseCount();
|
this.resetNavBaseCount();
|
||||||
this.drawCanvas();
|
} else {
|
||||||
return;
|
// increment screen index
|
||||||
|
this.screenIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment screen index
|
|
||||||
this.screenIndex++;
|
|
||||||
// test for end reached
|
// test for end reached
|
||||||
if (this.screenIndex >= this.timing.totalScreens) {
|
if (this.screenIndex >= this.timing.totalScreens) {
|
||||||
|
this.screenIndex = this.timing.totalScreens - 1;
|
||||||
this.sendNavDisplayMessage(navigation.msg.response.next);
|
this.sendNavDisplayMessage(navigation.msg.response.next);
|
||||||
this.stopNavBaseCount();
|
this.stopNavBaseCount();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if the end was not reached, update the canvas
|
this.baseCountFromScreenIndex();
|
||||||
this.drawCanvas();
|
// if the end was not reached, update the canvas (typical), or run a callback (atypical)
|
||||||
|
if (!this.screenIndexChange) {
|
||||||
|
this.drawCanvas();
|
||||||
|
} else {
|
||||||
|
this.screenIndexChange(this.screenIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// navigate to previous screen
|
// navigate to previous screen
|
||||||
|
@ -281,19 +314,36 @@ class WeatherDisplay {
|
||||||
// check for special 'last frame' command
|
// check for special 'last frame' command
|
||||||
if (command === navigation.msg.command.lastFrame) {
|
if (command === navigation.msg.command.lastFrame) {
|
||||||
this.screenIndex = this.timing.totalScreens-1;
|
this.screenIndex = this.timing.totalScreens-1;
|
||||||
this.drawCanvas();
|
} else {
|
||||||
return;
|
// decrement screen index
|
||||||
|
this.screenIndex--;
|
||||||
}
|
}
|
||||||
// decrement screen index
|
|
||||||
this.screenIndex--;
|
|
||||||
|
|
||||||
// test for end reached
|
// test for end reached
|
||||||
if (this.screenIndex < 0) {
|
if (this.screenIndex < 0) {
|
||||||
|
this.screenIndex = 0;
|
||||||
this.sendNavDisplayMessage(navigation.msg.response.previous);
|
this.sendNavDisplayMessage(navigation.msg.response.previous);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if the end was not reached, update the canvas
|
this.baseCountFromScreenIndex();
|
||||||
this.drawCanvas();
|
// if the end was not reached, update the canvas (typical), or run a callback (atypical)
|
||||||
|
if (!this.screenIndexChange) {
|
||||||
|
this.drawCanvas();
|
||||||
|
} else {
|
||||||
|
this.screenIndexChange(this.screenIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate a baseCount from the screen index for the array timings
|
||||||
|
baseCountFromScreenIndex() {
|
||||||
|
if (!Array.isArray(this.timing.delay)) return;
|
||||||
|
// first screen starts at zero
|
||||||
|
if (this.screenIndex === 0) {
|
||||||
|
this.navBaseCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// otherwise return one more than the previous sum
|
||||||
|
this.navBaseCount = this.timing.fullDelay[this.screenIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
// start and stop base counter
|
// start and stop base counter
|
||||||
|
|
Loading…
Reference in a new issue