You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

261 lines
7.7 KiB

  1. <template>
  2. <nav class="[&>*]:m-3 text-black w-48">
  3. <div class="flex flex-col ui">
  4. <div>
  5. <button @click="drawRect">draw rect</button>
  6. {{ selectedBounds }}
  7. </div>
  8. <button @click="goHome">home</button>
  9. <button @click="addPoint">Add Point</button>
  10. <button @click="addRectangle">Add Rectangle</button>
  11. <div class="flex flex-col my-6">
  12. <button
  13. v-for="marker in sortedMarkers"
  14. :key="marker.order"
  15. @click="selectMarker(marker)"
  16. :class="marker.id === selectedMarker.id ? 'text-green-500 text-bold' : ''"
  17. >
  18. {{ marker.name }}
  19. </button>
  20. </div>
  21. <div v-if="isAMarkerSelected">
  22. <input
  23. ref="textInputMarkerName"
  24. type="text"
  25. v-model="selectedMarker.name"
  26. class="w-40 my-4"
  27. />
  28. <textarea v-model="selectedMarker.annotation" class="w-40 h-40"></textarea>
  29. <button @click="saveMarker">Save</button>
  30. <button @click="unselectMarker">Cancel</button>
  31. </div>
  32. </div>
  33. </nav>
  34. </template>
  35. <script setup lang="ts">
  36. import type { Marker } from '@/types/virages'
  37. import { onMounted, ref, inject, nextTick, type Ref, computed } from 'vue'
  38. import OpenSeadragon from 'openseadragon'
  39. const props = defineProps<{ markers: Marker[] }>()
  40. const Viewer = inject<Ref<OpenSeadragon.Viewer>>('Viewer')
  41. const isAddingPoint = ref<boolean>(false)
  42. const isAddingRectangle = ref<boolean>(false)
  43. const selectedMarker = ref<Marker>({} as Marker)
  44. const textInputMarkerName = ref<HTMLInputElement | null>(null)
  45. const selectedBounds = ref(null)
  46. const isAMarkerSelected = computed(() => {
  47. return selectedMarker.value.id !== undefined
  48. })
  49. const sortedMarkers = computed(() => {
  50. return [...props.markers].sort((a, b) => a.order - b.order)
  51. })
  52. const drawRect = () => {}
  53. function convertZoomPointToBounds(zoom, pointX, pointY) {
  54. const canvasWidth = Viewer?.value.canvas.offsetWidth
  55. const canvasHeight = Viewer?.value.canvas.offsetHeight
  56. const bounds = {
  57. x: (pointX - canvasWidth / 2) / (canvasWidth / 2) / zoom,
  58. y: (pointY - canvasHeight / 2) / (canvasHeight / 2) / zoom,
  59. width: canvasWidth / zoom,
  60. height: canvasHeight / zoom,
  61. }
  62. return bounds
  63. }
  64. const goHome = () => {
  65. Viewer?.value.viewport.goHome(false)
  66. }
  67. const addPoint = () => {
  68. unselectMarker()
  69. isAddingPoint.value = true
  70. document.querySelector('.openseadragon-canvas')?.classList.add('cursor-crosshair')
  71. }
  72. const addRectangle = () => {
  73. unselectMarker()
  74. isAddingRectangle.value = true
  75. document.querySelector('.openseadragon-canvas')?.classList.add('cursor-crosshair')
  76. }
  77. const saveMarker = () => {
  78. unselectMarker()
  79. }
  80. const selectMarker = (marker: Marker) => {
  81. document.querySelectorAll('.marker-selected').forEach((el) => {
  82. el.classList.remove('marker-selected')
  83. })
  84. const theMarker = document.querySelector('.marker-id-' + marker.id)
  85. theMarker?.classList.add('marker-selected')
  86. // Viewer?.value.viewport.fitBoundsWithConstraints(new OpenSeadragon.Rect(marker.bounds), false)
  87. Viewer?.value.viewport.zoomTo(marker.zoom, undefined, false)
  88. Viewer?.value.viewport.panTo(new OpenSeadragon.Point(marker.point.x, marker.point.y))
  89. console.log('marker', marker)
  90. selectedMarker.value = marker
  91. nextTick(() => {
  92. textInputMarkerName.value?.focus()
  93. })
  94. }
  95. const unselectMarker = () => {
  96. selectedMarker.value = {} as Marker
  97. document.querySelector('.marker-selected')?.classList.remove('marker-selected')
  98. }
  99. const injectMarker = (marker: Marker) => {
  100. const overlay = document.createElement('button')
  101. overlay.className = `marker-id-${marker.id} marker`
  102. overlay.title = marker.name
  103. overlay.onfocus = function () {
  104. selectMarker(marker)
  105. }
  106. overlay.addEventListener('click', function () {
  107. selectMarker(marker)
  108. })
  109. overlay.addEventListener('mouseover', function () {
  110. Viewer?.value.setMouseNavEnabled(false)
  111. // Viewer.value.gestureSettingsMouse.dragToPan = false
  112. })
  113. overlay.addEventListener('mouseout', function () {
  114. Viewer?.value.setMouseNavEnabled(true)
  115. })
  116. // Injection de l'overlay
  117. Viewer?.value.addOverlay({
  118. element: overlay,
  119. location: new OpenSeadragon.Point(marker.point.x, marker.point.y),
  120. })
  121. }
  122. const createMarkerOnClick = () => {
  123. Viewer?.value.addHandler('canvas-click', (event) => {
  124. // adding point
  125. if (isAddingPoint.value) {
  126. console.log('adding point')
  127. const point = Viewer?.value.viewport.pointFromPixel(event.position)
  128. const zoom = Viewer?.value.viewport.getZoom()
  129. const bounds = Viewer?.value.viewport.getBounds()
  130. console.log('bounds', bounds)
  131. console.log('zoom', zoom)
  132. console.log('point', point)
  133. const newMarker: Marker = {
  134. id: props.markers.length,
  135. order: props.markers.length,
  136. name: '',
  137. bounds: bounds,
  138. point: new OpenSeadragon.Point(point.x, point.y),
  139. zoom: zoom,
  140. annotation: '',
  141. }
  142. injectMarker(newMarker)
  143. isAddingPoint.value = false
  144. document.querySelector('.openseadragon-canvas')?.classList.remove('cursor-crosshair')
  145. selectMarker(newMarker)
  146. } else if (isAddingRectangle.value) {
  147. const point = Viewer?.value.viewport.pointFromPixel(event.position)
  148. const zoom = Viewer?.value.viewport.getZoom()
  149. const bounds = Viewer?.value.viewport.getBounds()
  150. } else if (isAMarkerSelected.value) {
  151. unselectMarker()
  152. }
  153. })
  154. }
  155. const editMarkerOnDragOrZoom = () => {
  156. Viewer?.value.addHandler('canvas-drag', (event) => {
  157. if (isAMarkerSelected.value) {
  158. Viewer.value.gestureSettingsMouse.dragToPan = false
  159. const point = Viewer?.value.viewport.pointFromPixel(event.position)
  160. selectedMarker.value.point = point
  161. selectedMarker.value.bounds = Viewer?.value.viewport.getBounds()
  162. const overlay = document.querySelector('.marker-id-' + selectedMarker.value.id)
  163. Viewer?.value.updateOverlay(overlay as Element, point)
  164. }
  165. })
  166. Viewer?.value.addHandler('canvas-release', () => {
  167. Viewer.value.gestureSettingsMouse.dragToPan = true
  168. })
  169. Viewer?.value.addHandler('zoom', (event) => {
  170. if (isAMarkerSelected.value) {
  171. selectedMarker.value.zoom = event.zoom
  172. selectedMarker.value.bounds = Viewer?.value.viewport.getBounds()
  173. }
  174. })
  175. }
  176. const loadStory = (markers: Marker[]) => {
  177. markers.forEach((marker) => {
  178. injectMarker(marker)
  179. })
  180. }
  181. const keyboardShortcut = () => {
  182. // if press on escpae key
  183. window.addEventListener('keydown', (e) => {
  184. if (e.key !== 'Escape') return
  185. unselectMarker()
  186. goHome()
  187. })
  188. }
  189. const initScalebar = () => {
  190. Viewer.value.scalebar({
  191. type: OpenSeadragon.ScalebarType.MAP,
  192. pixelsPerMeter: 37792223.52,
  193. minWidth: '74px',
  194. location: OpenSeadragon.ScalebarLocation.BOTTOM_RIGHT,
  195. color: 'black',
  196. fontColor: 'black',
  197. backgroundColor: 'rgba(255, 255, 255, 0.5)',
  198. barThickness: 1,
  199. stayInsideImage: false,
  200. xOffset: 20,
  201. yOffset: 20,
  202. })
  203. }
  204. const initUrl = () => {
  205. Viewer.value.bookmarkUrl()
  206. }
  207. onMounted(() => {
  208. nextTick(() => {
  209. Viewer?.value.clearOverlays()
  210. // Viewer.value.viewport.defaultZoomLevel = 1
  211. loadStory(props.markers)
  212. createMarkerOnClick()
  213. editMarkerOnDragOrZoom()
  214. keyboardShortcut()
  215. initScalebar()
  216. initUrl()
  217. })
  218. })
  219. </script>
  220. <style>
  221. .ui button {
  222. @apply border-2 border-black px-4 py-2 rounded-xl bg-slate-300 hover:bg-neutral-100 m-1;
  223. }
  224. .ui input,
  225. .ui textarea {
  226. @apply border-2 border-black px-4 py-2;
  227. }
  228. .marker {
  229. @apply w-8 h-8 rounded-full shadow-2xl bg-green-500 bg-opacity-50 hover:bg-green-600 hover:bg-opacity-100 hover:cursor-pointer pointer-events-auto hover:scale-150 transition-all border-2 border-white border-8 transform -translate-y-1/2 -translate-x-1/2;
  230. }
  231. .marker-selected {
  232. @apply border-blue-400 border-8 bg-white animate-pulse hover:cursor-move hover:bg-white;
  233. }
  234. </style>