Skip to main content

Mouse, touch and events

A viewer responds to the mouse, touch and keyboard on its own. You choose what a drag does, and you listen for events to keep the rest of your page in step with the viewer. Pick a drag mode below and drag on a slice; the line under the canvas shows the events it fires.

import NiiVue, { DRAG_MODE } from '@niivue/niivue'

const demo = async (canvas, { ui, asset }) => {
const nv = new NiiVue({ backgroundColor: [0.2, 0.2, 0.2, 1] })
await nv.attachToCanvas(canvas)
await nv.loadVolumes([{ url: asset('volumes/mni152.nii.gz') }])

ui.select(
'Left drag',
[
['crosshair', String(DRAG_MODE.crosshair)],
['crosshairPan', String(DRAG_MODE.crosshairPan)],
['pan', String(DRAG_MODE.pan)],
['contrast', String(DRAG_MODE.contrast)],
['measurement', String(DRAG_MODE.measurement)],
['angle', String(DRAG_MODE.angle)],
],
String(DRAG_MODE.crosshair),
(v) => {
nv.primaryDragMode = Number(v)
},
)

const show = ui.status('Click or drag on a slice')
nv.addEventListener('locationChange', (e) => show(e.detail.string))
nv.addEventListener('dragRelease', (e) => {
show(`dragRelease: ${e.detail.mmLength.toFixed(1)} mm`)
})
nv.addEventListener('measurementCompleted', (e) => {
show(`measurementCompleted: ${e.detail.distance.toFixed(1)} mm`)
})
nv.addEventListener('angleCompleted', (e) => {
show(`angleCompleted: ${e.detail.angle.toFixed(1)} degrees`)
})
return nv
}

export default demo

Drag modes​

primaryDragMode sets what a left-button drag does, and secondaryDragMode sets the right button. Both take a DRAG_MODE value:

ModeWhat a drag does
crosshairmoves the crosshair (primary default)
crosshairPana click moves the crosshair, a drag pans; handy for touch and trackpads
contrastdraws a box; on release the display range is set from the intensities inside it (secondary default)
windowingadjusts the window level and width live as you drag
panpans the 2D view
measurementdraws a distance measurement
angledraws two lines and measures the angle between them
roiSelectiondraws a selection box that stays on screen
slicer3Dpans and zooms the slice
callbackOnlydraws a box and only fires dragRelease, for your own handling
nonenothing
import NiiVue, { DRAG_MODE } from '@niivue/niivue'

const nv = new NiiVue({
primaryDragMode: DRAG_MODE.crosshair,
secondaryDragMode: DRAG_MODE.pan,
})
nv.primaryDragMode = DRAG_MODE.measurement

setDragMode('pan') also accepts a mode by name, but it sets the secondary mode only. clearMeasurements() removes finished measurements and angles (clearDistanceMeasurements() and clearAngles() remove one kind).

Drag modes apply to 2D slices. In the 3D render, a left drag rotates the view, a right drag turns the active clip plane, and a double-click moves the crosshair to the surface under the pointer.

Wheel and touch​

On a 2D slice, the scroll wheel steps through slices. If either drag mode is pan, the wheel zooms instead, anchored at the crosshair (set wheelZoomAnchor = 'pointer' to zoom about the pointer). Over the render, the wheel moves the active clip plane (see Clip planes). On a touch screen, a two-finger pinch zooms.

The old package's setMouseEventConfig and setTouchEventConfig are not ported, so you cannot remap buttons or gestures beyond the two drag modes.

Keyboard​

Keys act on the viewer under the pointer, and are ignored while focus is in a text field or select.

KeyAction
H / Lmove the crosshair left or right (in the render, rotate)
J / Kmove the crosshair posterior or anterior (in the render, tilt)
Ctrl+U / Ctrl+Dmove the crosshair up or down a slice
Vcycle the slice type when isViewModeHotKeyEnabled is true; otherwise log the NiiVue package version to the console (also nv.version)
C / Acycle the clip plane direction / choose the active clip plane
Escapecancel a contour or measurement in progress

moveCrosshairInVox(dx, dy, dz) does the same stepping from your own buttons.

Events​

A viewer is an EventTarget. Use addEventListener and removeEventListener as you would on a DOM element; the data is in e.detail. In TypeScript the detail type follows from the event name.

nv.addEventListener('locationChange', (e) => {
status.textContent = e.detail.string
})

The events you will use most:

  • locationChange: the crosshair moved. detail has mm, vox, the per-volume values, and a ready-made string.
  • dragRelease: a drag ended on a slice, with its start and end points and mmLength.
  • measurementCompleted and angleCompleted: a measurement finished, with its distance or angle.
  • pointerUp: any button release, with canvas x, y and button.
  • sliceTypeChange, azimuthElevationChange, clipPlaneChange and frameChange: the view or 4D frame changed.
  • volumeLoaded, meshLoaded, volumeRemoved and meshRemoved: the scene contents changed.
  • drawingChanged, drawingEnabled and penValueChanged: see Drawing.
  • change: any property setter ran, with { property, value }.

The full list, with every detail field, is in events.md. Callback options from NiiVue 0.x such as onLocationChange are gone; use the events instead. There is no separate intensityChange event: read the values from locationChange.

See also​

  • Gestures: both drag modes, measurements and angles.
  • Events: a live log of every event a viewer fires.