@@ -54,6 +54,7 @@ import Feature, { FeatureLike } from 'ol/Feature';
5454import { FullScreen , ScaleLine } from 'ol/control' ;
5555import { Coordinate } from 'ol/coordinate' ;
5656import { singleClick } from 'ol/events/condition' ;
57+ import { getCenter } from 'ol/extent' ;
5758import { GeoJSON , MVT } from 'ol/format' ;
5859import { Geometry , Point } from 'ol/geom' ;
5960import { DragAndDrop , Select } from 'ol/interaction' ;
@@ -1664,7 +1665,7 @@ export class MainView extends React.Component<IProps, IStates> {
16641665 const { x, y } = remoteViewport . value . coordinates ;
16651666 const zoom = remoteViewport . value . zoom ;
16661667
1667- this . _moveToPosition ( { x, y } , zoom , 0 ) ;
1668+ this . _flyToPosition ( { x, y } , zoom , 0 ) ;
16681669 }
16691670 } else {
16701671 // If we are unfollowing a remote user, we reset our center and zoom to their previous values
@@ -1676,7 +1677,7 @@ export class MainView extends React.Component<IProps, IStates> {
16761677 const viewportState = localState . viewportState ?. value ;
16771678
16781679 if ( viewportState ) {
1679- this . _moveToPosition ( viewportState . coordinates , viewportState . zoom ) ;
1680+ this . _flyToPosition ( viewportState . coordinates , viewportState . zoom ) ;
16801681 }
16811682 }
16821683 }
@@ -1798,7 +1799,7 @@ export class MainView extends React.Component<IProps, IStates> {
17981799 view . getProjection ( ) ,
17991800 ) ;
18001801
1801- this . _moveToPosition ( { x : centerCoord [ 0 ] , y : centerCoord [ 1 ] } , zoom || 0 ) ;
1802+ this . _flyToPosition ( { x : centerCoord [ 0 ] , y : centerCoord [ 1 ] } , zoom || 0 ) ;
18021803
18031804 // Save the extent if it does not exists, to allow proper export to qgis.
18041805 if ( ! options . extent ) {
@@ -2030,6 +2031,7 @@ export class MainView extends React.Component<IProps, IStates> {
20302031 } ) ;
20312032 }
20322033
2034+ // TODO this and flyToPosition need a rework
20332035 private _onZoomToPosition ( _ : IJupyterGISModel , id : string ) {
20342036 // Check if the id is an annotation
20352037 const annotation = this . _model . annotationModel ?. getAnnotation ( id ) ;
@@ -2048,11 +2050,18 @@ export class MainView extends React.Component<IProps, IStates> {
20482050 if ( ! layer ) {
20492051 const jgisLayer = this . _model . getLayer ( id ) ;
20502052 const layerParams = jgisLayer ?. parameters as ILandmarkLayer ;
2051- this . _Map . getView ( ) . fit ( layerParams . extent ! , {
2052- size : this . _Map . getSize ( ) ,
2053- duration : 500 ,
2054- maxZoom : layerParams . zoom ! ,
2055- } ) ;
2053+ const coords = getCenter ( layerParams . extent ) ;
2054+
2055+ // TODO: Should pass args through signal??
2056+ const { story } = this . _model . getSelectedStory ( ) ;
2057+
2058+ this . _flyToPosition (
2059+ { x : coords [ 0 ] , y : coords [ 1 ] } ,
2060+ layerParams . zoom ,
2061+ ( story ?. transition ?. time ?? 1 ) * 1000 , // second -> ms
2062+ story ?. transition ?. type ,
2063+ ) ;
2064+
20562065 return ;
20572066 }
20582067
@@ -2090,35 +2099,53 @@ export class MainView extends React.Component<IProps, IStates> {
20902099 } ) ;
20912100 }
20922101
2093- private _moveToPosition (
2102+ private _flyToPosition (
20942103 center : { x : number ; y : number } ,
20952104 zoom : number ,
20962105 duration = 1000 ,
2106+ transitionType ?: 'linear' | 'immediate' | 'smooth' ,
20972107 ) {
20982108 const view = this . _Map . getView ( ) ;
2109+ const currentZoom = view . getZoom ( ) || 0 ;
2110+ const targetCenter : Coordinate = [ center . x , center . y ] ;
20992111
2100- view . setZoom ( zoom ) ;
2101- view . setCenter ( [ center . x , center . y ] ) ;
2102- // Zoom needs to be set before changing center
2103- if ( ! view . animate === undefined ) {
2104- view . animate ( { zoom, duration } ) ;
2112+ if ( transitionType === 'immediate' ) {
2113+ view . setCenter ( targetCenter ) ;
2114+ view . setZoom ( zoom ) ;
2115+ return ;
2116+ }
2117+
2118+ if ( transitionType === 'smooth' ) {
2119+ // Smooth: zoom out, center, and zoom in
2120+ // Centering takes full duration, zoom out completes halfway, zoom in starts halfway
2121+ const zoomOutLevel = Math . min ( currentZoom , zoom ) - 1 ;
2122+
2123+ // Start centering (full duration) and zoom out (50% duration) simultaneously
2124+ view . animate ( {
2125+ center : targetCenter ,
2126+ duration : duration ,
2127+ } ) ;
2128+ // Chain zoom out -> zoom in (zoom in starts when zoom out completes)
2129+ view . animate (
2130+ {
2131+ zoom : zoomOutLevel ,
2132+ duration : duration * 0.5 ,
2133+ } ,
2134+ {
2135+ zoom : zoom ,
2136+ duration : duration * 0.5 ,
2137+ } ,
2138+ ) ;
2139+ } else {
2140+ // Linear: direct zoom
21052141 view . animate ( {
2106- center : [ center . x , center . y ] ,
2142+ center : targetCenter ,
2143+ zoom : zoom ,
21072144 duration,
21082145 } ) ;
21092146 }
21102147 }
21112148
2112- private _flyToPosition (
2113- center : { x : number ; y : number } ,
2114- zoom : number ,
2115- duration = 1000 ,
2116- ) {
2117- const view = this . _Map . getView ( ) ;
2118- view . animate ( { zoom, duration } ) ;
2119- view . animate ( { center : [ center . x , center . y ] , duration } ) ;
2120- }
2121-
21222149 private _onPointerMove ( e : MouseEvent ) {
21232150 const pixel = this . _Map . getEventPixel ( e ) ;
21242151 const coordinates = this . _Map . getCoordinateFromPixel ( pixel ) ;
0 commit comments