travel cities
This commit is contained in:
parent
ed09d683d3
commit
3d7e93947b
|
@ -99,7 +99,7 @@ const navigation = (() => {
|
||||||
currentWeather,
|
currentWeather,
|
||||||
new LatestObservations(1, 'latest-observations'),
|
new LatestObservations(1, 'latest-observations'),
|
||||||
new Hourly(2, 'hourly'),
|
new Hourly(2, 'hourly'),
|
||||||
new TravelForecast(3, 'travelForecast', false), // not active by default
|
new TravelForecast(3, 'travel', false), // not active by default
|
||||||
new RegionalForecast(4, 'regional-forecast'),
|
new RegionalForecast(4, 'regional-forecast'),
|
||||||
new LocalForecast(5, 'local-forecast'),
|
new LocalForecast(5, 'local-forecast'),
|
||||||
new ExtendedForecast(6, 'extended-forecast'),
|
new ExtendedForecast(6, 'extended-forecast'),
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
// travel forecast display
|
// travel forecast display
|
||||||
/* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, icons, luxon, TravelCities */
|
/* globals WeatherDisplay, utils, STATUS, UNITS, navigation, icons, luxon, TravelCities */
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
class TravelForecast extends WeatherDisplay {
|
class TravelForecast extends WeatherDisplay {
|
||||||
constructor(navId, elemId, defaultActive) {
|
constructor(navId, elemId, defaultActive) {
|
||||||
// special height and width for scrolling
|
// special height and width for scrolling
|
||||||
super(navId, elemId, 'Travel Forecast', defaultActive);
|
super(navId, elemId, 'Travel Forecast', defaultActive, true);
|
||||||
// pre-load background image (returns promise)
|
|
||||||
this.backgroundImage = utils.image.load('images/BackGround6_1.png');
|
|
||||||
|
|
||||||
// height of one city in the travel forecast
|
|
||||||
this.cityHeight = 72;
|
|
||||||
|
|
||||||
// set up the timing
|
// set up the timing
|
||||||
this.timing.baseDelay = 20;
|
this.timing.baseDelay = 20;
|
||||||
|
@ -18,7 +13,7 @@ class TravelForecast extends WeatherDisplay {
|
||||||
const pagesFloat = TravelCities.length / 4;
|
const pagesFloat = TravelCities.length / 4;
|
||||||
const pages = Math.floor(pagesFloat) - 2; // first page is already displayed, last page doesn't happen
|
const pages = Math.floor(pagesFloat) - 2; // first page is already displayed, last page doesn't happen
|
||||||
const extra = pages % 1;
|
const extra = pages % 1;
|
||||||
const timingStep = this.cityHeight * 4;
|
const timingStep = 75 * 4;
|
||||||
this.timing.delay = [150 + timingStep];
|
this.timing.delay = [150 + timingStep];
|
||||||
// add additional pages
|
// add additional pages
|
||||||
for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
|
for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
|
||||||
|
@ -69,47 +64,22 @@ class TravelForecast extends WeatherDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
async drawLongCanvas() {
|
async drawLongCanvas() {
|
||||||
// create the "long" canvas if necessary
|
// get the element and populate
|
||||||
if (!this.longCanvas) {
|
const list = this.elem.querySelector('.travel-lines');
|
||||||
this.longCanvas = document.createElement('canvas');
|
list.innerHTML = '';
|
||||||
this.longCanvas.width = 640;
|
|
||||||
this.longCanvas.height = 1728;
|
|
||||||
this.longContext = this.longCanvas.getContext('2d');
|
|
||||||
this.longCanvasGifs = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop all gifs
|
|
||||||
this.longCanvasGifs.forEach((gif) => gif.pause());
|
|
||||||
// delete the gifs
|
|
||||||
this.longCanvasGifs.length = 0;
|
|
||||||
|
|
||||||
// set up variables
|
// set up variables
|
||||||
const cities = this.data;
|
const cities = this.data;
|
||||||
|
|
||||||
// clean up existing gifs
|
const lines = cities.map((city) => {
|
||||||
this.gifs.forEach((gif) => gif.pause());
|
const fillValues = {};
|
||||||
// delete the gifs
|
|
||||||
this.gifs.length = 0;
|
|
||||||
|
|
||||||
this.longContext.clearRect(0, 0, this.longCanvas.width, this.longCanvas.height);
|
|
||||||
|
|
||||||
// draw the "long" canvas with all cities
|
|
||||||
draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, TravelCities.length * this.cityHeight);
|
|
||||||
|
|
||||||
for (let i = 0; i <= 4; i += 1) {
|
|
||||||
const y = i * 346;
|
|
||||||
draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040');
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(cities.map(async (city, index) => {
|
|
||||||
// calculate base y value
|
|
||||||
const y = 50 + this.cityHeight * index;
|
|
||||||
|
|
||||||
// city name
|
// city name
|
||||||
draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2);
|
fillValues.city = city;
|
||||||
|
|
||||||
// check for forecast data
|
// check for forecast data
|
||||||
if (city.icon) {
|
if (city.icon) {
|
||||||
|
fillValues.city = city.name;
|
||||||
// get temperatures and convert if necessary
|
// get temperatures and convert if necessary
|
||||||
let { low, high } = city;
|
let { low, high } = city;
|
||||||
|
|
||||||
|
@ -122,25 +92,16 @@ class TravelForecast extends WeatherDisplay {
|
||||||
const lowString = Math.round(low).toString();
|
const lowString = Math.round(low).toString();
|
||||||
const highString = Math.round(high).toString();
|
const highString = Math.round(high).toString();
|
||||||
|
|
||||||
const xLow = (500 - (lowString.length * 20));
|
fillValues.low = lowString;
|
||||||
draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', xLow, y, lowString, 2);
|
fillValues.high = highString;
|
||||||
|
|
||||||
const xHigh = (560 - (highString.length * 20));
|
fillValues.icon = { type: 'img', src: city.icon };
|
||||||
draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', xHigh, y, highString, 2);
|
|
||||||
|
|
||||||
this.longCanvasGifs.push(await utils.image.superGifAsync({
|
|
||||||
src: city.icon,
|
|
||||||
auto_play: true,
|
|
||||||
canvas: this.longCanvas,
|
|
||||||
x: 330,
|
|
||||||
y: y - 35,
|
|
||||||
max_width: 47,
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y - 18, 'NO TRAVEL', 2);
|
fillValues.error = 'NO TRAVEL DATA AVAILABLE';
|
||||||
draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y, 'DATA AVAILABLE', 2);
|
|
||||||
}
|
}
|
||||||
}));
|
return this.fillTemplate('travel-row', fillValues);
|
||||||
|
});
|
||||||
|
list.append(...lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
async drawCanvas() {
|
async drawCanvas() {
|
||||||
|
@ -151,18 +112,7 @@ class TravelForecast extends WeatherDisplay {
|
||||||
// set up variables
|
// set up variables
|
||||||
const cities = this.data;
|
const cities = this.data;
|
||||||
|
|
||||||
// draw the standard context
|
this.elem.querySelector('.header .title.dual .bottom').innerHTML = `For ${TravelForecast.getTravelCitiesDayName(cities)}`;
|
||||||
this.context.drawImage(await this.backgroundImage, 0, 0);
|
|
||||||
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
|
|
||||||
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
|
|
||||||
|
|
||||||
draw.titleText(this.context, 'Travel Forecast', `For ${TravelForecast.getTravelCitiesDayName(cities)}`);
|
|
||||||
|
|
||||||
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 455, 105, 'LOW', 2);
|
|
||||||
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 510, 105, 'HIGH', 2);
|
|
||||||
|
|
||||||
// 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.finishDraw();
|
this.finishDraw();
|
||||||
}
|
}
|
||||||
|
@ -180,17 +130,14 @@ class TravelForecast extends WeatherDisplay {
|
||||||
|
|
||||||
// base count change callback
|
// base count change callback
|
||||||
baseCountChange(count) {
|
baseCountChange(count) {
|
||||||
// get a fresh canvas
|
|
||||||
const longCanvas = this.getLongCanvas();
|
|
||||||
|
|
||||||
// calculate scroll offset and don't go past end
|
// calculate scroll offset and don't go past end
|
||||||
let offsetY = Math.min(longCanvas.height - 289, (count - 150));
|
let offsetY = Math.min(this.elem.querySelector('.travel-lines').getBoundingClientRect().height - 289, (count - 150));
|
||||||
|
|
||||||
// don't let offset go negative
|
// don't let offset go negative
|
||||||
if (offsetY < 0) offsetY = 0;
|
if (offsetY < 0) offsetY = 0;
|
||||||
|
|
||||||
// copy the scrolled portion of the canvas
|
// copy the scrolled portion of the canvas
|
||||||
this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289);
|
this.elem.querySelector('.main').scrollTo(0, offsetY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getTravelCitiesDayName(cities) {
|
static getTravelCitiesDayName(cities) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -22,7 +22,7 @@
|
||||||
&.right {
|
&.right {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
font-family: 'Star4000 Large';
|
font-family: 'Star4000 Large';
|
||||||
font-size: 20px;
|
font-size: 16pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
|
|
103
server/styles/scss/_travel.scss
Normal file
103
server/styles/scss/_travel.scss
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
@use 'shared/_colors'as c;
|
||||||
|
@use 'shared/_utils'as u;
|
||||||
|
|
||||||
|
.weather-display .main.travel {
|
||||||
|
&.main {
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
.column-headers {
|
||||||
|
background-color: c.$column-header;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-headers {
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 5;
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: inline-block;
|
||||||
|
font-family: 'Star4000 Small';
|
||||||
|
font-size: 24pt;
|
||||||
|
color: c.$column-header-text;
|
||||||
|
position: absolute;
|
||||||
|
top: -14px;
|
||||||
|
z-index: 5;
|
||||||
|
@include u.text-shadow();
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp {
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&.low {
|
||||||
|
left: 455px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.high {
|
||||||
|
left: 510px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.travel-lines {
|
||||||
|
min-height: 338px;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
background: repeating-linear-gradient(0deg, c.$gradient-main-background-2 0px,
|
||||||
|
c.$gradient-main-background-1 136px,
|
||||||
|
c.$gradient-main-background-1 202px,
|
||||||
|
c.$gradient-main-background-2 338px,
|
||||||
|
);
|
||||||
|
|
||||||
|
.travel-row {
|
||||||
|
font-family: 'Star4000 Large';
|
||||||
|
font-size: 24pt;
|
||||||
|
height: 72px;
|
||||||
|
color: c.$title-color;
|
||||||
|
@include u.text-shadow();
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
>div {
|
||||||
|
position: absolute;
|
||||||
|
white-space: pre;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.city {
|
||||||
|
left: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
left: 330px;
|
||||||
|
width: 70px;
|
||||||
|
text-align: center;
|
||||||
|
top: unset;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 47px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp {
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&.low {
|
||||||
|
left: 455px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.high {
|
||||||
|
left: 510px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
@import 'current-weather';
|
@import 'current-weather';
|
||||||
@import 'extended-forecast';
|
@import 'extended-forecast';
|
||||||
@import 'hourly';
|
@import 'hourly';
|
||||||
|
@import 'travel';
|
||||||
@import 'latest-observations';
|
@import 'latest-observations';
|
||||||
@import 'local-forecast';
|
@import 'local-forecast';
|
||||||
@import 'progress';
|
@import 'progress';
|
||||||
|
|
|
@ -92,6 +92,9 @@
|
||||||
<div id="hourly-html" class="weather-display">
|
<div id="hourly-html" class="weather-display">
|
||||||
<%- include('partials/hourly.ejs') %>
|
<%- include('partials/hourly.ejs') %>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="travel-html" class="weather-display">
|
||||||
|
<%- include('partials/travel.ejs') %>
|
||||||
|
</div>
|
||||||
<div id="current-weather-html" class="weather-display">
|
<div id="current-weather-html" class="weather-display">
|
||||||
<%- include('partials/current-weather.ejs') %>
|
<%- include('partials/current-weather.ejs') %>
|
||||||
</div>
|
</div>
|
||||||
|
|
16
views/partials/travel.ejs
Normal file
16
views/partials/travel.ejs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<%- include('header.ejs', {titleDual: {top: 'Travel Forecast', bottom: 'For '} , hasTime: true }) %>
|
||||||
|
<div class="main has-scroll travel">
|
||||||
|
<div class="column-headers">
|
||||||
|
<div class="temp low">LOW</div>
|
||||||
|
<div class="temp high">HIGH</div>
|
||||||
|
</div>
|
||||||
|
<div class="travel-lines">
|
||||||
|
<div class="travel-row template">
|
||||||
|
<div class="city"></div>
|
||||||
|
<div class="icon"><img /></div>
|
||||||
|
<div class="temp low"></div>
|
||||||
|
<div class="temp high"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%- include('scroll.ejs') %>
|
Loading…
Reference in a new issue