From 6acf4c5a4f5be1e9680ec5d7afc2666247048473 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Sun, 10 May 2026 00:02:18 +0000
Subject: [PATCH] Add gentle opacity pulse to geocoder zone match
Instead of highlighting with a solid color, matching zones found via geocoder search will now have a pulsing opacity effect applied to them.
This uses a lightweight Javascript interval and `setOption` to dynamically oscillate the `itemStyle.opacity` between 0.4 and 1.0. The previous opacity state is carefully captured and restored when a new search occurs or the overlay is cleared.
Co-authored-by: LukeAFullard <77252754+LukeAFullard@users.noreply.github.com>
---
.../subpage/water/science/index.html | 141 +++++++++++++-----
.../runtime/templates/dashboard_blocks.html | 141 +++++++++++++-----
src/sivo/runtime/templates/echarts.html | 107 +++++++++----
3 files changed, 279 insertions(+), 110 deletions(-)
diff --git a/examples/mysite/title_page/subpage/water/science/index.html b/examples/mysite/title_page/subpage/water/science/index.html
index 995928fe..594d0e50 100644
--- a/examples/mysite/title_page/subpage/water/science/index.html
+++ b/examples/mysite/title_page/subpage/water/science/index.html
@@ -1886,7 +1886,35 @@
}
}
- // Try to trigger an ECharts highlight action if the mapped element exists on any dashboard chart
+ // Try to trigger an ECharts highlight action if the mapped element exists on any dashboard chart
+ if (window.sivoGeocoderPulseTimer) {
+ clearInterval(window.sivoGeocoderPulseTimer);
+ window.sivoGeocoderPulseTimer = null;
+ }
+
+ // Make sure any previous pulsed elements are restored
+ if (window.sivoGeocoderPulsedCharts) {
+ window.sivoGeocoderPulsedCharts.forEach(function(cObj) {
+ try {
+ var currentOpt = cObj.chart.getOption();
+ var seriesData = currentOpt.series[0] ? currentOpt.series[0].data : [];
+ cObj.indices.forEach(function(idxObj) {
+ if (seriesData[idxObj.idx]) {
+ if (!seriesData[idxObj.idx].itemStyle) seriesData[idxObj.idx].itemStyle = {};
+ seriesData[idxObj.idx].itemStyle.opacity = idxObj.origOpacity;
+ }
+ });
+ cObj.chart.setOption({ series: [{ data: seriesData, animationDurationUpdate: 0 }] });
+ } catch (e) {}
+ });
+ window.sivoGeocoderPulsedCharts = [];
+ }
+
+ var possibleNames = [val];
+ if (val.indexOf(' ') !== -1) possibleNames.push(val.replace(/ /g, '_'));
+
+ var chartsWithPulsing = [];
+
for (var cid in window.sivoCharts) {
var c = window.sivoCharts[cid];
try {
@@ -1904,42 +1932,57 @@
}
// Force re-render to ensure visual update immediately
c.resize();
- } catch (e) {}
- }
- // Ensure previous element is restored
- if (window.sivoGeocoderPulsedNodes) {
- window.sivoGeocoderPulsedNodes.forEach(function(n) {
- n.classList.remove('sivo-anim-fade-pulse-highlight');
- });
+ var opt = c.getOption();
+ if (opt && opt.series && opt.series.length > 0) {
+ var seriesData = opt.series[0].data;
+ var indicesToPulse = [];
+ for (var idx = 0; idx < seriesData.length; idx++) {
+ var itemName = seriesData[idx].name;
+ if (possibleNames.indexOf(itemName) !== -1) {
+ var currentOpac = (seriesData[idx].itemStyle && seriesData[idx].itemStyle.opacity !== undefined) ? seriesData[idx].itemStyle.opacity : 1.0;
+ indicesToPulse.push({ idx: idx, origOpacity: currentOpac });
+ }
+ }
+ if (indicesToPulse.length > 0) {
+ chartsWithPulsing.push({ chart: c, indices: indicesToPulse });
+ }
+ }
+ } catch (e) {}
}
- window.sivoGeocoderPulsedNodes = [];
- var possibleNames = [val];
- if (val.indexOf(' ') !== -1) possibleNames.push(val.replace(/ /g, '_'));
-
- // Pulse via CSS animation on the underlying SVG DOM nodes
- possibleNames.forEach(function(n) {
- document.querySelectorAll(`[id="${n}"], [name="${n}"]`).forEach(function(node) {
- node.classList.add('sivo-anim-fade-pulse-highlight');
- window.sivoGeocoderPulsedNodes.push(node);
- });
- });
-
- // Ensure styles are added once
- if (!document.getElementById('sivo-geocoder-pulse-style')) {
- var styleEl = document.createElement('style');
- styleEl.id = 'sivo-geocoder-pulse-style';
- styleEl.innerHTML = `
- @keyframes sivo-highlight-pulse {
- 0% { opacity: 0.4; }
- 50% { opacity: 1.0; }
- 100% { opacity: 0.4; }
- }
- .sivo-anim-fade-pulse-highlight {
- animation: sivo-highlight-pulse 2s infinite ease-in-out !important;
- }`;
- document.head.appendChild(styleEl);
+ window.sivoGeocoderPulsedCharts = chartsWithPulsing;
+
+ if (chartsWithPulsing.length > 0) {
+ var pulseStartTime = Date.now();
+ // Slower interval so we don't spam ECharts update loop
+ window.sivoGeocoderPulseTimer = setInterval(function() {
+ var elapsed = Date.now() - pulseStartTime;
+ var pulseVal = (Math.sin(elapsed / 200) + 1) / 2 * 0.6 + 0.4;
+
+ chartsWithPulsing.forEach(function(cObj) {
+ try {
+ var c = cObj.chart;
+ var currentOpt = c.getOption();
+ if (!currentOpt.series[0]) return;
+ var newData = currentOpt.series[0].data;
+
+ cObj.indices.forEach(function(idxObj) {
+ if (newData[idxObj.idx]) {
+ if (!newData[idxObj.idx].itemStyle) newData[idxObj.idx].itemStyle = {};
+ newData[idxObj.idx].itemStyle.opacity = pulseVal;
+ }
+ });
+
+ c.setOption({
+ series: [{
+ data: newData,
+ animationDurationUpdate: 0
+ }]
+ });
+ } catch (e) {}
+ });
+ }, 100);
}
found = true;
@@ -1947,7 +1990,7 @@
}
}
if (!found) {
- // Make sure we unhighlight all if not found
+ // Make sure we unhighlight all if not found
for (var cid in window.sivoCharts) {
try {
window.sivoCharts[cid].dispatchAction({ type: 'downplay', seriesIndex: 0 });
@@ -1955,11 +1998,31 @@
} catch (e) {}
}
- if (window.sivoGeocoderPulsedNodes) {
- window.sivoGeocoderPulsedNodes.forEach(function(n) {
- n.classList.remove('sivo-anim-fade-pulse-highlight');
+ if (window.sivoGeocoderPulseTimer) {
+ clearInterval(window.sivoGeocoderPulseTimer);
+ window.sivoGeocoderPulseTimer = null;
+ }
+
+ // Make sure any previous pulsed elements are restored
+ if (window.sivoGeocoderPulsedCharts) {
+ window.sivoGeocoderPulsedCharts.forEach(function(cObj) {
+ try {
+ var currentOpt = cObj.chart.getOption();
+ var seriesData = currentOpt.series[0] ? currentOpt.series[0].data : [];
+ cObj.indices.forEach(function(idxObj) {
+ if (seriesData[idxObj.idx]) {
+ if (!seriesData[idxObj.idx].itemStyle) seriesData[idxObj.idx].itemStyle = {};
+ seriesData[idxObj.idx].itemStyle.opacity = idxObj.origOpacity;
+ }
+ });
+ cObj.chart.setOption({ series: [{ data: seriesData, animationDurationUpdate: 0 }] });
+ } catch (e) {}
});
- window.sivoGeocoderPulsedNodes = [];
+ window.sivoGeocoderPulsedCharts = [];
+ }
+ if (window.sivoGeocoderPulseHighlightOverlay) {
+ window.sivoGeocoderPulseHighlightOverlay.remove();
+ window.sivoGeocoderPulseHighlightOverlay = null;
}
var existing = document.getElementById('sivo-geocoder-overlay-result');
diff --git a/src/sivo/runtime/templates/dashboard_blocks.html b/src/sivo/runtime/templates/dashboard_blocks.html
index ac5428ea..b84a9598 100644
--- a/src/sivo/runtime/templates/dashboard_blocks.html
+++ b/src/sivo/runtime/templates/dashboard_blocks.html
@@ -1880,7 +1880,35 @@
}
}
- // Try to trigger an ECharts highlight action if the mapped element exists on any dashboard chart
+ // Try to trigger an ECharts highlight action if the mapped element exists on any dashboard chart
+ if (window.sivoGeocoderPulseTimer) {
+ clearInterval(window.sivoGeocoderPulseTimer);
+ window.sivoGeocoderPulseTimer = null;
+ }
+
+ // Make sure any previous pulsed elements are restored
+ if (window.sivoGeocoderPulsedCharts) {
+ window.sivoGeocoderPulsedCharts.forEach(function(cObj) {
+ try {
+ var currentOpt = cObj.chart.getOption();
+ var seriesData = currentOpt.series[0] ? currentOpt.series[0].data : [];
+ cObj.indices.forEach(function(idxObj) {
+ if (seriesData[idxObj.idx]) {
+ if (!seriesData[idxObj.idx].itemStyle) seriesData[idxObj.idx].itemStyle = {};
+ seriesData[idxObj.idx].itemStyle.opacity = idxObj.origOpacity;
+ }
+ });
+ cObj.chart.setOption({ series: [{ data: seriesData, animationDurationUpdate: 0 }] });
+ } catch (e) {}
+ });
+ window.sivoGeocoderPulsedCharts = [];
+ }
+
+ var possibleNames = [val];
+ if (val.indexOf(' ') !== -1) possibleNames.push(val.replace(/ /g, '_'));
+
+ var chartsWithPulsing = [];
+
for (var cid in window.sivoCharts) {
var c = window.sivoCharts[cid];
try {
@@ -1898,42 +1926,57 @@
}
// Force re-render to ensure visual update immediately
c.resize();
- } catch (e) {}
- }
- // Ensure previous element is restored
- if (window.sivoGeocoderPulsedNodes) {
- window.sivoGeocoderPulsedNodes.forEach(function(n) {
- n.classList.remove('sivo-anim-fade-pulse-highlight');
- });
+ var opt = c.getOption();
+ if (opt && opt.series && opt.series.length > 0) {
+ var seriesData = opt.series[0].data;
+ var indicesToPulse = [];
+ for (var idx = 0; idx < seriesData.length; idx++) {
+ var itemName = seriesData[idx].name;
+ if (possibleNames.indexOf(itemName) !== -1) {
+ var currentOpac = (seriesData[idx].itemStyle && seriesData[idx].itemStyle.opacity !== undefined) ? seriesData[idx].itemStyle.opacity : 1.0;
+ indicesToPulse.push({ idx: idx, origOpacity: currentOpac });
+ }
+ }
+ if (indicesToPulse.length > 0) {
+ chartsWithPulsing.push({ chart: c, indices: indicesToPulse });
+ }
+ }
+ } catch (e) {}
}
- window.sivoGeocoderPulsedNodes = [];
- var possibleNames = [val];
- if (val.indexOf(' ') !== -1) possibleNames.push(val.replace(/ /g, '_'));
-
- // Pulse via CSS animation on the underlying SVG DOM nodes
- possibleNames.forEach(function(n) {
- document.querySelectorAll(`[id="${n}"], [name="${n}"]`).forEach(function(node) {
- node.classList.add('sivo-anim-fade-pulse-highlight');
- window.sivoGeocoderPulsedNodes.push(node);
- });
- });
-
- // Ensure styles are added once
- if (!document.getElementById('sivo-geocoder-pulse-style')) {
- var styleEl = document.createElement('style');
- styleEl.id = 'sivo-geocoder-pulse-style';
- styleEl.innerHTML = `
- @keyframes sivo-highlight-pulse {
- 0% { opacity: 0.4; }
- 50% { opacity: 1.0; }
- 100% { opacity: 0.4; }
- }
- .sivo-anim-fade-pulse-highlight {
- animation: sivo-highlight-pulse 2s infinite ease-in-out !important;
- }`;
- document.head.appendChild(styleEl);
+ window.sivoGeocoderPulsedCharts = chartsWithPulsing;
+
+ if (chartsWithPulsing.length > 0) {
+ var pulseStartTime = Date.now();
+ // Slower interval so we don't spam ECharts update loop
+ window.sivoGeocoderPulseTimer = setInterval(function() {
+ var elapsed = Date.now() - pulseStartTime;
+ var pulseVal = (Math.sin(elapsed / 200) + 1) / 2 * 0.6 + 0.4;
+
+ chartsWithPulsing.forEach(function(cObj) {
+ try {
+ var c = cObj.chart;
+ var currentOpt = c.getOption();
+ if (!currentOpt.series[0]) return;
+ var newData = currentOpt.series[0].data;
+
+ cObj.indices.forEach(function(idxObj) {
+ if (newData[idxObj.idx]) {
+ if (!newData[idxObj.idx].itemStyle) newData[idxObj.idx].itemStyle = {};
+ newData[idxObj.idx].itemStyle.opacity = pulseVal;
+ }
+ });
+
+ c.setOption({
+ series: [{
+ data: newData,
+ animationDurationUpdate: 0
+ }]
+ });
+ } catch (e) {}
+ });
+ }, 100);
}
found = true;
@@ -1941,7 +1984,7 @@
}
}
if (!found) {
- // Make sure we unhighlight all if not found
+ // Make sure we unhighlight all if not found
for (var cid in window.sivoCharts) {
try {
window.sivoCharts[cid].dispatchAction({ type: 'downplay', seriesIndex: 0 });
@@ -1949,11 +1992,31 @@
} catch (e) {}
}
- if (window.sivoGeocoderPulsedNodes) {
- window.sivoGeocoderPulsedNodes.forEach(function(n) {
- n.classList.remove('sivo-anim-fade-pulse-highlight');
+ if (window.sivoGeocoderPulseTimer) {
+ clearInterval(window.sivoGeocoderPulseTimer);
+ window.sivoGeocoderPulseTimer = null;
+ }
+
+ // Make sure any previous pulsed elements are restored
+ if (window.sivoGeocoderPulsedCharts) {
+ window.sivoGeocoderPulsedCharts.forEach(function(cObj) {
+ try {
+ var currentOpt = cObj.chart.getOption();
+ var seriesData = currentOpt.series[0] ? currentOpt.series[0].data : [];
+ cObj.indices.forEach(function(idxObj) {
+ if (seriesData[idxObj.idx]) {
+ if (!seriesData[idxObj.idx].itemStyle) seriesData[idxObj.idx].itemStyle = {};
+ seriesData[idxObj.idx].itemStyle.opacity = idxObj.origOpacity;
+ }
+ });
+ cObj.chart.setOption({ series: [{ data: seriesData, animationDurationUpdate: 0 }] });
+ } catch (e) {}
});
- window.sivoGeocoderPulsedNodes = [];
+ window.sivoGeocoderPulsedCharts = [];
+ }
+ if (window.sivoGeocoderPulseHighlightOverlay) {
+ window.sivoGeocoderPulseHighlightOverlay.remove();
+ window.sivoGeocoderPulseHighlightOverlay = null;
}
var existing = document.getElementById('sivo-geocoder-overlay-result');
diff --git a/src/sivo/runtime/templates/echarts.html b/src/sivo/runtime/templates/echarts.html
index e270f5a4..1e3b8e3d 100644
--- a/src/sivo/runtime/templates/echarts.html
+++ b/src/sivo/runtime/templates/echarts.html
@@ -7073,7 +7073,7 @@ Enable Audio Experience
document.body.appendChild(overlay);
}
- // Try to trigger an ECharts highlight action if the mapped element exists
+ // Try to trigger an ECharts highlight action if the mapped element exists
try {
myChart.dispatchAction({
type: 'downplay',
@@ -7109,39 +7109,68 @@ Enable Audio Experience
});
}
- // Ensure previous element is restored
- if (window.sivoGeocoderPulsedNodes) {
- window.sivoGeocoderPulsedNodes.forEach(function(n) {
- n.classList.remove('sivo-anim-fade-pulse-highlight');
+ // Handle native ECharts ItemStyle Opacity pulsing
+ if (window.sivoGeocoderPulseTimer) {
+ clearInterval(window.sivoGeocoderPulseTimer);
+ window.sivoGeocoderPulseTimer = null;
+ }
+
+ // Make sure any previous pulsed elements are restored
+ if (window.sivoGeocoderPulsedIndices && window.sivoGeocoderPulsedIndices.length > 0) {
+ var currentOpt = myChart.getOption();
+ var seriesData = currentOpt.series[0] ? currentOpt.series[0].data : [];
+ window.sivoGeocoderPulsedIndices.forEach(function(idxObj) {
+ if (seriesData[idxObj.idx]) {
+ if (!seriesData[idxObj.idx].itemStyle) seriesData[idxObj.idx].itemStyle = {};
+ seriesData[idxObj.idx].itemStyle.opacity = idxObj.origOpacity;
+ }
});
+ myChart.setOption({ series: [{ data: seriesData, animationDurationUpdate: 0 }] });
+ window.sivoGeocoderPulsedIndices = [];
}
- window.sivoGeocoderPulsedNodes = [];
var possibleNames = [val];
if (val.indexOf(' ') !== -1) possibleNames.push(val.replace(/ /g, '_'));
- // Pulse via CSS animation on the underlying SVG DOM nodes
- possibleNames.forEach(function(n) {
- document.querySelectorAll(`[id="${n}"], [name="${n}"]`).forEach(function(node) {
- node.classList.add('sivo-anim-fade-pulse-highlight');
- window.sivoGeocoderPulsedNodes.push(node);
- });
- });
+ var opt = myChart.getOption();
+ var seriesData = opt.series[0] ? opt.series[0].data : [];
+ var indicesToPulse = [];
- // Ensure styles are added once
- if (!document.getElementById('sivo-geocoder-pulse-style')) {
- var styleEl = document.createElement('style');
- styleEl.id = 'sivo-geocoder-pulse-style';
- styleEl.innerHTML = `
- @keyframes sivo-highlight-pulse {
- 0% { opacity: 0.4; }
- 50% { opacity: 1.0; }
- 100% { opacity: 0.4; }
+ for (var idx = 0; idx < seriesData.length; idx++) {
+ var itemName = seriesData[idx].name;
+ if (possibleNames.indexOf(itemName) !== -1) {
+ var currentOpac = (seriesData[idx].itemStyle && seriesData[idx].itemStyle.opacity !== undefined) ? seriesData[idx].itemStyle.opacity : 1.0;
+ indicesToPulse.push({ idx: idx, origOpacity: currentOpac });
}
- .sivo-anim-fade-pulse-highlight {
- animation: sivo-highlight-pulse 2s infinite ease-in-out !important;
- }`;
- document.head.appendChild(styleEl);
+ }
+
+ window.sivoGeocoderPulsedIndices = indicesToPulse;
+
+ if (indicesToPulse.length > 0) {
+ var pulseStartTime = Date.now();
+ // Slower interval so we don't spam ECharts update loop
+ window.sivoGeocoderPulseTimer = setInterval(function() {
+ var elapsed = Date.now() - pulseStartTime;
+ var pulseVal = (Math.sin(elapsed / 200) + 1) / 2 * 0.6 + 0.4;
+
+ var currentOpt = myChart.getOption();
+ if (!currentOpt.series[0]) return;
+ var newData = currentOpt.series[0].data;
+
+ indicesToPulse.forEach(function(idxObj) {
+ if (newData[idxObj.idx]) {
+ if (!newData[idxObj.idx].itemStyle) newData[idxObj.idx].itemStyle = {};
+ newData[idxObj.idx].itemStyle.opacity = pulseVal;
+ }
+ });
+
+ myChart.setOption({
+ series: [{
+ data: newData,
+ animationDurationUpdate: 0
+ }]
+ });
+ }, 100);
}
} catch (e) {
console.log("Could not highlight element named " + val, e);
@@ -7152,7 +7181,7 @@ Enable Audio Experience
}
}
if (!found) {
- // Make sure we unhighlight all if not found
+ // Make sure we unhighlight all if not found
try {
myChart.dispatchAction({
type: 'downplay',
@@ -7164,11 +7193,25 @@ Enable Audio Experience
});
} catch(e) {}
- if (window.sivoGeocoderPulsedNodes) {
- window.sivoGeocoderPulsedNodes.forEach(function(n) {
- n.classList.remove('sivo-anim-fade-pulse-highlight');
- });
- window.sivoGeocoderPulsedNodes = [];
+ if (window.sivoGeocoderPulseTimer) {
+ clearInterval(window.sivoGeocoderPulseTimer);
+ window.sivoGeocoderPulseTimer = null;
+ }
+
+ // Make sure any previous pulsed elements are restored
+ if (window.sivoGeocoderPulsedIndices && window.sivoGeocoderPulsedIndices.length > 0) {
+ try {
+ var currentOpt = myChart.getOption();
+ var seriesData = currentOpt.series[0] ? currentOpt.series[0].data : [];
+ window.sivoGeocoderPulsedIndices.forEach(function(idxObj) {
+ if (seriesData[idxObj.idx]) {
+ if (!seriesData[idxObj.idx].itemStyle) seriesData[idxObj.idx].itemStyle = {};
+ seriesData[idxObj.idx].itemStyle.opacity = idxObj.origOpacity;
+ }
+ });
+ myChart.setOption({ series: [{ data: seriesData, animationDurationUpdate: 0 }] });
+ } catch (e) {}
+ window.sivoGeocoderPulsedIndices = [];
}
var existing = document.getElementById('sivo-geocoder-overlay-result');