68 lines
2.7 KiB
JavaScript
68 lines
2.7 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
const root = window.PixelIslandModules ||= {};
|
|
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
const lerp = (a, b, t) => a + (b - a) * t;
|
|
const mod = (n, m) => ((n % m) + m) % m;
|
|
|
|
function getCelestialLightForMinute(minute) {
|
|
const isNight = minute >= 6;
|
|
const local = isNight ? (minute - 6) / 4 : minute / 6;
|
|
const t = clamp(local, 0, 1);
|
|
const eased = t * t * (3 - 2 * t);
|
|
const elevation = Math.max(.08, Math.sin(t * Math.PI));
|
|
const sourceX = isNight ? lerp(-1.05, 1.05, eased) : lerp(1.18, -1.18, eased);
|
|
const sourceY = isNight ? -.46 - elevation * .24 : -.58 - elevation * .34;
|
|
return {
|
|
x: sourceX,
|
|
y: sourceY,
|
|
elevation,
|
|
isNight,
|
|
shadeAlpha: isNight ? .18 : lerp(.20, .08, elevation)
|
|
};
|
|
}
|
|
|
|
function getShadowForMinute(minute) {
|
|
const light = getCelestialLightForMinute(minute);
|
|
const length = lerp(light.isNight ? 1.35 : 1.9, light.isNight ? .85 : .52, light.elevation);
|
|
return {
|
|
x: -light.x * 6.5 * length,
|
|
y: Math.min(-1.8, light.y * 4.2 * length),
|
|
length,
|
|
alpha: light.isNight ? .085 : lerp(.27, .12, light.elevation)
|
|
};
|
|
}
|
|
|
|
function getPhase(dayMs, now = Date.now()) {
|
|
const t = mod(now, dayMs);
|
|
const minute = t / 60000;
|
|
const stops = [
|
|
{ at: 0, key: 'morning', label: 'Morning', darkness: 0.18, tint: [255, 208, 144, 0.12] },
|
|
{ at: 1, key: 'day', label: 'Day', darkness: 0.00, tint: [255, 255, 255, 0.00] },
|
|
{ at: 5, key: 'day', label: 'Day', darkness: 0.00, tint: [255, 255, 255, 0.00] },
|
|
{ at: 6, key: 'evening', label: 'Evening', darkness: 0.18, tint: [255, 146, 114, 0.14] },
|
|
{ at: 10, key: 'night', label: 'Night', darkness: 0.48, tint: [36, 47, 96, 0.18] }
|
|
];
|
|
let a = stops[0], b = stops[1];
|
|
for (let i = 0; i < stops.length - 1; i++) {
|
|
if (minute >= stops[i].at && minute < stops[i + 1].at) { a = stops[i]; b = stops[i + 1]; break; }
|
|
if (minute >= 6) { a = stops[3]; b = stops[4]; }
|
|
}
|
|
const localT = clamp((minute - a.at) / Math.max(0.0001, b.at - a.at), 0, 1);
|
|
const eased = localT * localT * (3 - 2 * localT);
|
|
const tint = a.tint.map((v, i) => lerp(v, b.tint[i], eased));
|
|
const label = minute < 1 ? 'Morning' : minute < 5 ? 'Day' : minute < 6 ? 'Evening' : 'Night';
|
|
return {
|
|
key: label.toLowerCase(),
|
|
label,
|
|
progress: t / dayMs,
|
|
darkness: lerp(a.darkness, b.darkness, eased),
|
|
tint: `rgba(${Math.round(tint[0])}, ${Math.round(tint[1])}, ${Math.round(tint[2])}, ${tint[3].toFixed(3)})`,
|
|
light: getCelestialLightForMinute(minute),
|
|
shadow: getShadowForMinute(minute)
|
|
};
|
|
}
|
|
|
|
root.Lighting = { getPhase, getShadowForMinute, getCelestialLightForMinute };
|
|
})();
|