diff --git a/src/mol-canvas3d/controls/trackball.ts b/src/mol-canvas3d/controls/trackball.ts index 40f34459a4a7d1a638c1de5b1548aa529b8a7dd3..7955ab4c5976d0c137f2a88c9ed878cf1632a902 100644 --- a/src/mol-canvas3d/controls/trackball.ts +++ b/src/mol-canvas3d/controls/trackball.ts @@ -21,6 +21,9 @@ export const TrackballControlsParams = { zoomSpeed: PD.Numeric(6.0, { min: 0.1, max: 10, step: 0.1 }), panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }), + spin: PD.Boolean(false), + spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }), + staticMoving: PD.Boolean(true, { isHidden: true }), dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }), @@ -50,9 +53,12 @@ namespace TrackballControls { let disposed = false const dragSub = input.drag.subscribe(onDrag) + const interactionEndSub = input.interactionEnd.subscribe(onInteractionEnd) const wheelSub = input.wheel.subscribe(onWheel) const pinchSub = input.pinch.subscribe(onPinch) + let _isInteracting = false; + // For internal use const lastPosition = Vec3.zero() @@ -234,6 +240,8 @@ namespace TrackballControls { // listeners function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) { + _isInteracting = true; + if (isStart) { if (buttons === ButtonsType.Flag.Primary) { Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY)) @@ -257,11 +265,17 @@ namespace TrackballControls { } } + function onInteractionEnd() { + _isInteracting = false; + } + function onWheel({ dy }: WheelInput) { _zoomStart[1] -= dy * 0.0001 } function onPinch({ distance, isStart }: PinchInput) { + _isInteracting = true; + if (isStart) { _touchZoomDistanceStart = distance } @@ -279,16 +293,30 @@ namespace TrackballControls { dragSub.unsubscribe() wheelSub.unsubscribe() pinchSub.unsubscribe() + interactionEndSub.unsubscribe() + } + + const _spinSpeed = Vec2.create(0.005, 0); + function spin() { + _spinSpeed[0] = (p.spinSpeed || 0) / 1000; + if (!_isInteracting) Vec2.add(_moveCurr, _movePrev, _spinSpeed); + if (p.spin) requestAnimationFrame(spin); } // force an update at start update(); + if (props.spin) { spin(); } + return { viewport, get props() { return p as Readonly<TrackballControlsProps> }, - setProps: (props: Partial<TrackballControlsProps>) => { Object.assign(p, props) }, + setProps: (props: Partial<TrackballControlsProps>) => { + const wasSpinning = p.spin + Object.assign(p, props) + if (p.spin && !wasSpinning) requestAnimationFrame(spin) + }, update, reset, diff --git a/src/mol-util/input/input-observer.ts b/src/mol-util/input/input-observer.ts index 84ead2d65d6638c103ef0932ede0a4a3f4d924c6..cbfbd8dfd935be39069e1b0bebdff1c65ebdabbd 100644 --- a/src/mol-util/input/input-observer.ts +++ b/src/mol-util/input/input-observer.ts @@ -136,6 +136,8 @@ interface InputObserver { noContextMenu: boolean drag: Subject<DragInput>, + // Equivalent to mouseUp and touchEnd + interactionEnd: Subject<undefined>, wheel: Subject<WheelInput>, pinch: Subject<PinchInput>, click: Subject<ClickInput>, @@ -169,6 +171,7 @@ namespace InputObserver { let buttons = 0 const drag = new Subject<DragInput>() + const interactionEnd = new Subject<undefined>(); const click = new Subject<ClickInput>() const move = new Subject<MoveInput>() const wheel = new Subject<WheelInput>() @@ -186,6 +189,7 @@ namespace InputObserver { set noContextMenu (value: boolean) { noContextMenu = value }, drag, + interactionEnd, wheel, pinch, click, @@ -297,7 +301,9 @@ namespace InputObserver { } } - function onTouchEnd (ev: TouchEvent) {} + function onTouchEnd (ev: TouchEvent) { + endDrag() + } function onTouchMove (ev: TouchEvent) { if (ev.touches.length === 1) { @@ -331,6 +337,11 @@ namespace InputObserver { function onMouseUp (ev: MouseEvent) { onPointerUp(ev) + endDrag() + } + + function endDrag() { + interactionEnd.next() } function onPointerDown (ev: PointerEvent) {