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.
 
 
 
 
 
 

192 lines
5.7 KiB

  1. <template>
  2. <nav class="[&>*]:m-3 text-black w-48">
  3. <div class="flex flex-col ui">
  4. <button @click="goHome">home</button>
  5. <button @click="addMarker">Add Marker</button>
  6. <div class="flex flex-col my-6">
  7. <button
  8. v-for="marker in sortedMarkers"
  9. :key="marker.order"
  10. @click="selectMarker(marker)"
  11. :class="marker.id === selectedMarker.id ? 'text-green-500 text-bold' : ''"
  12. >
  13. {{ marker.name }}
  14. </button>
  15. </div>
  16. <div v-if="isAMarkerSelected">
  17. <input
  18. ref="textInputMarkerName"
  19. type="text"
  20. v-model="selectedMarker.name"
  21. class="w-40 my-4"
  22. />
  23. <textarea v-model="selectedMarker.annotation" class="w-40 h-40"></textarea>
  24. <button @click="saveMarker">Save</button>
  25. <button @click="unselectMarker">Cancel</button>
  26. </div>
  27. </div>
  28. </nav>
  29. </template>
  30. <script setup lang="ts">
  31. import type { Marker } from '@/types/virages'
  32. import { onMounted, ref, inject, nextTick, type Ref, computed } from 'vue'
  33. import OpenSeadragon from 'openseadragon'
  34. const props = defineProps<{ markers: Marker[] }>()
  35. const Viewer = inject<Ref<OpenSeadragon.Viewer>>('Viewer')
  36. const isAddingMarker = ref<boolean>(false)
  37. const selectedMarker = ref<Marker>({} as Marker)
  38. const textInputMarkerName = ref<HTMLInputElement | null>(null)
  39. const isAMarkerSelected = computed(() => {
  40. return selectedMarker.value.id !== undefined
  41. })
  42. const sortedMarkers = computed(() => {
  43. return [...props.markers].sort((a, b) => a.order - b.order)
  44. })
  45. const goHome = () => {
  46. Viewer?.value.viewport.goHome(false)
  47. }
  48. const addMarker = () => {
  49. unselectMarker()
  50. isAddingMarker.value = true
  51. document.querySelector('.openseadragon-canvas')?.classList.add('cursor-crosshair')
  52. }
  53. const saveMarker = () => {
  54. unselectMarker()
  55. }
  56. const selectMarker = (marker: Marker) => {
  57. document.querySelectorAll('.marker-selected').forEach((el) => {
  58. el.classList.remove('marker-selected')
  59. })
  60. const theMarker = document.querySelector('.marker-id-' + marker.id)
  61. theMarker?.classList.add('marker-selected')
  62. Viewer?.value.viewport.zoomTo(marker.zoom, undefined, false)
  63. Viewer?.value.viewport.panTo(new OpenSeadragon.Point(marker.position.x, marker.position.y))
  64. selectedMarker.value = marker
  65. nextTick(() => {
  66. textInputMarkerName.value?.focus()
  67. })
  68. }
  69. const unselectMarker = () => {
  70. selectedMarker.value = {} as Marker
  71. document.querySelector('.marker-selected')?.classList.remove('marker-selected')
  72. }
  73. const injectMarker = (marker: Marker) => {
  74. const overlay = document.createElement('button')
  75. overlay.className = `marker-id-${marker.id} marker`
  76. overlay.title = marker.name
  77. overlay.onfocus = function () {
  78. selectMarker(marker)
  79. }
  80. overlay.addEventListener('click', function () {
  81. selectMarker(marker)
  82. })
  83. overlay.addEventListener('mouseover', function () {
  84. Viewer?.value.setMouseNavEnabled(false)
  85. // Viewer.value.gestureSettingsMouse.dragToPan = false
  86. })
  87. overlay.addEventListener('mouseout', function () {
  88. Viewer?.value.setMouseNavEnabled(true)
  89. })
  90. // Injection de l'overlay
  91. Viewer?.value.addOverlay({
  92. element: overlay,
  93. location: new OpenSeadragon.Point(marker.position.x, marker.position.y),
  94. })
  95. }
  96. const createMarkerOnClick = () => {
  97. Viewer?.value.addHandler('canvas-click', (e) => {
  98. if (isAddingMarker.value) {
  99. const point = Viewer?.value.viewport.pointFromPixel(e.position)
  100. const zoom = Viewer?.value.viewport.getZoom()
  101. const newMarker: Marker = {
  102. id: props.markers.length, // WIP
  103. order: props.markers.length, // WIP
  104. name: '',
  105. annotation: '',
  106. position: new OpenSeadragon.Point(point.x, point.y),
  107. zoom: zoom,
  108. }
  109. injectMarker(newMarker)
  110. isAddingMarker.value = false
  111. document.querySelector('.openseadragon-canvas').classList.remove('cursor-crosshair')
  112. selectMarker(newMarker)
  113. } else if (isAMarkerSelected.value) {
  114. unselectMarker()
  115. }
  116. })
  117. }
  118. const editMarkerOnDragOrZoom = () => {
  119. Viewer?.value.addHandler('canvas-drag', (event) => {
  120. if (isAMarkerSelected.value) {
  121. Viewer.value.gestureSettingsMouse.dragToPan = false
  122. const point = Viewer?.value.viewport.pointFromPixel(event.position)
  123. selectedMarker.value.position = point
  124. const overlay = document.querySelector('.marker-id-' + selectedMarker.value.id)
  125. Viewer?.value.updateOverlay(overlay as Element, point)
  126. }
  127. })
  128. Viewer?.value.addHandler('canvas-release', () => {
  129. Viewer.value.gestureSettingsMouse.dragToPan = true
  130. })
  131. Viewer?.value.addHandler('zoom', (event) => {
  132. if (isAMarkerSelected.value) {
  133. selectedMarker.value.zoom = event.zoom
  134. }
  135. })
  136. }
  137. const loadStory = (markers: Marker[]) => {
  138. markers.forEach((marker) => {
  139. injectMarker(marker)
  140. })
  141. }
  142. const keyboardShortcut = () => {
  143. // if press on escpae key
  144. window.addEventListener('keydown', (e) => {
  145. if (e.key !== 'Escape') return
  146. unselectMarker()
  147. goHome()
  148. })
  149. }
  150. onMounted(() => {
  151. nextTick(() => {
  152. loadStory(props.markers)
  153. createMarkerOnClick()
  154. editMarkerOnDragOrZoom()
  155. keyboardShortcut()
  156. })
  157. })
  158. </script>
  159. <style>
  160. .ui button {
  161. @apply border-2 border-black px-4 py-2 rounded-xl bg-slate-300 hover:bg-neutral-100 m-1;
  162. }
  163. .ui input,
  164. .ui textarea {
  165. @apply border-2 border-black px-4 py-2;
  166. }
  167. .marker {
  168. @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;
  169. }
  170. .marker-selected {
  171. @apply border-blue-400 border-8 bg-white animate-pulse hover:cursor-move hover:bg-white;
  172. }
  173. </style>