Thank you very much @mootari @Fil for your help and for both workarounds!
Inline HTML in Markdown
Ah, an interesting fix! I assume that it works because each HTML element is updated separately unless grouped by an HTML code fence.
Resync
I really appreciate the explanation and the patch. For others who may have a similar problem, I have implemented your patch in the app, with the updated source code (and the initial version) here.
Despite quite a bit of effort to grasp the precise logic of the workaround, I still only partially understand it. For example, I am uncertain about the exact significance of the changing parameter values in the implementation of the resync function. I am also unclear on how the render
property operates. I attempted to locate some documentation on it, but was unsuccessful. Could you perhaps point me to it if it exists?
Here is a version of your workaround, adapted to the style of my code base. If you are interested and would spot anything misunderstood (e.g., an inaccurate variable name), I would really appreciate a nod to it:
/**
* Prevents flickering of the crosshair mark by resyncing the pointer transform.
* This approach ensures that the crosshair mark is updated a tick earlier.
* Note: This will not work with elements like tooltips, as they would lose their state.
*
* Different indices for the 'resync' function calls are used to track the state
* independently for each mark type (e.g., crosshair X line, crosshair Y line,
* crosshair X text, and crosshair Y text). This helps manage their transformations individually.
*
* @param {number} i - Index to track the previous value for resync.
* @param {Array} renderHistory - An array to hold the previous values during the iteration of the function.
* @returns {function}
*
* @author This function is based on a workaround suggested by Fil @ Observable.
* See the discussion here: https://talk.observablehq.com/t/flickering-elements-in-chart-with-streaming-data/9733/2
*
* Example usage:
*
* ```javascript
* let renderHistory = []
* // ...
*
* // Crosshair X Line
* Plot.ruleY(
* OHLCVData,
* Plot.pointerY({
* px: "time",
* y: "close",
* stroke: "orange",
* strokeWidth: 1,
* strokeDasharray: "4,4",
* render: resync(renderHistory, 0)
* })
* ),
*
* // Crosshair Y Line
* Plot.ruleX(
* OHLCVData,
* Plot.pointerX({
* x: "time",
* py: "close",
* stroke: "orange",
* strokeWidth: 1,
* strokeDasharray: "4,4",
* render: resync(renderHistory, 1)
* })
* ),
* // ...
* ```
*/
function resync( renderHistory, i ) {
const resyncFunction = ( index, scales, values, dimensions, context, next ) => {
const svgIsConnected = context.ownerSVGElement.isConnected
// If the SVG is not connected, use the saved previous index value.
if ( !svgIsConnected ) {
index = [ renderHistory[i] ]
}
// Otherwise, save the current index value for the next call.
if ( svgIsConnected ) {
renderHistory[i] = index[0]
}
// Proceed with the next function in the render chain.
return next( index, scales, values, dimensions, context )
}
return resyncFunction
}
Crosshair Synchronization Between Charts
Like tooltips, I assume crosshair pinning is also affected due to the workaround, as I can no longer pin the crosshair for the charts for which I applied the workaround:
(The top chart has the workaround implemented; bottom chart is still the old, flickery crosshair code. I can pin the crosshair in the bottom chart.)
This is an acceptable tradeoff, but I expect it to complicate things a bit. Earlier, I spent a significant amount of time trying to synchronize the crosshair locations between multiple charts, but I was unsuccessful. The best result I achieved was making the first chart broadcast its crosshair position to the second chart, which then used it to draw the crosshair. However, there was a considerable delay between the two charts, and the implementation was problematic, so I had reverted the changes. I expect the current fix to potentially complicate things further when I try again. Could you perhaps be able to provide some guidance on how to achieve crosshair location synchronization between multiple charts in this scenario, given the current workaround?
(If you prefer that I post this in a new thread or change the thread title, since this may be considered a new issue, please let me know.)
Many thanks for reading and for any further help or comments in advance.