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');