Options Watching
NiiVue provides a powerful options watching system that allows you to monitor changes to any configuration option in real-time. This is particularly useful for creating responsive UIs, synchronizing multiple viewers, or implementing custom behaviors based on user interactions.
Try the interactive demo below! Use the controls to change various options and watch the change log update in real-time.
Overview
The options watching feature uses JavaScript Proxies to detect when any property in the opts
object changes. When a change occurs, a callback function is triggered with information about what changed, including the property name, old value, and new value.
Basic Usage
Setting up Options Watching
Use the watchOptsChanges()
method to start monitoring option changes:
// Create a Niivue instance
const nv = new Niivue();
await nv.attachToCanvas(canvas);
// Set up options watcher
nv.watchOptsChanges((propertyName, newValue, oldValue) => {
console.log(`Option ${propertyName} changed:`);
console.log(` Old value:`, oldValue);
console.log(` New value:`, newValue);
});
// Now any changes to options will trigger the callback
nv.opts.crosshairWidth = 5; // Triggers callback
nv.opts.backColor = [1, 0, 0, 1]; // Triggers callback
nv.opts.isColorbar = true; // Triggers callback
Alternative: Direct Callback Assignment
You can also assign the callback directly to the onOptsChange
property:
nv.onOptsChange = (propertyName, newValue, oldValue) => {
console.log(`${propertyName}: ${oldValue} → ${newValue}`);
};
Stopping Options Watching
Use unwatchOptsChanges()
to stop monitoring changes:
// Stop watching for changes
nv.unwatchOptsChanges();
// Changes after this point will not trigger callbacks
nv.opts.crosshairWidth = 10; // No callback triggered
Advanced Features
Change Detection Logic
The options watcher is intelligent about change detection:
- No duplicate events: Setting the same value multiple times only triggers the callback once
- Deep value comparison: Array and object changes are properly detected
- Type preservation: Original data types are maintained in old/new value parameters
// Only triggers callback once, even though set multiple times
nv.opts.crosshairWidth = 3;
nv.opts.crosshairWidth = 3; // No callback (same value)
nv.opts.crosshairWidth = 3; // No callback (same value)
nv.opts.crosshairWidth = 5; // Triggers callback (different value)
Array and Object Changes
The watcher properly handles complex data types:
nv.watchOptsChanges((propertyName, newValue, oldValue) => {
if (propertyName === 'backColor') {
console.log('Background color changed:');
console.log('Old:', oldValue); // [0, 0, 0, 1]
console.log('New:', newValue); // [1, 0, 0, 1]
}
});
// Array changes are detected
nv.opts.backColor = [1, 0, 0, 1]; // Red background
nv.opts.crosshairColor = [0, 1, 0, 1]; // Green crosshair
nv.opts.clipVolumeLow = [0.1, 0.1, 0.1]; // Clipping bounds
Automatic Cleanup
The options watcher is automatically cleaned up when the Niivue instance is destroyed:
// Watcher is active
nv.watchOptsChanges(callback);
// Cleanup the instance
nv.cleanup();
// Watcher is automatically removed - no memory leaks
Example Use Cases
Creating Responsive UIs
nv.watchOptsChanges((propertyName, newValue, oldValue) => {
// Update UI controls when options change programmatically
if (propertyName === 'isColorbar') {
document.getElementById('colorbarToggle').checked = newValue;
}
if (propertyName === 'backColor') {
updateColorPicker('backgroundColor', newValue);
}
});
Implementing Undo/Redo
const changeHistory = [];
nv.watchOptsChanges((propertyName, newValue, oldValue) => {
// Store change for undo functionality
changeHistory.push({
property: propertyName,
oldValue: oldValue,
newValue: newValue,
timestamp: Date.now()
});
// Keep history manageable
if (changeHistory.length > 100) {
changeHistory.shift();
}
});
function undo() {
const lastChange = changeHistory.pop();
if (lastChange) {
// Temporarily stop watching to avoid creating new history entries
nv.unwatchOptsChanges();
nv.opts[lastChange.property] = lastChange.oldValue;
nv.watchOptsChanges(/* restore watcher */);
}
}
Analytics and Logging
nv.watchOptsChanges((propertyName, newValue, oldValue) => {
// Log user interactions for analytics
analytics.track('niivue_option_changed', {
property: propertyName,
oldValue: JSON.stringify(oldValue),
newValue: JSON.stringify(newValue),
sessionId: getCurrentSessionId()
});
});
Supported Options
The options watcher monitors all properties in the NVConfigOptions
interface, including:
Visual Options
crosshairWidth
,crosshairColor
,crosshairGap
backColor
,fontColor
textHeight
,colorbarHeight
,colorbarWidth
isColorbar
,isRuler
,isOrientCube
Interaction Options
dragMode
,doubleTouchTimeout
,longTouchTimeout
penValue
,penSize
,isFilledPen
drawingEnabled
Display Options
isRadiologicalConvention
,isNearestInterpolation
show3Dcrosshair
,showColorbarBorder
multiplanarShowRender
,multiplanarEqualSize
Layout Options
sliceMosaicString
clipVolumeLow
,clipVolumeHigh
And many more! Any option that can be set through nv.opts
will trigger the watcher when changed.
Technical Details
Performance
The options watching system is designed to be lightweight:
- Uses JavaScript Proxies for efficient property interception
- No polling or periodic checks required
- Minimal overhead for unchanged properties
- Automatic cleanup prevents memory leaks
Type Safety
When using TypeScript, the options watcher provides full type safety:
import { NVConfigOptions } from '@niivue/niivue';
nv.watchOptsChanges((
propertyName: keyof NVConfigOptions,
newValue: NVConfigOptions[keyof NVConfigOptions],
oldValue: NVConfigOptions[keyof NVConfigOptions]
) => {
// TypeScript knows the exact types of all parameters
console.log(`${propertyName} changed from ${oldValue} to ${newValue}`);
});