mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-20 18:49:31 -07:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d75121e894 | ||
|
|
4cdced3659 | ||
|
|
1a5548d135 | ||
|
|
11c826a2af | ||
|
|
7a129c1cd3 | ||
|
|
867657a965 | ||
|
|
e89dc52541 | ||
|
|
317883fc04 |
@@ -109,7 +109,7 @@ The resulting files will be in the /dist folder in the root of the project. Thes
|
||||
## Music
|
||||
The WeatherStar had wonderful background music from the smooth jazz and new age genres by artists of the time. Lists of the music that played are available by searching online, but it's all copyrighted music and would be difficult to provide as part of this repository.
|
||||
|
||||
I've used AI tools to create WeatherStar-inspired music tracks that are unencumbered by copyright and are included in this repo. Too keep the size down, I've only included 4 tracks. Additional tracks will be posted in a companion repository [ws4kp-music](https://github.com/netbymatt/ws4kp-music).
|
||||
I've used AI tools to create WeatherStar-inspired music tracks that are unencumbered by copyright and are included in this repo. Too keep the size down, I've only included 4 tracks. Additional tracks are in a companion repository [ws4kp-music](https://github.com/netbymatt/ws4kp-music).
|
||||
|
||||
### Customizing the music
|
||||
Placing .mp3 files in the `/server/music` folder will override the default music included in the repo. Subdirectories will not be scanned. When weatherstar loads in the browser it will load a list if available files and randomize the order when it starts playing. On each loop through the available tracks the order will again be shuffled. If you're using the static files method to host your WeatherStar music is located in `/music`.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ws4kp",
|
||||
"version": "5.19.1",
|
||||
"version": "5.20.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ws4kp",
|
||||
"version": "5.19.1",
|
||||
"version": "5.20.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.5.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ws4kp",
|
||||
"version": "5.19.1",
|
||||
"version": "5.20.0",
|
||||
"description": "Welcome to the WeatherStar 4000+ project page!",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -109,9 +109,6 @@ const init = () => {
|
||||
document.querySelector('#spanRadarId').innerHTML = '';
|
||||
document.querySelector('#spanZoneId').innerHTML = '';
|
||||
|
||||
document.querySelector('#chkAutoRefresh').checked = true;
|
||||
localStorage.removeItem('autoRefresh');
|
||||
|
||||
localStorage.removeItem('play');
|
||||
postMessage('navButton', 'play');
|
||||
|
||||
|
||||
@@ -5,10 +5,14 @@ import { currentDisplay } from './navigation.mjs';
|
||||
|
||||
// constants
|
||||
const degree = String.fromCharCode(176);
|
||||
const SCROLL_SPEED = 75; // pixels/second
|
||||
const DEFAULT_UPDATE = 8; // 0.5s ticks
|
||||
|
||||
// local variables
|
||||
let interval;
|
||||
let screenIndex = 0;
|
||||
let sinceLastUpdate = 0;
|
||||
let nextUpdate = DEFAULT_UPDATE;
|
||||
|
||||
// start drawing conditions
|
||||
// reset starts from the first item in the text scroll list
|
||||
@@ -17,7 +21,7 @@ const start = () => {
|
||||
|
||||
// set up the interval if needed
|
||||
if (!interval) {
|
||||
interval = setInterval(incrementInterval, 4000);
|
||||
interval = setInterval(incrementInterval, 500);
|
||||
}
|
||||
|
||||
// draw the data
|
||||
@@ -29,14 +33,24 @@ const stop = (reset) => {
|
||||
};
|
||||
|
||||
// increment interval, roll over
|
||||
const incrementInterval = () => {
|
||||
// forcing is used when drawScreen receives an invalid screen and needs to request the next one in line
|
||||
const incrementInterval = (force) => {
|
||||
if (!force) {
|
||||
// test for elapsed time (0.5s ticks);
|
||||
sinceLastUpdate += 1;
|
||||
if (sinceLastUpdate < nextUpdate) return;
|
||||
}
|
||||
// reset flags
|
||||
sinceLastUpdate = 0;
|
||||
nextUpdate = DEFAULT_UPDATE;
|
||||
|
||||
// test current screen
|
||||
const display = currentDisplay();
|
||||
if (!display?.okToDrawCurrentConditions) {
|
||||
stop(display?.elemId === 'progress');
|
||||
return;
|
||||
}
|
||||
screenIndex = (screenIndex + 1) % (screens.length);
|
||||
screenIndex = (screenIndex + 1) % (lastScreen);
|
||||
// draw new text
|
||||
drawScreen();
|
||||
};
|
||||
@@ -48,7 +62,22 @@ const drawScreen = async () => {
|
||||
// nothing to do if there's no data yet
|
||||
if (!data) return;
|
||||
|
||||
drawCondition(screens[screenIndex](data));
|
||||
const thisScreen = screens[screenIndex](data);
|
||||
if (typeof thisScreen === 'string') {
|
||||
// only a string
|
||||
drawCondition(thisScreen);
|
||||
} else if (typeof thisScreen === 'object') {
|
||||
// an object was provided with additional parameters
|
||||
switch (thisScreen.type) {
|
||||
case 'scroll':
|
||||
drawScrollCondition(thisScreen);
|
||||
break;
|
||||
default: drawCondition(thisScreen);
|
||||
}
|
||||
} else {
|
||||
// can't identify screen, get another one
|
||||
incrementInterval(true);
|
||||
}
|
||||
};
|
||||
|
||||
// the "screens" are stored in an array for easy addition and removal
|
||||
@@ -71,7 +100,7 @@ const screens = [
|
||||
(data) => `Humidity: ${data.Humidity}% Dewpoint: ${data.DewPoint}${degree}${data.TemperatureUnit}`,
|
||||
|
||||
// barometric pressure
|
||||
(data) => `Barometric Pressure: ${data.Pressure} ${data.PressureUnit} ${data.PressureDirection}`,
|
||||
(data) => `Barometric Pressure: ${data.Pressure} ${data.PressureDirection}`,
|
||||
|
||||
// wind
|
||||
(data) => {
|
||||
@@ -102,3 +131,56 @@ const drawCondition = (text) => {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
start();
|
||||
});
|
||||
|
||||
// store the original number of screens
|
||||
const originalScreens = screens.length;
|
||||
let lastScreen = originalScreens;
|
||||
|
||||
// reset the number of screens
|
||||
const reset = () => {
|
||||
lastScreen = originalScreens;
|
||||
};
|
||||
|
||||
// add screen
|
||||
const addScreen = (screen) => {
|
||||
screens.push(screen);
|
||||
lastScreen += 1;
|
||||
};
|
||||
|
||||
const drawScrollCondition = (screen) => {
|
||||
// create the scroll element
|
||||
const scrollElement = document.createElement('div');
|
||||
scrollElement.classList.add('scroll-area');
|
||||
scrollElement.innerHTML = screen.text;
|
||||
// add it to the page to get the width
|
||||
document.querySelector('.weather-display .scroll .fixed').innerHTML = scrollElement.outerHTML;
|
||||
// grab the width
|
||||
const { scrollWidth, clientWidth } = document.querySelector('.weather-display .scroll .fixed .scroll-area');
|
||||
|
||||
// calculate the scroll distance and set a minimum scroll
|
||||
const scrollDistance = Math.max(scrollWidth - clientWidth, 0);
|
||||
// calculate the scroll time
|
||||
const scrollTime = scrollDistance / SCROLL_SPEED;
|
||||
// calculate a new minimum on-screen time +1.0s at start and end
|
||||
nextUpdate = Math.round(Math.ceil(scrollTime / 0.5) + 4);
|
||||
|
||||
// update the element transition and set initial left position
|
||||
scrollElement.style.left = '0px';
|
||||
scrollElement.style.transition = `left linear ${scrollTime.toFixed(1)}s`;
|
||||
elemForEach('.weather-display .scroll .fixed', (elem) => {
|
||||
elem.innerHTML = '';
|
||||
elem.append(scrollElement.cloneNode(true));
|
||||
});
|
||||
// start the scroll after a short delay
|
||||
setTimeout(() => {
|
||||
// change the left position to trigger the scroll
|
||||
elemForEach('.weather-display .scroll .fixed .scroll-area', (elem) => {
|
||||
elem.style.left = `-${scrollDistance.toFixed(0)}px`;
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
window.CurrentWeatherScroll = {
|
||||
addScreen,
|
||||
reset,
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ class LocalForecast extends WeatherDisplay {
|
||||
forecastsElem.append(...templates);
|
||||
|
||||
// increase each forecast height to a multiple of container height
|
||||
this.pageHeight = forecastsElem.parentNode.scrollHeight;
|
||||
this.pageHeight = forecastsElem.parentNode.offsetHeight;
|
||||
templates.forEach((forecast) => {
|
||||
const newHeight = Math.ceil(forecast.scrollHeight / this.pageHeight) * this.pageHeight;
|
||||
forecast.style.height = `${newHeight}px`;
|
||||
|
||||
@@ -54,23 +54,25 @@ class SpcOutlook extends WeatherDisplay {
|
||||
async getData(weatherParameters, refresh) {
|
||||
if (!super.getData(weatherParameters, refresh)) return;
|
||||
|
||||
let initialData;
|
||||
try {
|
||||
// get the three categorical files to get started
|
||||
const filePromises = await Promise.allSettled(this.files.map((file) => json(file)));
|
||||
// store the data, promise will always be fulfilled
|
||||
initialData = filePromises.map((outlookDay) => outlookDay.value);
|
||||
} catch (error) {
|
||||
console.error('Unable to get spc outlook');
|
||||
console.error(error.status, error.responseJSON);
|
||||
// if there's no previous data, fail
|
||||
if (!this.data) {
|
||||
this.setStatus(STATUS.failed);
|
||||
return;
|
||||
// initial data does not need to be reloaded on a location change, only during silent refresh
|
||||
if (!this.initialData || refresh) {
|
||||
try {
|
||||
// get the three categorical files to get started
|
||||
const filePromises = await Promise.allSettled(this.files.map((file) => json(file)));
|
||||
// store the data, promise will always be fulfilled
|
||||
this.initialData = filePromises.map((outlookDay) => outlookDay.value);
|
||||
} catch (error) {
|
||||
console.error('Unable to get spc outlook');
|
||||
console.error(error.status, error.responseJSON);
|
||||
// if there's no previous data, fail
|
||||
if (!this.initialData) {
|
||||
this.setStatus(STATUS.failed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// do the initial parsing of the data
|
||||
this.data = testAllPoints([weatherParameters.longitude, weatherParameters.latitude], initialData);
|
||||
this.data = testAllPoints([weatherParameters.longitude, weatherParameters.latitude], this.initialData);
|
||||
|
||||
// if all the data returns false the there's nothing to do, skip this screen
|
||||
if (this.data.reduce((prev, cur) => prev || !!cur, false)) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -113,7 +113,7 @@
|
||||
|
||||
.scroll {
|
||||
@include u.text-shadow(3px, 1.5px);
|
||||
width: 640px;
|
||||
width: calc(640px - 2 * 30px);
|
||||
height: 70px;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
@@ -122,6 +122,15 @@
|
||||
font-family: 'Star4000';
|
||||
font-size: 24pt;
|
||||
margin-left: 55px;
|
||||
overflow: hidden;
|
||||
|
||||
.scroll-area {
|
||||
text-wrap: nowrap;
|
||||
position: relative;
|
||||
// the following added by js code as it is dependent on the content of the element
|
||||
// transition: left (x)s;
|
||||
// left: calc((elem width) - 640px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,7 @@
|
||||
Station Id: <span id="spanStationId"></span><br />
|
||||
Radar Id: <span id="spanRadarId"></span><br />
|
||||
Zone Id: <span id="spanZoneId"></span><br />
|
||||
Ws4kp Version: <span><%- version %></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user