1
1
import { zoomIdentity } from 'd3-zoom'
2
2
import { computed } from 'vue'
3
- import type { D3Selection , GraphNode , Project , State , ViewportFunctions } from '../types'
3
+ import type { D3Selection , GraphNode , Project , State , TransitionOptions , ViewportFunctions } from '../types'
4
4
import { clampPosition , getRectOfNodes , getTransformForBounds , pointToRendererPoint , rendererPointToPoint , warn } from '../utils'
5
5
6
6
export interface ViewportHelper extends ViewportFunctions {
@@ -11,6 +11,10 @@ export interface ViewportHelper extends ViewportFunctions {
11
11
12
12
const DEFAULT_PADDING = 0.1
13
13
14
+ // taken from d3-ease: https://github.com/d3/d3-ease/blob/main/src/cubic.js
15
+ // eslint-disable-next-line no-cond-assign
16
+ const defaultEase = ( t : number ) => ( ( t *= 2 ) <= 1 ? t * t * t : ( t -= 2 ) * t * t + 2 ) / 2
17
+
14
18
function noop ( ) {
15
19
warn ( 'Viewport not initialized yet.' )
16
20
@@ -41,11 +45,11 @@ const initialViewportHelper: ViewportHelper = {
41
45
* @param state
42
46
*/
43
47
export function useViewportHelper ( state : State ) {
44
- function zoom ( scale : number , duration ?: number ) {
48
+ function zoom ( scale : number , transitionOptions ?: TransitionOptions ) {
45
49
return new Promise < boolean > ( ( resolve ) => {
46
50
if ( state . d3Selection && state . d3Zoom ) {
47
51
state . d3Zoom . scaleBy (
48
- transition ( state . d3Selection , duration , ( ) => {
52
+ getD3Transition ( state . d3Selection , transitionOptions ?. duration , transitionOptions ?. ease , ( ) => {
49
53
resolve ( true )
50
54
} ) ,
51
55
scale ,
@@ -56,7 +60,7 @@ export function useViewportHelper(state: State) {
56
60
} )
57
61
}
58
62
59
- function transformViewport ( x : number , y : number , zoom : number , duration ?: number ) {
63
+ function transformViewport ( x : number , y : number , zoom : number , transitionOptions ?: TransitionOptions ) {
60
64
return new Promise < boolean > ( ( resolve ) => {
61
65
// enforce translate extent
62
66
const { x : clampedX , y : clampedY } = clampPosition ( { x : - x , y : - y } , state . translateExtent )
@@ -65,7 +69,7 @@ export function useViewportHelper(state: State) {
65
69
66
70
if ( state . d3Selection && state . d3Zoom ) {
67
71
state . d3Zoom . transform (
68
- transition ( state . d3Selection , duration , ( ) => {
72
+ getD3Transition ( state . d3Selection , transitionOptions ?. duration , transitionOptions ?. ease , ( ) => {
69
73
resolve ( true )
70
74
} ) ,
71
75
nextTransform ,
@@ -87,16 +91,16 @@ export function useViewportHelper(state: State) {
87
91
viewportInitialized : true ,
88
92
// todo: allow passing scale as option
89
93
zoomIn : ( options ) => {
90
- return zoom ( 1.2 , options ?. duration )
94
+ return zoom ( 1.2 , options )
91
95
} ,
92
96
zoomOut : ( options ) => {
93
- return zoom ( 1 / 1.2 , options ?. duration )
97
+ return zoom ( 1 / 1.2 , options )
94
98
} ,
95
99
zoomTo : ( zoomLevel , options ) => {
96
100
return new Promise < boolean > ( ( resolve ) => {
97
101
if ( state . d3Selection && state . d3Zoom ) {
98
102
state . d3Zoom . scaleTo (
99
- transition ( state . d3Selection , options ?. duration , ( ) => {
103
+ getD3Transition ( state . d3Selection , options ?. duration , options ?. ease , ( ) => {
100
104
resolve ( true )
101
105
} ) ,
102
106
zoomLevel ,
@@ -107,10 +111,10 @@ export function useViewportHelper(state: State) {
107
111
} )
108
112
} ,
109
113
setViewport : ( transform , options ) => {
110
- return transformViewport ( transform . x , transform . y , transform . zoom , options ?. duration )
114
+ return transformViewport ( transform . x , transform . y , transform . zoom , options )
111
115
} ,
112
116
setTransform : ( transform , options ) => {
113
- return transformViewport ( transform . x , transform . y , transform . zoom , options ?. duration )
117
+ return transformViewport ( transform . x , transform . y , transform . zoom , options )
114
118
} ,
115
119
getViewport : ( ) => ( {
116
120
x : state . viewport . x ,
@@ -158,14 +162,14 @@ export function useViewportHelper(state: State) {
158
162
options . offset ,
159
163
)
160
164
161
- return transformViewport ( x , y , zoom , options ?. duration )
165
+ return transformViewport ( x , y , zoom , options )
162
166
} ,
163
167
setCenter : ( x , y , options ) => {
164
168
const nextZoom = typeof options ?. zoom !== 'undefined' ? options . zoom : state . maxZoom
165
169
const centerX = state . dimensions . width / 2 - x * nextZoom
166
170
const centerY = state . dimensions . height / 2 - y * nextZoom
167
171
168
- return transformViewport ( centerX , centerY , nextZoom , options ?. duration )
172
+ return transformViewport ( centerX , centerY , nextZoom , options )
169
173
} ,
170
174
fitBounds : ( bounds , options = { padding : DEFAULT_PADDING } ) => {
171
175
const { x, y, zoom } = getTransformForBounds (
@@ -177,7 +181,7 @@ export function useViewportHelper(state: State) {
177
181
options . padding ,
178
182
)
179
183
180
- return transformViewport ( x , y , zoom , options ?. duration )
184
+ return transformViewport ( x , y , zoom , options )
181
185
} ,
182
186
project : ( position ) => pointToRendererPoint ( position , state . viewport , state . snapToGrid , state . snapGrid ) ,
183
187
screenToFlowCoordinate : ( position ) => {
@@ -212,6 +216,12 @@ export function useViewportHelper(state: State) {
212
216
} )
213
217
}
214
218
215
- function transition ( selection : D3Selection , ms = 0 , onEnd : ( ) => void ) {
216
- return ( selection as any ) . transition ( ) . duration ( ms ) . on ( 'end' , onEnd )
219
+ export function getD3Transition ( selection : D3Selection , duration = 0 , ease = defaultEase , onEnd = ( ) => { } ) {
220
+ const hasDuration = typeof duration === 'number' && duration > 0
221
+
222
+ if ( ! hasDuration ) {
223
+ onEnd ( )
224
+ }
225
+
226
+ return hasDuration ? selection . transition ( ) . duration ( duration ) . ease ( ease ) . on ( 'end' , onEnd ) : selection
217
227
}
0 commit comments