]> git.localhorst.tv Git - alttp.git/blob - resources/js/components/map/OpenSeadragon.js
improved underworld map navigation
[alttp.git] / resources / js / components / map / OpenSeadragon.js
1 import axios from 'axios';
2 import OpenSeadragon from 'openseadragon';
3 import PropTypes from 'prop-types';
4 import React from 'react';
5 import { useNavigate, useParams } from 'react-router';
6 import { createSearchParams, useSearchParams } from 'react-router-dom';
7
8 export const Context = React.createContext({});
9
10 export const useOpenSeadragon = () => React.useContext(Context);
11
12 export const Provider = React.forwardRef(({ children }, ref) => {
13         const { activeMap } = useParams();
14         const navigate = useNavigate();
15         const [searchParams, setSearchParams] = useSearchParams();
16         const [pins, setPins] = React.useState([]);
17         const [viewer, setViewer] = React.useState(null);
18
19         const storePosition = React.useCallback(() => {
20                 if (!viewer || !viewer.viewport) return;
21                 const center = viewer.viewport.getCenter();
22                 const zoom = viewer.viewport.getZoom();
23                 setSearchParams({ x: center.x, y: center.y, z: zoom }, { replace: true });
24         }, [setSearchParams, viewer]);
25
26         const setActiveMap = React.useCallback(map => {
27                 if (viewer && viewer.viewport) {
28                         const center = viewer.viewport.getCenter();
29                         const zoom = viewer.viewport.getZoom();
30                         const params = { x: center.x, y: center.y, z: zoom };
31                         navigate(
32                                 { pathname: `../${map}`, search: `?${createSearchParams(params)}` },
33                                 { replace: true },
34                         );
35                 } else {
36                         navigate(`../${map}`, { replace: true });
37                 }
38         }, [navigate, viewer]);
39
40         React.useEffect(() => {
41                 if (!viewer || !viewer.viewport) return;
42                 if (searchParams.has('x') && searchParams.has('y')) {
43                         viewer.viewport.panTo(new OpenSeadragon.Point(
44                                 parseFloat(searchParams.get('x')),
45                                 parseFloat(searchParams.get('y')),
46                         ));
47                 }
48                 if (searchParams.has('z')) {
49                         viewer.viewport.zoomTo(parseFloat(searchParams.get('z')));
50                 }
51         }, [searchParams, viewer]);
52
53         React.useEffect(() => {
54                 if (!ref.current) return;
55
56                 const v = OpenSeadragon({
57                         element: ref.current,
58                         preserveViewport: true,
59                         sequenceMode: true,
60                         showNavigator: true,
61                         showNavigationControl: false,
62                         showSequenceControl: false,
63                         tileSources: [
64                                 new OpenSeadragon.DziTileSource({
65                                         width: 8192,
66                                         height: 8192,
67                                         tileSize: 256,
68                                         tileOverlap: 0,
69                                         minLevel: 8,
70                                         maxLevel: 13,
71                                         tilesUrl: '/media/alttp/map/lw_files/',
72                                         fileFormat: 'png',
73                                 }), new OpenSeadragon.DziTileSource({
74                                         width: 8192,
75                                         height: 8192,
76                                         tileSize: 256,
77                                         tileOverlap: 0,
78                                         minLevel: 8,
79                                         maxLevel: 13,
80                                         tilesUrl: '/media/alttp/map/dw_files/',
81                                         fileFormat: 'png',
82                                 }), new OpenSeadragon.DziTileSource({
83                                         width: 8192,
84                                         height: 4096,
85                                         tileSize: 256,
86                                         tileOverlap: 0,
87                                         minLevel: 8,
88                                         maxLevel: 13,
89                                         tilesUrl: '/media/alttp/map/sp_files/',
90                                         fileFormat: 'png',
91                                 }), new OpenSeadragon.DziTileSource({
92                                         width: 16384,
93                                         height: 16384,
94                                         tileSize: 256,
95                                         tileOverlap: 0,
96                                         minLevel: 8,
97                                         maxLevel: 14,
98                                         tilesUrl: '/media/alttp/map/uw_files/',
99                                         fileFormat: 'png',
100                                 }), new OpenSeadragon.DziTileSource({
101                                         width: 16384,
102                                         height: 3072,
103                                         tileSize: 256,
104                                         tileOverlap: 0,
105                                         minLevel: 8,
106                                         maxLevel: 14,
107                                         tilesUrl: '/media/alttp/map/uw2_files/',
108                                         fileFormat: 'png',
109                                 }),
110                         ],
111                 });
112                 v.addHandler('canvas-nonprimary-press', e => {
113                         if (e.button === 3) {
114                                 navigate(-1);
115                         } else if (e.button === 4) {
116                                 navigate(1);
117                         }
118                 });
119                 setViewer(v);
120                 return () => {
121                         v.destroy();
122                 };
123         }, [ref.current]);
124
125         React.useEffect(() => {
126                 if (!viewer) return;
127                 switch (activeMap) {
128                         case 'lw':
129                                 viewer.goToPage(0);
130                                 break;
131                         case 'dw':
132                                 viewer.goToPage(1);
133                                 break;
134                         case 'sp':
135                                 viewer.goToPage(2);
136                                 break;
137                         case 'uw':
138                                 viewer.goToPage(3);
139                                 break;
140                         case 'uw2':
141                                 viewer.goToPage(4);
142                                 break;
143                 }
144                 const controller = new AbortController();
145                 axios.get(`/api/markers/${activeMap}`, {
146                         signal: controller.signal,
147                 }).then(response => {
148                         setPins(response.data || []);
149                 }).catch(e => {
150                         if (!axios.isCancel(e)) {
151                                 console.error(e);
152                         }
153                 });
154                 return () => {
155                         controller.abort();
156                 };
157         }, [activeMap, viewer]);
158
159         return <Context.Provider value={{ activeMap, pins, setActiveMap, storePosition, viewer }}>
160                 {children}
161         </Context.Provider>;
162 });
163
164 Provider.displayName = 'OpenSeadragonProvider';
165
166 Provider.propTypes = {
167         children: PropTypes.node,
168 };
169
170 export default Provider;