mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-17 09:09:30 -07:00
more html
This commit is contained in:
@@ -69,7 +69,7 @@ class CurrentWeather extends WeatherDisplay {
|
||||
data.Temperature = Math.round(observations.temperature.value);
|
||||
data.TemperatureUnit = 'C';
|
||||
data.DewPoint = Math.round(observations.dewpoint.value);
|
||||
data.Ceiling = Math.round(observations.cloudLayers[0].base.value);
|
||||
data.Ceiling = Math.round(observations.cloudLayers[0]?.base?.value ?? 0);
|
||||
data.CeilingUnit = 'm.';
|
||||
data.Visibility = Math.round(observations.visibility.value / 1000);
|
||||
data.VisibilityUnit = ' km.';
|
||||
@@ -190,15 +190,17 @@ class CurrentWeather extends WeatherDisplay {
|
||||
draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 560, 365, data.WindChill + String.fromCharCode(176), 2, 'right');
|
||||
}
|
||||
|
||||
if (data.Icon) {
|
||||
// get main icon
|
||||
this.gifs.push(await utils.image.superGifAsync({
|
||||
src: data.Icon,
|
||||
auto_play: true,
|
||||
canvas: this.canvas,
|
||||
x: 140,
|
||||
y: 175,
|
||||
max_width: 126,
|
||||
}));
|
||||
this.gifs.push(await utils.image.superGifAsync({
|
||||
src: data.Icon,
|
||||
auto_play: true,
|
||||
canvas: this.canvas,
|
||||
x: 140,
|
||||
y: 175,
|
||||
max_width: 126,
|
||||
}));
|
||||
}
|
||||
|
||||
this.finishDraw();
|
||||
}
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
// hourly forecast list
|
||||
/* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, icons, luxon */
|
||||
/* globals WeatherDisplay, utils, STATUS, UNITS, navigation, icons, luxon */
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
class Hourly extends WeatherDisplay {
|
||||
constructor(navId, elemId, defaultActive) {
|
||||
// special height and width for scrolling
|
||||
super(navId, elemId, 'Hourly Forecast', defaultActive);
|
||||
// pre-load background image (returns promise)
|
||||
this.backgroundImage = utils.image.load('images/BackGround6_1.png');
|
||||
|
||||
// height of one hour in the forecast
|
||||
this.hourHeight = 72;
|
||||
super(navId, elemId, 'Hourly Forecast', defaultActive, true);
|
||||
|
||||
// set up the timing
|
||||
this.timing.baseDelay = 20;
|
||||
// 24 hours = 6 pages
|
||||
const pages = 4; // first page is already displayed, last page doesn't happen
|
||||
const timingStep = this.hourHeight * 4;
|
||||
const timingStep = 75 * 4;
|
||||
this.timing.delay = [150 + timingStep];
|
||||
// add additional pages
|
||||
for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
|
||||
@@ -114,52 +109,25 @@ class Hourly extends WeatherDisplay {
|
||||
}
|
||||
|
||||
async drawLongCanvas() {
|
||||
// create the "long" canvas if necessary
|
||||
if (!this.longCanvas) {
|
||||
this.longCanvas = document.createElement('canvas');
|
||||
this.longCanvas.width = 640;
|
||||
this.longCanvas.height = 24 * this.hourHeight;
|
||||
this.longContext = this.longCanvas.getContext('2d');
|
||||
this.longCanvasGifs = [];
|
||||
}
|
||||
|
||||
// stop all gifs
|
||||
this.longCanvasGifs.forEach((gif) => gif.pause());
|
||||
// delete the gifs
|
||||
this.longCanvasGifs.length = 0;
|
||||
|
||||
// clean up existing gifs
|
||||
this.gifs.forEach((gif) => gif.pause());
|
||||
// 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, 24 * this.hourHeight);
|
||||
|
||||
for (let i = 0; i <= 4; i += 1) {
|
||||
const y = i * 346;
|
||||
draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040');
|
||||
}
|
||||
// get the list element and populate
|
||||
const list = this.elem.querySelector('.hourly-lines');
|
||||
list.innerHTML = '';
|
||||
|
||||
const startingHour = luxon.DateTime.local();
|
||||
|
||||
await Promise.all(this.data.map(async (data, index) => {
|
||||
// calculate base y value
|
||||
const y = 50 + this.hourHeight * index;
|
||||
|
||||
const lines = this.data.map((data, index) => {
|
||||
const line = this.templates['hourly-row'].cloneNode(true);
|
||||
// hour
|
||||
const hour = startingHour.plus({ hours: index });
|
||||
const formattedHour = hour.toLocaleString({ weekday: 'short', hour: 'numeric' });
|
||||
draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, formattedHour, 2);
|
||||
line.querySelector('.hour').innerHTML = formattedHour;
|
||||
|
||||
// temperatures, convert to strings with no decimal
|
||||
const temperature = Math.round(data.temperature).toString().padStart(3);
|
||||
const feelsLike = Math.round(data.apparentTemperature).toString().padStart(3);
|
||||
draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', 390, y, temperature, 2, 'center');
|
||||
line.querySelector('.temp').innerHTML = temperature;
|
||||
// only plot apparent temperature if there is a difference
|
||||
if (temperature !== feelsLike) draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', 470, y, feelsLike, 2, 'center');
|
||||
if (temperature !== feelsLike) line.querySelector('.like').innerHTML = feelsLike;
|
||||
|
||||
// wind
|
||||
let wind = 'Calm';
|
||||
@@ -167,44 +135,25 @@ class Hourly extends WeatherDisplay {
|
||||
const windSpeed = Math.round(data.windSpeed).toString();
|
||||
wind = data.windDirection + (Array(6 - data.windDirection.length - windSpeed.length).join(' ')) + windSpeed;
|
||||
}
|
||||
draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', 580, y, wind, 2, 'center');
|
||||
line.querySelector('.wind').innerHTML = wind;
|
||||
|
||||
this.longCanvasGifs.push(await utils.image.superGifAsync({
|
||||
src: data.icon,
|
||||
auto_play: true,
|
||||
canvas: this.longCanvas,
|
||||
x: 290,
|
||||
y: y - 35,
|
||||
max_width: 47,
|
||||
}));
|
||||
}));
|
||||
// image
|
||||
line.querySelector('.icon img').src = data.icon;
|
||||
|
||||
return line;
|
||||
});
|
||||
|
||||
list.append(...lines);
|
||||
}
|
||||
|
||||
async drawCanvas() {
|
||||
// there are technically 2 canvases: the standard canvas and the extra-long canvas that contains the complete
|
||||
// list of cities. The second canvas is copied into the standard canvas to create the scroll
|
||||
drawCanvas() {
|
||||
super.drawCanvas();
|
||||
|
||||
// draw the standard context
|
||||
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, 'Hourly Forecast');
|
||||
|
||||
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 390, 105, 'TEMP', 2, 'center');
|
||||
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 470, 105, 'LIKE', 2, 'center');
|
||||
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 580, 105, 'WIND', 2, 'center');
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
async showCanvas() {
|
||||
// special to travel forecast to draw the remainder of the canvas
|
||||
await this.drawCanvas();
|
||||
showCanvas() {
|
||||
// special to hourly to draw the remainder of the canvas
|
||||
this.drawCanvas();
|
||||
super.showCanvas();
|
||||
}
|
||||
|
||||
@@ -215,17 +164,14 @@ class Hourly extends WeatherDisplay {
|
||||
|
||||
// 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 - 150));
|
||||
let offsetY = Math.min(this.elem.querySelector('.hourly-lines').getBoundingClientRect().height - 289, (count - 150));
|
||||
|
||||
// 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);
|
||||
this.elem.querySelector('.main').scrollTo(0, offsetY);
|
||||
}
|
||||
|
||||
static getTravelCitiesDayName(cities) {
|
||||
@@ -241,9 +187,4 @@ class Hourly extends WeatherDisplay {
|
||||
return dayName;
|
||||
}, '');
|
||||
}
|
||||
|
||||
// necessary to get the lastest long canvas when scrolling
|
||||
getLongCanvas() {
|
||||
return this.longCanvas;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,8 @@ const icons = (() => {
|
||||
};
|
||||
|
||||
const getWeatherIconFromIconLink = (link, _isNightTime) => {
|
||||
if (!link) return;
|
||||
|
||||
// internal function to add path to returned icon
|
||||
const addPath = (icon) => `images/${icon}`;
|
||||
// extract day or night if not provided
|
||||
|
||||
@@ -94,8 +94,10 @@ class Progress extends WeatherDisplay {
|
||||
}
|
||||
|
||||
canvasClick(e) {
|
||||
const x = e.offsetX;
|
||||
const y = e.offsetY;
|
||||
// un-scale
|
||||
const scale = e.target.getBoundingClientRect().width / e.target.width;
|
||||
const x = e.offsetX / scale;
|
||||
const y = e.offsetY / scale;
|
||||
// eliminate off canvas and outside area clicks
|
||||
if (!this.isActive()) return;
|
||||
if (y < 100 || y > 410) return;
|
||||
|
||||
@@ -286,8 +286,15 @@ const utils = (() => {
|
||||
}
|
||||
};
|
||||
|
||||
const elemForEach = (selector, callback) => {
|
||||
[...document.querySelectorAll(selector)].forEach(callback);
|
||||
};
|
||||
|
||||
// return an orderly object
|
||||
return {
|
||||
elem: {
|
||||
forEach: elemForEach,
|
||||
},
|
||||
image: {
|
||||
load: loadImg,
|
||||
superGifAsync,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// base weather display class
|
||||
|
||||
/* globals navigation, utils, draw, UNITS, luxon, currentWeatherScroll */
|
||||
/* globals navigation, utils, luxon, currentWeatherScroll */
|
||||
|
||||
const STATUS = {
|
||||
loading: Symbol('loading'),
|
||||
@@ -12,7 +12,7 @@ const STATUS = {
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
class WeatherDisplay {
|
||||
constructor(navId, elemId, name, defaultEnabled) {
|
||||
constructor(navId, elemId, name, defaultEnabled, isHtml) {
|
||||
// navId is used in messaging
|
||||
this.navId = navId;
|
||||
this.elemId = undefined;
|
||||
@@ -21,6 +21,7 @@ class WeatherDisplay {
|
||||
this.loadingStatus = STATUS.loading;
|
||||
this.name = name ?? elemId;
|
||||
this.getDataCallbacks = [];
|
||||
this.isHtml = isHtml;
|
||||
|
||||
// default navigation timing
|
||||
this.timing = {
|
||||
@@ -41,6 +42,9 @@ class WeatherDisplay {
|
||||
this.setStatus(STATUS.disabled);
|
||||
}
|
||||
this.startNavCount();
|
||||
|
||||
// get any templates
|
||||
this.loadTemplates();
|
||||
}
|
||||
|
||||
addCheckbox(defaultEnabled = true) {
|
||||
@@ -97,6 +101,9 @@ class WeatherDisplay {
|
||||
if (this.elemId) return;
|
||||
this.elemId = elemId;
|
||||
|
||||
// no additional work if this is HTML
|
||||
if (this.isHtml) return;
|
||||
|
||||
// create a canvas
|
||||
const canvas = document.createElement('template');
|
||||
canvas.innerHTML = `<canvas id='${`${elemId}Canvas`}' width='${width}' height='${height}' style='display: none;' />`;
|
||||
@@ -136,13 +143,15 @@ class WeatherDisplay {
|
||||
}
|
||||
|
||||
drawCanvas() {
|
||||
if (!this.isHtml) {
|
||||
// stop all gifs
|
||||
this.gifs.forEach((gif) => gif.pause());
|
||||
// delete the gifs
|
||||
this.gifs.length = 0;
|
||||
// refresh the canvas
|
||||
this.canvas = document.getElementById(`${this.elemId}Canvas`);
|
||||
this.context = this.canvas.getContext('2d');
|
||||
this.gifs.forEach((gif) => gif.pause());
|
||||
// delete the gifs
|
||||
this.gifs.length = 0;
|
||||
// refresh the canvas
|
||||
this.canvas = document.getElementById(`${this.elemId}Canvas`);
|
||||
this.context = this.canvas.getContext('2d');
|
||||
}
|
||||
|
||||
// clean up the first-run flag in screen index
|
||||
if (this.screenIndex < 0) this.screenIndex = 0;
|
||||
@@ -197,64 +206,31 @@ class WeatherDisplay {
|
||||
// if (OkToDrawCustomScrollText) DrawCustomScrollText(WeatherParameters, context);
|
||||
}
|
||||
|
||||
drawCurrentDateTime(bottom) {
|
||||
drawCurrentDateTime() {
|
||||
// only draw if canvas is active to conserve battery
|
||||
if (!this.isActive()) return;
|
||||
const { DateTime } = luxon;
|
||||
const font = 'Star4000 Small';
|
||||
const size = '24pt';
|
||||
const color = '#ffffff';
|
||||
const shadow = 2;
|
||||
|
||||
// on the first pass store the background for the date and time
|
||||
if (!this.dateTimeBackground) {
|
||||
const bg = this.context.getImageData(410, 30, 175, 60);
|
||||
// test background draw complete and skip drawing if there is no background yet
|
||||
if (bg.data[0] === 0) return;
|
||||
// store the background
|
||||
this.dateTimeBackground = bg;
|
||||
}
|
||||
|
||||
// Clear the date and time area.
|
||||
if (bottom) {
|
||||
draw.box(this.context, 'rgb(25, 50, 112)', 0, 389, 640, 16);
|
||||
} else {
|
||||
this.context.putImageData(this.dateTimeBackground, 410, 30);
|
||||
}
|
||||
|
||||
// Get the current date and time.
|
||||
const now = DateTime.local();
|
||||
|
||||
// time = "11:35:08 PM";
|
||||
const time = now.toLocaleString(DateTime.TIME_WITH_SECONDS).padStart(11, ' ');
|
||||
|
||||
let x; let y;
|
||||
if (bottom) {
|
||||
x = 400;
|
||||
y = 402;
|
||||
} else {
|
||||
x = 410;
|
||||
y = 65;
|
||||
if (this.lastTime !== time) {
|
||||
utils.elem.forEach('.date-time.time', (elem) => { elem.innerHTML = time.toUpperCase(); });
|
||||
}
|
||||
if (navigation.units() === UNITS.metric) {
|
||||
x += 45;
|
||||
}
|
||||
|
||||
draw.text(this.context, font, size, color, x, y, time.toUpperCase(), shadow); // y += 20;
|
||||
this.lastTime = time;
|
||||
|
||||
const date = now.toFormat(' ccc LLL ') + now.day.toString().padStart(2, ' ');
|
||||
|
||||
if (bottom) {
|
||||
x = 55;
|
||||
y = 402;
|
||||
} else {
|
||||
x = 410;
|
||||
y = 85;
|
||||
if (this.lastDate !== date) {
|
||||
utils.elem.forEach('.date-time.date', (elem) => { elem.innerHTML = date.toUpperCase(); });
|
||||
}
|
||||
draw.text(this.context, font, size, color, x, y, date.toUpperCase(), shadow);
|
||||
this.lastDate = date;
|
||||
}
|
||||
|
||||
async drawNoaaImage() {
|
||||
if (this.isHtml) return;
|
||||
// load the image and store locally
|
||||
if (!this.drawNoaaImage.image) {
|
||||
this.drawNoaaImage.image = utils.image.load('images/noaa5.gif');
|
||||
@@ -265,6 +241,7 @@ class WeatherDisplay {
|
||||
}
|
||||
|
||||
async drawLogoImage() {
|
||||
if (this.isHtml) return;
|
||||
// load the image and store locally
|
||||
if (!this.drawLogoImage.image) {
|
||||
this.drawLogoImage.image = utils.image.load('images/Logo3.png');
|
||||
@@ -281,23 +258,33 @@ class WeatherDisplay {
|
||||
if (navCmd === navigation.msg.command.firstFrame) this.navNext(navCmd);
|
||||
if (navCmd === navigation.msg.command.lastFrame) this.navPrev(navCmd);
|
||||
|
||||
// see if the canvas is already showing
|
||||
if (this.canvas.style.display === 'block') return false;
|
||||
this.startNavCount();
|
||||
|
||||
// show the canvas
|
||||
this.canvas.style.display = 'block';
|
||||
return false;
|
||||
if (!this.isHtml) {
|
||||
// see if the canvas is already showing
|
||||
if (this.canvas.style.display === 'block') return;
|
||||
|
||||
// show the canvas
|
||||
this.canvas.style.display = 'block';
|
||||
} else {
|
||||
this.elem.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
hideCanvas() {
|
||||
this.resetNavBaseCount();
|
||||
|
||||
if (!this.canvas) return;
|
||||
this.canvas.style.display = 'none';
|
||||
if (this.canvas) {
|
||||
this.canvas.style.display = 'none';
|
||||
}
|
||||
if (this.isHtml) {
|
||||
this.elem.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return document.getElementById(`${this.elemId}Canvas`).offsetParent !== null;
|
||||
if (!this.isHtml) return document.getElementById(`${this.elemId}Canvas`).offsetParent !== null;
|
||||
return this.elem.offsetParent !== null;
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
@@ -452,7 +439,6 @@ class WeatherDisplay {
|
||||
clearInterval(this.navInterval);
|
||||
this.navInterval = undefined;
|
||||
}
|
||||
this.startNavCount();
|
||||
}
|
||||
|
||||
sendNavDisplayMessage(message) {
|
||||
@@ -461,4 +447,20 @@ class WeatherDisplay {
|
||||
type: message,
|
||||
});
|
||||
}
|
||||
|
||||
loadTemplates() {
|
||||
this.templates = {};
|
||||
if (this.elemId !== 'progress') {
|
||||
this.elem = document.getElementById(`${this.elemId}-html`);
|
||||
if (!this.elem) return;
|
||||
const templates = this.elem.querySelectorAll('.template');
|
||||
templates.forEach((template) => {
|
||||
const className = template.classList[0];
|
||||
const node = template.cloneNode(true);
|
||||
node.classList.remove('template');
|
||||
this.templates[className] = node;
|
||||
template.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user