Mondphase eingebaut
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
+166
-10
@@ -22,6 +22,14 @@ const ASTRONOMY_LIB_URL = (() => {
|
||||
}
|
||||
})();
|
||||
|
||||
const MOON_IMAGE_URL = (() => {
|
||||
try {
|
||||
return new URL("./moon_small.png", import.meta.url).toString();
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
class SternwarteCard extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -31,10 +39,13 @@ class SternwarteCard extends HTMLElement {
|
||||
};
|
||||
this._pendingRender = false;
|
||||
this._astronomyLoadPromise = null;
|
||||
this._moonTexture = null;
|
||||
this._moonTexturePromise = null;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._ensureAstronomy();
|
||||
this._ensureMoonTexture();
|
||||
this._ensureClockTimer();
|
||||
}
|
||||
|
||||
@@ -121,6 +132,29 @@ class SternwarteCard extends HTMLElement {
|
||||
return this._astronomyLoadPromise;
|
||||
}
|
||||
|
||||
_ensureMoonTexture() {
|
||||
if (this._moonTexture) return Promise.resolve(this._moonTexture);
|
||||
if (this._moonTexturePromise) return this._moonTexturePromise;
|
||||
if (!MOON_IMAGE_URL) return Promise.resolve(null);
|
||||
|
||||
this._moonTexturePromise = new Promise((resolve) => {
|
||||
const image = new Image();
|
||||
image.addEventListener("load", () => {
|
||||
this._moonTexture = image;
|
||||
this._moonTexturePromise = null;
|
||||
if (this._hass) this._requestRender();
|
||||
resolve(image);
|
||||
}, { once: true });
|
||||
image.addEventListener("error", () => {
|
||||
this._moonTexturePromise = null;
|
||||
resolve(null);
|
||||
}, { once: true });
|
||||
image.src = MOON_IMAGE_URL;
|
||||
});
|
||||
|
||||
return this._moonTexturePromise;
|
||||
}
|
||||
|
||||
_requestRender() {
|
||||
if (this._isScheduleEditing()) {
|
||||
this._pendingRender = true;
|
||||
@@ -500,7 +534,7 @@ class SternwarteCard extends HTMLElement {
|
||||
const astronomy = this._astronomy();
|
||||
const observer = this._astroObserver();
|
||||
if (!astronomy || !observer) {
|
||||
return { altitude: Number.NEGATIVE_INFINITY, illumination: 0 };
|
||||
return { altitude: Number.NEGATIVE_INFINITY, illumination: 0, phaseAngle: 0 };
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -508,16 +542,108 @@ class SternwarteCard extends HTMLElement {
|
||||
const equator = astronomy.Equator(bodyMoon, date, observer, true, true);
|
||||
const horizon = astronomy.Horizon(date, observer, equator.ra, equator.dec, "normal");
|
||||
const illumination = astronomy.Illumination(bodyMoon, date);
|
||||
const phaseAngle = Number(astronomy.MoonPhase(date)) || 0;
|
||||
|
||||
return {
|
||||
altitude: (Number(horizon?.altitude) || 0) * Math.PI / 180,
|
||||
illumination: Math.max(0, Math.min(1, Number(illumination?.phase_fraction) || 0)),
|
||||
phaseAngle: ((phaseAngle % 360) + 360) % 360,
|
||||
};
|
||||
} catch (error) {
|
||||
return { altitude: Number.NEGATIVE_INFINITY, illumination: 0 };
|
||||
return { altitude: Number.NEGATIVE_INFINITY, illumination: 0, phaseAngle: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
_moonVisualState() {
|
||||
const moon = this._moonStateAt(new Date());
|
||||
const phaseAngle = moon.phaseAngle ?? 0;
|
||||
const waxing = phaseAngle <= 180;
|
||||
const radians = phaseAngle * Math.PI / 180;
|
||||
|
||||
return {
|
||||
illumination: Math.max(0, Math.min(1, moon.illumination || 0)),
|
||||
illuminationPercent: Math.round((moon.illumination || 0) * 100),
|
||||
waxing,
|
||||
phaseAngle,
|
||||
cosPhase: Math.cos(radians),
|
||||
};
|
||||
}
|
||||
|
||||
_renderMoonPhaseDataUrl(moonVisual, size = 64) {
|
||||
const texture = this._moonTexture;
|
||||
if (!texture) return "";
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return "";
|
||||
|
||||
const radius = size / 2 - 1;
|
||||
const x = size / 2;
|
||||
const y = size / 2;
|
||||
const cosPhase = Number(moonVisual.cosPhase) || 0;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.clip();
|
||||
|
||||
ctx.fillStyle = "rgba(8,10,16,0.96)";
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.globalAlpha = 0.98;
|
||||
ctx.drawImage(texture, x - radius, y - radius, radius * 2, radius * 2);
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
ctx.fillStyle = "rgba(8,10,16,0.96)";
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.clip();
|
||||
|
||||
if (moonVisual.illumination >= 0.999) {
|
||||
ctx.drawImage(texture, x - radius, y - radius, radius * 2, radius * 2);
|
||||
} else if (moonVisual.illumination > 0.001) {
|
||||
ctx.beginPath();
|
||||
for (let i = 0; i <= 72; i += 1) {
|
||||
const t = -radius + (2 * radius * i) / 72;
|
||||
const xr = Math.sqrt(Math.max(0, radius * radius - t * t));
|
||||
const xt = (moonVisual.waxing ? cosPhase : -cosPhase) * xr;
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x + xt, y + t);
|
||||
} else {
|
||||
ctx.lineTo(x + xt, y + t);
|
||||
}
|
||||
}
|
||||
for (let i = 72; i >= 0; i -= 1) {
|
||||
const t = -radius + (2 * radius * i) / 72;
|
||||
const xr = Math.sqrt(Math.max(0, radius * radius - t * t));
|
||||
ctx.lineTo(x + (moonVisual.waxing ? xr : -xr), y + t);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
ctx.drawImage(texture, x - radius, y - radius, radius * 2, radius * 2);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
ctx.restore();
|
||||
|
||||
ctx.strokeStyle = "rgba(255,255,255,0.18)";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
|
||||
_moonTrackState() {
|
||||
const now = new Date();
|
||||
const cycleStart = new Date(now);
|
||||
@@ -709,6 +835,8 @@ class SternwarteCard extends HTMLElement {
|
||||
}
|
||||
const astro = this._astroState();
|
||||
const moonTrack = this._moonTrackState();
|
||||
const moonVisual = this._moonVisualState();
|
||||
const moonImageDataUrl = this._renderMoonPhaseDataUrl(moonVisual, 56);
|
||||
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
@@ -790,10 +918,6 @@ class SternwarteCard extends HTMLElement {
|
||||
outline: 2px solid var(--primary-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.stat-spacer {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.stat.wind-good {
|
||||
background: rgba(76, 175, 80, 0.14);
|
||||
border-color: rgba(76, 175, 80, 0.3);
|
||||
@@ -817,6 +941,28 @@ class SternwarteCard extends HTMLElement {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.moon-stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.moon-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
flex: 0 0 28px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 0 10px rgba(246, 240, 196, 0.12);
|
||||
}
|
||||
.moon-percent {
|
||||
text-align: right;
|
||||
line-height: 1.05;
|
||||
}
|
||||
.moon-percent .stat-value {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.position {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -1096,9 +1242,6 @@ class SternwarteCard extends HTMLElement {
|
||||
.stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.stat-spacer {
|
||||
display: none;
|
||||
}
|
||||
.schedule-overview {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -1129,7 +1272,20 @@ class SternwarteCard extends HTMLElement {
|
||||
<span class="stat-label">Wind</span>
|
||||
<span class="stat-value">${windIndicator.display}</span>
|
||||
</div>
|
||||
<div class="stat stat-spacer" aria-hidden="true"></div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Mond</span>
|
||||
<div class="moon-stat">
|
||||
<img
|
||||
class="moon-icon"
|
||||
src="${moonImageDataUrl}"
|
||||
alt="Mondphase"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="moon-percent">
|
||||
<span class="stat-value">${moonVisual.illuminationPercent} %</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="badges">
|
||||
${this._boolBadge("Dach zu", this.config.roofClosed, "Ja", "Nein")}
|
||||
|
||||
Reference in New Issue
Block a user