Skip to content

Commit bff26b7

Browse files
committed
Allow customization for transition animations
1 parent 0b5736c commit bff26b7

File tree

3 files changed

+69
-29
lines changed

3 files changed

+69
-29
lines changed

packages/base/src/mainview/mainView.tsx

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import Feature, { FeatureLike } from 'ol/Feature';
5454
import { FullScreen, ScaleLine } from 'ol/control';
5555
import { Coordinate } from 'ol/coordinate';
5656
import { singleClick } from 'ol/events/condition';
57+
import { getCenter } from 'ol/extent';
5758
import { GeoJSON, MVT } from 'ol/format';
5859
import { Geometry, Point } from 'ol/geom';
5960
import { 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);

packages/base/src/panelview/components/story-maps/StoryEditorPanel.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ export function StoryEditorPanel({ model, togglePreview }: IStoryPanelProps) {
1919
}, [model, model.sharedModel.storiesMap]);
2020

2121
const syncStoryData = (properties: IDict) => {
22-
const { title, storyType, landmarks } = properties;
23-
const updatedStory: IJGISStoryMap = { title, storyType, landmarks };
24-
25-
model.sharedModel.updateStoryMap(landmarkId, updatedStory);
22+
model.sharedModel.updateStoryMap(landmarkId, properties as IJGISStoryMap);
2623
};
2724

2825
if (!story) {

packages/schema/src/schema/project/jgis.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,22 @@
151151
"enum": ["guided", "unguided"],
152152
"description": "The type of story map"
153153
},
154+
"transition": {
155+
"type": "object",
156+
"description": "Transition configuration between landmarks",
157+
"properties": {
158+
"type": {
159+
"type": "string",
160+
"enum": ["linear", "immediate", "smooth"],
161+
"description": "Transition animation style"
162+
},
163+
"time": {
164+
"type": "number",
165+
"description": "The time in seconds for the transition"
166+
}
167+
},
168+
"required": ["type", "time"]
169+
},
154170
"landmarks": {
155171
"type": "array",
156172
"default": [],

0 commit comments

Comments
 (0)