-# Klimawidget Update+# Carbon Emissions
min_emission = d3.min(carbon_emissions_total.map((d) => d.total))
anomaly_reduced = Plot.plot({ width: chart_width, height: height, marginTop: 70, marginLeft: 0, marginRight: 0, marginBottom: 40, y: { axis: null }, x: { axis: null }, marks: [ // Balken Plot.barY(global_monthly_filtered, { y: "Anomalie", x: "Jahr", strokeWidth: 0, fill: (d) => (d.Jahr == 2025) | (d.Jahr == start) ? "#e84f1c" : grey_color, fillOpacity: (d) => (d.Jahr == start ? 0.2 : 1) }), // Monat Plot.text(anomaly_text, { x: (d) => d.year, y: "max_anomaly", text: (d) => d.month + " " + d.year, textAnchor: "end", fill: "#e84f1c", fontSize: 12, fontFamily: "InterRegular", dy: -60, dx: 0 }), // große Zahl Plot.text(anomaly_text, { x: "year", y: "max_anomaly", text: (d) => "+" + d.text.toLocaleString("de", { minimumFractionDigits: "2", maximumFractionDigits: "2" }) + "°C", textAnchor: "end", fill: "#e84f1c", fontSize: 22, fontWeight: 600, fontFamily: "DIN Next LT Pro", dy: -40, dx: 0 }), // Definition Plot.text(anomaly_text, { x: "year", y: "max_anomaly", text: (d) => `globale Durchschnittstemperatur\nAbweichung von 1991-2000`, textAnchor: "end", fontFamily: "InterRegular", dy: -15, dx: 0 }), // kleine Zahl Plot.text(anomaly_text_2, { x: "year", y: "min_anomaly", text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "1", maximumFractionDigits: "1" }) + "°C im " + d.month + " " + d.year, textAnchor: "start", fill: "#e84f1c", fillOpacity: 0.8, fontSize: 12, fontWeight: 600, fontFamily: "InterRegular", dy: 10, dx: 0 }), // Quelle Plot.text(anomaly_text, { x: "year", y: "min_anomaly", text: (d) => `Quelle: Copernicus Climate Change Service`, textAnchor: "end", fill: "#99AFC2", fontFamily: "InterRegular", dy: 30, dx: 0 }) ] })
anomaly_text = [ { year: 2025, max_anomaly: 0.73, min_anomaly: -0.17, text: 0.73, month: "März" } ]
anomaly_text_2 = [ { year: 2001, max_anomaly: 0.69, min_anomaly: -0.18, text: -0.06, month: "August" } ]
global_monthly_filtered_data = FileAttachment( "klimawidget_anomaly_march_2025.csv" ).csv()
global_monthly_filtered = global_monthly_filtered_data.map((d) => ({ Jahr: +d.Jahr, Anomalie: +d.Anomalie }))
months = [ "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" ]
forest_reduced = Plot.plot({ width: chart_width, height: height, marginTop: 70, marginLeft: 10, marginRight: 10, marginBottom: 40, x: { axis: null }, y: { axis: null }, marks: [ // Fläche Plot.areaY(forest_loss_data, { x: "year", y: "loss", curve: "monotone-x", fill: grey_color }), // Linie Plot.lineY(forest_loss_data, { x: "year", y: "loss", curve: "monotone-x", stroke: "#6bb024" }), // Punkte Plot.dot(forest_loss_data, { x: "year", y: "loss", stroke: "#6bb024", strokeOpacity: (d) => ((d.year == 2023) | (d.year == 2001) ? 1 : 0), fillOpacity: (d) => ((d.year == 2023) | (d.year == 2001) ? 1 : 0), fill: "#ffffff" }), // Jahr Plot.text(forest_text, { x: (d) => d.year, y: "max_forest", text: (d) => d.year.toString(), textAnchor: "end", fill: "#6bb024", fontSize: 12, fontFamily: "InterRegular", dy: -60, dx: 0 }), // große Zahl Plot.text(forest_text, { x: "year", y: "max_forest", text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "0", maximumFractionDigits: "0" }) + " km²", textAnchor: "end", fill: "#6bb024", fontSize: 22, fontWeight: 600, fontFamily: "DIN Next LT Pro", dy: -40, dx: 0 }), // Definition Plot.text(forest_text, { x: "year", y: "max_forest", text: (d) => `Waldverluste weltweit\n(ohne Waldgewinne)`, textAnchor: "end", fontFamily: "InterRegular", dy: -15, dx: 0 }), // kleine Zahl Plot.text(forest_text_2, { x: "year", y: 0, text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "0", maximumFractionDigits: "0" }) + " km² im Jahr " + d.year, textAnchor: "start", fill: "#6bb024", fillOpacity: 0.8, fontSize: 12, fontWeight: 600, fontFamily: "InterRegular", dy: 10, dx: 0 }), // Quelle Plot.text(forest_text, { x: "year", y: "min_forest", text: (d) => `Quelle: Global Forest Watch`, textAnchor: "end", fontFamily: "InterRegular", fill: "#99AFC2", dy: 30, dx: 0 }) ] })
forest_loss_data = FileAttachment("wald_global_time_2023@2.csv") .csv() .then(function (data) { data.forEach(function (d) { d.loss = +d.loss; d.year = +d.year; }); return data; })
forest_text = [ { year: year_forest_last, max_forest: max_forest, min_forest: 0, text: recent_forest } ]
year_forest_last = d3.max(forest_loss_data.map((d) => d.year))
recent_forest = +forest_loss_data[0].loss
max_forest = d3.max(forest_loss_data.map((d) => d.loss))
min_forest = forest_loss_data[forest_loss_data.length - 1].loss
forest_text_2 = [ { year: year_forest_first, max_forest: max_forest, min_forest: min_forest, text: first_forest } ]
year_forest_first = d3.min(forest_loss_data.map((d) => d.year))
first_forest = +forest_loss_data[forest_loss_data.length - 1].loss
klimawidget_title = md`<div class="mode" style="margin-bottom: 30px;"><h2 class="mode">Klimawandel in Grafiken</h2></div>`
klimawidget_zwei = html` <div> ${klimawidget_title} <div class="box"> <div class="box"> <div class="box_one"> ${emission_widget} ${credits_emission} </div> <div class="box_two"> ${temp_widget} ${credits_temp} </div> </div>`
temp_widget = Plot.plot({ width: chart_width, height: 250, marginLeft: 0, marginRight: 0, y: { axis: "left", domain: [-0.3, 1.5], ticks: [] }, x: { ticks: [1900, 1950, 2000], tickFormat: d3.format(""), tickSize: 4 }, marks: [ // Balken Plot.barY(global_temp, { y: "degree", x: "jahr", stroke: "#ffffff", strokeWidth: 0.4, fill: (d) => (d.degree > 0 ? "#e84f1c" : "#4f80ff"), title: (d) => `${d.year}`, sort: { y: "y", reverse: true }, opacity: (d) => (d.jahr == 2022 ? 1 : 0.75) }), // weiße Linien Plot.gridY({ interval: 0.25, stroke: isDarkMode() ? "#293845" : "#ffffff", strokeOpacity: 1 }), Plot.text([0], { x: 1850, y: 1.5, text: (d) => `globale Durchschnittstemperatur 2022 im\nVerleich zur vorindustriellen Zeit in °C`, textAnchor: "start", lineAnchor: "top", dy: 0, dx: 0 }), // Beschriftung rechtsbündig Plot.text(text_temp_end, { x: "x", y: "y", text: "text", textAnchor: "end", dy: 0, dx: 0 }), // hervorstehende Zahl Plot.text(text_zahl, { x: "x", y: "y", text: "text", textAnchor: "start", fill: "#e84f1c", fontSize: 24, fontWeight: 600, dy: 45, dx: 0 }) ] })
credits_temp = md`<span style="font-family: InterRegular; font-size: 12px; color: #99AFC2;">Quelle: Met Office<span>`
// https://climate.metoffice.cloud/temperature.html#datasets global_temp = FileAttachment("global_temperature_berkeley@2.csv") .csv() .then(function (data) { data.forEach(function (d) { d.degree = +d.degree; d.jahr = +d.year; }); return data; })
text_zahl = [{ x: 1850, y: 1.5, text: "+1,17°" }]
text_temp_end = [ { x: 1937, y: 0.25, text: "+0,25°" }, { x: 1978, y: 0.5, text: "+0,5°" }, { x: 1995, y: 0.75, text: "+0,75°" }, { x: 2005, y: 1, text: "+1,0°" } ]
carbon_emissions_axis = FileAttachment( "carbon_emissions_observable_total@2.csv" ) .csv() .then(function (data) { data.forEach(function (d) { d.emissions = +d.emissions / 1000; d.year = +d.year; }); return data; })
-emission_widget = Plot.plot({ width: chart_width, height: 250, marginLeft: 0, marginRight: 0,+Plot.plot({ width: 400, height: 300, marginLeft: 30,x: { tickFormat: d3.format(""), label: null,-tickSize: 4, ticks: [1900, 1950, 2000]+ticks: 3}, y: {-axis: null, ticks: []+tickFormat: (d) => d.toLocaleString("de"), grid: false, axis: "right", label: "Milliarden Tonnen"}, color: { domain: group_order, range: color_scale },-marks: [-// Kurven einzeichnenPlot.areaY(carbon_emissions, { x: "year", y: "emissions", fill: "group", z: "group",-stroke: "#f2f2f2", strokeWidth: 0.5, strokeOpacity: 0.5,+stroke: "black", // Add strokeorder: group_order,-tip: false+tip: true}),-// Label für die Regionen Plot.text(text_group, {+// Use one text mark applied to a data array. Plot.text(text_data, {x: "x", y: "y", text: "text", fontSize: 10, textAnchor: "end", lineAnchor: "bottom", fill: "white"-}), // Beschriftung Plot.text([0], { x: 1850, y: 40, text: (d) => `Globale Treibhausgasemissionen 2022\nin Milliarden Tonnen CO₂-Äquivalenten`, textAnchor: "start", lineAnchor: "top", dy: 0, dx: 0 }), // Achsenticks einzeichnen Plot.axisY({ x: (y) => carbon_emissions_axis.find((d) => d.emissions >= y)?.year, insetLeft: -3, domain: [0, 40], ticks: [10, 20, 30, 40] }), // Legende Plot.text([0], { x: 1850, y: 18, text: (d) => `veränderte Landnutzung`, textAnchor: "start", lineAnchor: "top", fill: "#99AFC2", dy: -15, dx: 0 }), Plot.text([0], { x: 1850, y: 18, text: (d) => `spät industrialisierte Länder`, textAnchor: "start", lineAnchor: "top", fill: "#e84f1c", dy: 0, dx: 0 }), Plot.text([0], { x: 1850, y: 18, text: (d) => `früh industrialisierte Länder`, textAnchor: "start", lineAnchor: "top", fill: "#4f80ff", dy: 15, dx: 0 }), // hervorstehende Zahl Plot.text(text_emission, { x: "x", y: "y", text: "text", textAnchor: "start", fill: "#4f80ff", fontSize: 24, fontWeight: 600, dy: 45, dx: 0}) ] })
numbers = Array.from({ length: 800 }, (_, i) => i)
viewof timer = Player(numbers, { loop: true, alternate: false, show: { play: true } })
klimawidget = html` ${klimawidget_title} <div class="box"> <div class="box_one" style="display: ${first_box_one}"> ${emission_reduced} </div> <div class="box_one" style="display: ${first_box_two}"> ${forest_reduced} </div> <div class="box_two" style="display: ${second_box_two}"> ${ice_reduced} </div> <div class="box_two" style="display: ${second_box_one}"> ${anomaly_reduced} </div> </div> `
klima_button = html` <div style="height:40px;padding-top: 20px;"><a href="https://www.rnd.de/klima/" target="_blank" style="text-decoration: none;"><span class="klima">mehr zur Klimakrise</span></a></div> `
first_box_one = { if (((timer >= 0) & (timer < 200)) | ((timer >= 400) & (timer < 600))) { return "inline"; } else { return "none"; } }
first_box_two = { if (((timer >= 200) & (timer < 400)) | ((timer >= 600) & (timer <= 800))) { return "inline"; } else { return "none"; } }
second_box_one = { if ( ((timer >= 700) & (timer < 800)) | ((timer >= 0) & (timer < 100)) | ((timer >= 300) & (timer < 500)) ) { return "inline"; } else { return "none"; } }
second_box_two = { if (((timer >= 100) & (timer < 300)) | ((timer >= 500) & (timer < 700))) { return "inline"; } else { return "none"; } }
start = 2001
height = 180
emission_opacity = isDarkMode() ? 0.8 : 0.2
emission_color = isDarkMode() ? "#eff3f5" : "#293845"
grey_color = isDarkMode() ? "#435b70" : "#eff3f5"
ice_reduced = Plot.plot({ width: chart_width, height: height, marginTop: 70, marginLeft: 0, marginRight: 0, marginBottom: 40, x: { axis: null }, y: { axis: null }, marks: [ // Balken Plot.barY(data_ice_filtered, { x: "year", y: "extent", fill: (d) => (d.year == 2025) | (d.year == start) ? "#4f80ff" : grey_color, fillOpacity: (d) => (d.year == start ? 0.2 : 1), strokeWidth: 0 }), // Tag Plot.text(ice_text, { x: (d) => d.day.getFullYear(), y: "max_ice", text: (d) => d.day.toLocaleString("de", { day: "numeric", month: "numeric", year: "numeric" }), textAnchor: "end", fill: "#4f80ff", fontSize: 12, fontFamily: "InterRegular", dy: -60, dx: 0 }), // große Zahl Plot.text(ice_text, { x: (d) => d.day.getFullYear(), y: "max_ice", text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "1", maximumFractionDigits: "1" }) + " Mio. km²", textAnchor: "end", fill: "#4f80ff", fontSize: 22, fontWeight: 600, fontFamily: "DIN Next LT Pro", dy: -40, dx: 0 }), // Definition Plot.text(ice_text, { x: (d) => d.day.getFullYear(), y: "max_ice", text: (d) => `Ausdehnung des Meereseises\nder Antarktis`, textAnchor: "end", fontFamily: "InterRegular", dy: -15, dx: 0 }), // kleine Zahl Plot.text(ice_text_2, { x: (d) => d.day.getFullYear(), y: "min_ice", text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "1", maximumFractionDigits: "1" }) + " Mio. km² am " + d.day.toLocaleString("de", { day: "numeric", month: "numeric", year: "numeric" }), textAnchor: "start", fill: "#4f80ff", fillOpacity: 0.8, fontSize: 12, fontWeight: 600, fontFamily: "InterRegular", dy: 10, dx: 0 }), // Quelle Plot.text(ice_text, { x: (d) => d.day.getFullYear(), y: "min_ice", text: (d) => `Quelle: climatereanalyzer.org`, textAnchor: "end", fontFamily: "InterRegular", fill: "#99AFC2", dy: 30, dx: 0 }) ] })
ice_text = [ { day: day_ice_last, max_ice: max_ice, min_ice: 0, text: recent_ice }, ]
day_ice_last = d3.max(data_ice_filtered.map((d) => d.date))
max_ice = d3.max(data_ice_filtered.map((d) => d.extent))
recent_ice = +data_ice_filtered[0].extent
ice_text_2 = [ { day: day_ice_first, max_ice: max_ice, min_ice: 0, text: first_ice } ]
day_ice_first = d3.min(data_ice_filtered.map((d) => d.date))
first_ice = +data_ice_filtered[data_ice_filtered.length - 1].extent
// https://nsidc.org/data/seaice_index/data-and-image-archive url = "https://docs.google.com/spreadsheets/d/1Jss8jkdNOIQ4JptDUoV4z3pUVlLaENIa9CD2Yiqv8wg/edit?gid=1253017609#gid=1253017609"
rawdata_ice = d3.csv(getCsvUrl(url), d3.autoType)
data_ice = rawdata_ice .map((d) => ({ year: +d.Year, month: +d.Month, day: +d.Day, extent: +d.Extent, date: roundUTCDay(new Date(d.Year + "-" + d.Month + "-" + d.Day)), date_repeat: roundUTCDay(new Date("2023" + "-" + d.Month + "-" + d.Day)), day_of_year: Math.floor( (roundUTCDay(new Date(d.Year + "-" + d.Month + "-" + d.Day)) - new Date( roundUTCDay( new Date(d.Year + "-" + d.Month + "-" + d.Day) ).getFullYear(), 0, 0 )) / 86400000 ) - 1 })) .filter((d) => (d.year != "YYYY") & (d.year >= start) & (d.day <= 364))
max_month_ice = d3.max(data_ice.map((d) => d.date)).getMonth() + 1
max_day_ice = d3.max(data_ice.map((d) => d.date)).getUTCDate()
SELECT year, date, extent FROM data_ice WHERE month == ${max_month_ice} AND day == ${max_day_ice} ORDER BY year desc
emission_reduced = Plot.plot({ width: chart_width, height: height, marginTop: 70, marginLeft: 10, marginRight: 10, marginBottom: 40, x: { axis: null }, y: { axis: null }, marks: [ // Fläche Plot.areaY(carbon_emissions_total_concat, { x: "year", y: "total", curve: "monotone-x", fill: grey_color }), // Linie Plot.lineY(carbon_emissions_total_concat, { x: "year", y: "total", curve: "monotone-x", stroke: emission_color }), // Punkte Plot.dot(carbon_emissions_total_concat, { x: "year", y: "total", stroke: emission_color, strokeOpacity: (d) => ((d.year == 2023) | (d.year == start) ? 1 : 0), fillOpacity: (d) => ((d.year == 2023) | (d.year == start) ? 1 : 0), fill: "#ffffff" }), // Jahr Plot.text(emissions_text, { x: "year", y: "max_emission", text: (d) => d.year.toString(), textAnchor: "end", fill: emission_color, fontSize: 12, fontFamily: "InterRegular", dy: -60, dx: 0 }), // große Zahl Plot.text(emissions_text, { x: "year", y: "max_emission", text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "1", maximumFractionDigits: "1" }) + " Mrd. t", textAnchor: "end", fill: emission_color, fontSize: 22, fontWeight: 600, fontFamily: "DIN Next LT Pro", dy: -40, dx: 0 }), // Definition Plot.text(emissions_text, { x: "year", y: "max_emission", fontFamily: "InterRegular", text: (d) => `globale Treibhausgasemissionen\nin CO₂-Äquivalenten`, textAnchor: "end", dy: -15, dx: 0 }), // kleine Zahl Plot.text(emissions_text_2, { x: "year", y: 0, text: (d) => d.text.toLocaleString("de", { minimumFractionDigits: "1", maximumFractionDigits: "1" }) + " Mrd. t im Jahr " + d.year, textAnchor: "start", opacity: 0.8, fill: emission_color, fontSize: 12, fontWeight: 600, fontFamily: "InterRegular", dy: 10, dx: 0 }), // Quelle Plot.text(emissions_text, { x: "year", y: "min_emission", text: (d) => `Quelle: Global Carbon Project`, textAnchor: "end", fill: "#99AFC2", fontFamily: "InterRegular", dy: 30, dx: 0 }) ] })
emissions_text = [ { year: d3.max(carbon_emissions_total_concat.map((d) => d.year)), max_emission: max_emission, min_emission: 0, text: recent_emission } ]
max_emission = d3.max(carbon_emissions_total.map((d) => d.total))
recent_emission = +carbon_emissions_total_concat[0].total
emissions_text_2 = [ { year: d3.min(carbon_emissions_total_concat.map((d) => d.year)), max_emission: max_emission, min_emission: carbon_emissions_total_concat[carbon_emissions_total_concat.length - 1] .total, text: first_emission } ]
first_emission = +carbon_emissions_total_concat[ carbon_emissions_total_concat.length - 1 ].total
carbon_emissions_total_concat = carbon_2023.concat( carbon_2022.concat(carbon_emissions_total) )
carbon_2022 = [{ year: 2022, total: 40.6 }]
carbon_2023 = [{ year: 2023, total: 40.9 }]
SELECT year, sum(emissions) as total FROM carbon_emissions WHERE year >= ${start} GROUP BY 1 ORDER BY year desc
-// https://globalcarbonbudgetdata.org/latest-data.html carbon_emissions = FileAttachment("carbon_emissions_observable@4.csv")+carbon_emissions = FileAttachment("carbon_emissions_observable@1.csv").csv() .then(function (data) { data.forEach(function (d) { d.emissions = +d.emissions / 1000; d.year = +d.year; }); return data; })
credits_emission = md`<span style="font-family: InterRegular; font-size: 12px; color: #99AFC2;">Quelle: Global Carbon Project<span>`
text_group = [ { x: 2020, y: 8.2 * 3.664, text: "Rest" }, { x: 2020, y: 6.6 * 3.664, text: "Indien" }, { x: 2020, y: 5 * 3.664, text: "China" }, { x: 2020, y: 2.65 * 3.664, text: "Rest" }, { x: 2020, y: 1.3 * 3.664, text: "USA" }, { x: 2020, y: 0.2 * 3.664, text: "EU" } ]
text_emission = [{ x: 1850, y: 40, text: "41,1 Mrd. t" }]
group_order = [+"USA","Europäische Union",-"USA", "andere entwickelte",+"andere entwickelte Länder", "Russland","China", "Indien",-"Russland", "Rest", "Landnutzung"+"Rest der Welt", "internationaler Flugverkehr"]
color_scale = [+"#4f80ff","#80A3FF",-"#B3C8FF", "#B3C8FF", "#ff6531", "#ff834c", "#ff834c", "#ff834c", "#eff3f5"+"#80A3FF", "#ff7842", "#e84f1c", "#ff7842", "#ff7842", "#ff7842", "#ff7842", "#ff7842"]
chart_width = { if (window.innerWidth < 500) { return window.innerWidth; } else { return window.innerWidth / 2 - 22; } }
isDarkMode = () => window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
function roundUTCDay(d) { return new Date( Date.UTC( d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + (d.getUTCHours() < 12 ? 0 : 1) ) ); }
getCsvUrl = (url) => { url = new URL(url); const id = url.pathname.split("/")[3]; const gid = new URLSearchParams(url.hash.slice(1)).get("gid") || 0; return `https://docs.google.com/spreadsheets/d/${id}/export?format=csv&gid=${gid}`; }
sb = ` margin-right: 0.3em; width: 2em; border-radius:0; background-color:${bgc}; border: 0px solid #bbb; outline: none `
bgc = "#ffffff"
bgc2 = "#e84f1c"
function fas(s) { return `<i class="fas fa-${String.raw(...arguments)} fa-xs"></i>`; }
faStyle({ solid: true })
import { disposal } from "@mbostock/disposal"
import { style as faStyle } from "@airbornemint/fontawesome"
function Player( values, { format = (value) => value, index = false, initial = 0, delay = 1000 / 20, autoplay = true, loop = false, alternate = false, width = 300, time = false, speedFactor = 1.5, debug = false, show = null } = {} ) { const delay0 = delay; const alternate0 = alternate; const loop0 = loop; values = Array.from(values); let idx = initial; let isPlay = false; let isAlternate = null; let isLoop = null; let isForward = true; let timer = null; let t0 = null; let t1 = null; const sf = ` font: 12px var(--sans-serif); font-variant-numeric: tabular-nums; display: flex; height: 33px; align-items: center; `; const icons = { play: html`${fas`play`}`, pause: html`${fas`pause`}`, stop: html`${fas`stop`}`, speedUp: html`${fas`chevron-up`}`, speedDown: html`${fas`chevron-down`}`, toRight: html`${fas`arrow-right`}`, toLeft: html`${fas`arrow-left`}`, alternate: html`${fas`exchange-alt`}`, loop: html`${fas`redo`}`, stepRight: html`${fas`step-forward`}`, stepLeft: html`${fas`step-backward`}` }; const btnDesc = [ ["p", "play"], ["s", "stop"], ["l", "stepLeft"], ["r", "stepRight"], ["u", "speedUp"], ["d", "speedDown"], ["w", "toRight"], ["a", "alternate"], ["b", "loop"] ]; const func = !show ? (acc, v) => ((acc[v] = true), acc) : Object.values(show).every((e) => e) ? (acc, v) => ((acc[v] = show[v] === true ? true : false), acc) : Object.values(show).every((e) => !e) ? (acc, v) => ((acc[v] = show[v] === false ? false : true), acc) : (acc, v) => ((acc[v] = show[v] || true), acc); const showBtn = btnDesc.map((e) => e[1]).reduce(func, {}); showBtn.play = true; const btns = btnDesc.map(([x, kind]) => { if (!showBtn[kind]) return ""; const icon = icons[kind]; return html`<button name=${x} type=button style="${sb}">${icon}</button>`; }); const subgroup = html` <div style="${sf}"> ${btns} <div style="display: flex; align-items: center;"> <input name=i type=range min=0 max=${ values.length - 1 } value=${idx} step=1 style="width: ${width}px;"> <output name=o style="margin-left: 0.4em;">${format( values[idx] )}""</output> </div> </div> `; const form = html` <form style="display:flex; justify-content:space-between;"> ${subgroup} <output name=t style="${sf}"></output> </form> `; function log(txt) { if (debug) console.log(txt); } function updateIconP() { const before = form.p.firstChild; const after = isPlay ? icons.pause : icons.play; form.p.replaceChild(after, before); } function updateIconW() { if (!showBtn.toRight) return; const before = form.w.firstChild; const after = isForward ? icons.toRight : icons.toLeft; form.w.replaceChild(after, before); } function updateIconStyleA() { if (!showBtn.alternate) return; form.a.style.background = isAlternate ? bgc2 : bgc; } function updateIconStyleB() { if (!showBtn.loop) return; form.b.style.background = isLoop ? bgc2 : bgc; } function updateForm() { form.i.valueAsNumber = idx; form.value = values[idx]; form.o.value = format(form.value, form.i.valueAsNumber, values); dispatchEvent(); } function dispatchEvent() { form.value = index ? idx : values[idx]; form.dispatchEvent(new CustomEvent("input", { bubbles: true })); } function resetTime() { t0 = new Date(); } function clearTime() { form.t.value = ""; } function updateTime() { const t1 = new Date(); const dt = (t1.getTime() - t0.getTime()) / 1000; if (time) form.t.value = `${dt.toFixed(2)} s`; } function step(isManualForward = null) { const _isForward = isManualForward === null ? isForward : isManualForward; const _canUpdateIsForward = isManualForward === null; if (isManualForward !== null) { log("_isForward", _isForward); log("_canUpdateIsForward", _canUpdateIsForward); } const first = 0; const last = values.length - 1; let move = true; if (_isForward) { if (idx < last) { idx++; } else if (isLoop) { if (isAlternate) { idx = last - 1; if (_canUpdateIsForward) isForward = false; updateIconW(); } else { idx = first; } } else { move = false; } } else if (idx > first) { idx--; } else if (isLoop) { if (isAlternate) { idx = first + 1; if (_canUpdateIsForward) isForward = true; updateIconW(); } else { idx = last; } } else { move = false; } if (!move) { log("no move"); clearInterval(timer); isPlay = false; updateIconP(); } updateForm(); updateTime(); log(idx); } function setTimer() { step(); if (isPlay) timer = setTimeout(setTimer, delay); } function clickPlayPause() { if (isPlay) { log("clicked pause"); isPlay = false; clearTimeout(timer); } else { log("clicked play"); isPlay = true; resetTime(); if (isForward && idx === values.length - 1) idx = 0; if (!isForward && idx === 0) idx = values.length - 1; updateIconP(); setTimer(); } updateIconP(); } function clickStop() { log("clicked stop"); clearInterval(timer); isPlay = false; idx = initial; delay = delay0; alternate = alternate0; loop = loop0; updateIconStyleA(); updateIconStyleB(); updateIconP(); updateForm(); clearTime(); } function clickStepLeft() { log("clicked step left"); if (!isPlay) { step(false); } } function clickStepRight() { log("clicked step right"); if (!isPlay) { step(true); } } function clickSpeedUp() { log("clicked speed up"); delay /= speedFactor; log(delay); } function clickSpeedDown() { log("clicked speed down"); delay *= speedFactor; log(delay); } function clickWay() { log("clicked toggle way"); isForward = !isForward; updateIconW(); } function clickAlternate() { log("clicked toggle alternate"); isAlternate = !isAlternate; updateIconStyleA(); } function clickLoop() { log("clicked toggle loop"); isLoop = !isLoop; updateIconStyleB(); } if (showBtn.play) form.p.onclick = clickPlayPause; if (showBtn.stop) form.s.onclick = clickStop; if (showBtn.stepLeft) form.l.onclick = clickStepLeft; if (showBtn.stepRight) form.r.onclick = clickStepRight; if (showBtn.speedUp) form.u.onclick = clickSpeedUp; if (showBtn.speedDown) form.d.onclick = clickSpeedDown; if (showBtn.toRight) form.w.onclick = clickWay; if (showBtn.alternate) form.a.onclick = clickAlternate; if (showBtn.loop) form.b.onclick = clickLoop; form.i.oninput = (event) => { // console.log('form.i event', event, form.i.valueAsNumber, form.i.value); idx = form.i.valueAsNumber; updateForm(); }; if (alternate) clickAlternate(); if (loop) clickLoop(); if (autoplay) clickPlayPause(); dispatchEvent(); if (showBtn.stop) disposal(form).then(clickStop); const update = (i) => { idx = i; updateForm(); }; const remoteClickPlayPause = () => { clickPlayPause(); }; return Object.assign(form, { update, remoteClickPlayPause }); }
style = html` <div></div> <style>-figure, .plot-d6a7b5 { margin-left: 0px; margin-right: 0px; padding: 0px; } .klima { background-color: #4f80ff; margin-bottom: 10px; margin-top: 30px; color: #ffffff; font-weight: 500; height: 40px; border: 1px solid rgb(79, 128, 255); padding: 10px 15px; border-radius: 4px; font-size: 14px; line-height: 18px; cursor: pointer; font-family: InterRegular; }.box_one {-width: calc(50% - 10px); padding-top: 0px; padding-bottom: 0px;+width: calc(33% - 10px); padding-top: 10px; padding-bottom: 10px;padding-left: 0px; padding-right: 10px; } .box_two {-width: calc(50% - 12px); padding-top: 0px; padding-bottom: 0px;+width: calc(33% - 22px); padding-top: 10px; padding-bottom: 10px;padding-left: 10px;+padding-right: 10px; border-left: 2px dotted #c5d2dd; } .box_three { width: calc(33% - 12px); padding-left: 10px;padding-right: 0px;+padding-top: 10px; padding-bottom: 10px;border-left: 2px dotted #c5d2dd; }-@media (max-width: 500px) {+@media (max-width: 900px) {-.box_one {+.box_one, .box_two, .box_three {width: 100%;-padding-top: 0px; padding-bottom: 20px;+padding-top: 10px; padding-bottom: 10px;padding-left: 0px; padding-right: 0px; border-top: 0px dotted #c5d2dd; border-bottom: 2px dotted #c5d2dd; border-left: 0px dotted #c5d2dd; border-right: 0px dotted #c5d2dd; }-.box_two { width: 100%; padding-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; border-top: 0px dotted #c5d2dd; border-bottom: 0px dotted #c5d2dd; border-left: 0px dotted #c5d2dd; border-right: 0px dotted #c5d2dd; }} .legend { bottom: 3px; position: relative; margin-right: 5px; width: 20px; height: 1px; background-color: #0f151a; display: inline-block;" } .link { color: #4f80ff; } .box { display: flex; flex-wrap: wrap; } .tooltip { fill: #fff; } .tooltip_text { color: #000; } .mode { color: #0f151a; background-color: #ffffff; } @font-face { font-family: Source Serif Pro; src: url(https://static.rndtech.de/share/rnd/jchrist/SourceSerifPro-Regular.ttf); } @font-face { font-display: swap; font-family: 'DIN Next LT Pro'; font-style: normal; font-weight: 700; src: url(https://static.rndtech.de/share/rnd/jchrist/DINNextLTPro-Bold.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/DINNextLTPro-Bold.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/DINNextLTPro-Bold.ttf) format("truetype") } @font-face { font-display: swap; font-family: 'InterRegular'; font-style: normal; font-weight: 400; src: url(https://static.rndtech.de/share/rnd/jchrist/Inter-Regular.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Regular.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Regular.ttf) format("truetype") } @font-face { font-display: swap; font-family: 'InterMedium'; font-style: normal; font-weight: 500; src: url(https://static.rndtech.de/share/rnd/jchrist/Inter-Medium.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Medium.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Medium.ttf) format("truetype") } @font-face { font-display: swap; font-family: 'InterBold'; font-style: normal; font-weight: 700; src: url(https://static.rndtech.de/share/rnd/jchrist/Inter-Bold.woff2) format("woff2"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Bold.woff) format("woff"), url(https://static.rndtech.de/share/rnd/jchrist/Inter-Bold.ttf) format("truetype") } h2 { color: rgb(15, 21, 26); font-family: "DIN Next LT Pro", sans-serif; font-weight: 700; font-size: 22px; margin: 0px 0px 0px 0px; } .title { color: rgb(15, 21, 26); font-family: "DIN Next LT Pro", sans-serif; font-weight: 700; font-size: 22px; margin: 0px 0px 0px 0px; } .subtitle { font-family: "Source Serif Pro", serif; font-size: 17px; font-weight: 400; color: rgb(41, 56, 69); } .credits { font-size: 12px; color: #99AFC2; font-family: InterRegular, sans-serif; font-weight: 400; position: relative; } body { display: block; margin-left: 0px; margin-right: 0px; margin-bottom: 0px; margin-top: 0px; } svg { background-color: white !important; color: #0f151a !important; } .tooltip { fill: #fff; } .tooltip_text { color: #000; } g:not([font-size], [font-weight]) text { font-size: 12px; line-height: 125%; font-weight: 400; font-family: InterRegular; color: #0f151a; background-color: #ffffff; } @media (prefers-color-scheme: dark) { .legend { bottom: 3px; position: relative; margin-right: 5px; width: 20px; height: 1px; background-color: #ffffff; display: inline-block;" } .tooltip { fill: #293845; opacity: .85; } .tooltip_text { color: #fff; } .mode { color: #ffffff; background-color: #293845; } .subtitle { font-family: "Source Serif Pro", serif; font-size: 17px; font-weight: 400; color: #ffffff; } g:not([font-size], [font-weight]) text { font-size: 12px; font-weight: 400; font-family: InterRegular; color: #ffffff; background-color: #293845; } svg { background-color: #293845 !important; color: white !important; } .subtitle { font-family: "Source Serif Pro", serif; font-size: 17px; font-weight: 400; color: #ffffff; } g:not([font-size], [font-weight]) text { font-size: 12px; line-height: 125%; font-weight: 400; font-family: InterRegular; color: #ffffff; background-color: #293845; } } </style>`