|
- <template>
- <nav class="[&>*]:m-3 text-black w-48">
- <div class="flex flex-col ui">
- <div>
- <button @click="drawRect">draw rect</button>
- {{ selectedBounds }}
- </div>
- <button @click="goHome">home</button>
- <button @click="addPoint">Add Point</button>
- <button @click="addRectangle">Add Rectangle</button>
- <div class="flex flex-col my-6">
- <button
- v-for="marker in sortedMarkers"
- :key="marker.order"
- @click="selectMarker(marker)"
- :class="marker.id === selectedMarker.id ? 'text-green-500 text-bold' : ''"
- >
- {{ marker.name }}
- </button>
- </div>
- <div v-if="isAMarkerSelected">
- <input
- ref="textInputMarkerName"
- type="text"
- v-model="selectedMarker.name"
- class="w-40 my-4"
- />
- <textarea v-model="selectedMarker.annotation" class="w-40 h-40"></textarea>
- <button @click="saveMarker">Save</button>
- <button @click="unselectMarker">Cancel</button>
- </div>
- </div>
- </nav>
- </template>
-
- <script setup lang="ts">
- import type { Marker } from '@/types/virages'
- import { onMounted, ref, inject, nextTick, type Ref, computed } from 'vue'
- import OpenSeadragon from 'openseadragon'
-
- const props = defineProps<{ markers: Marker[] }>()
- const Viewer = inject<Ref<OpenSeadragon.Viewer>>('Viewer')
- const isAddingPoint = ref<boolean>(false)
- const isAddingRectangle = ref<boolean>(false)
- const selectedMarker = ref<Marker>({} as Marker)
- const textInputMarkerName = ref<HTMLInputElement | null>(null)
-
- const selectedBounds = ref(null)
-
- const isAMarkerSelected = computed(() => {
- return selectedMarker.value.id !== undefined
- })
- const sortedMarkers = computed(() => {
- return [...props.markers].sort((a, b) => a.order - b.order)
- })
-
- const drawRect = () => {}
-
- function convertZoomPointToBounds(zoom, pointX, pointY) {
- const canvasWidth = Viewer?.value.canvas.offsetWidth
- const canvasHeight = Viewer?.value.canvas.offsetHeight
-
- const bounds = {
- x: (pointX - canvasWidth / 2) / (canvasWidth / 2) / zoom,
- y: (pointY - canvasHeight / 2) / (canvasHeight / 2) / zoom,
- width: canvasWidth / zoom,
- height: canvasHeight / zoom,
- }
-
- return bounds
- }
-
- const goHome = () => {
- Viewer?.value.viewport.goHome(false)
- }
-
- const addPoint = () => {
- unselectMarker()
- isAddingPoint.value = true
- document.querySelector('.openseadragon-canvas')?.classList.add('cursor-crosshair')
- }
-
- const addRectangle = () => {
- unselectMarker()
- isAddingRectangle.value = true
- document.querySelector('.openseadragon-canvas')?.classList.add('cursor-crosshair')
- }
-
- const saveMarker = () => {
- unselectMarker()
- }
-
- const selectMarker = (marker: Marker) => {
- document.querySelectorAll('.marker-selected').forEach((el) => {
- el.classList.remove('marker-selected')
- })
- const theMarker = document.querySelector('.marker-id-' + marker.id)
- theMarker?.classList.add('marker-selected')
- // Viewer?.value.viewport.fitBoundsWithConstraints(new OpenSeadragon.Rect(marker.bounds), false)
- Viewer?.value.viewport.zoomTo(marker.zoom, undefined, false)
- Viewer?.value.viewport.panTo(new OpenSeadragon.Point(marker.point.x, marker.point.y))
- console.log('marker', marker)
- selectedMarker.value = marker
- nextTick(() => {
- textInputMarkerName.value?.focus()
- })
- }
-
- const unselectMarker = () => {
- selectedMarker.value = {} as Marker
- document.querySelector('.marker-selected')?.classList.remove('marker-selected')
- }
-
- const injectMarker = (marker: Marker) => {
- const overlay = document.createElement('button')
- overlay.className = `marker-id-${marker.id} marker`
- overlay.title = marker.name
- overlay.onfocus = function () {
- selectMarker(marker)
- }
- overlay.addEventListener('click', function () {
- selectMarker(marker)
- })
- overlay.addEventListener('mouseover', function () {
- Viewer?.value.setMouseNavEnabled(false)
- // Viewer.value.gestureSettingsMouse.dragToPan = false
- })
- overlay.addEventListener('mouseout', function () {
- Viewer?.value.setMouseNavEnabled(true)
- })
-
- // Injection de l'overlay
- Viewer?.value.addOverlay({
- element: overlay,
- location: new OpenSeadragon.Point(marker.point.x, marker.point.y),
- })
- }
-
- const createMarkerOnClick = () => {
- Viewer?.value.addHandler('canvas-click', (event) => {
- // adding point
- if (isAddingPoint.value) {
- console.log('adding point')
- const point = Viewer?.value.viewport.pointFromPixel(event.position)
- const zoom = Viewer?.value.viewport.getZoom()
- const bounds = Viewer?.value.viewport.getBounds()
- console.log('bounds', bounds)
- console.log('zoom', zoom)
- console.log('point', point)
- const newMarker: Marker = {
- id: props.markers.length,
- order: props.markers.length,
- name: '',
- bounds: bounds,
- point: new OpenSeadragon.Point(point.x, point.y),
- zoom: zoom,
- annotation: '',
- }
- injectMarker(newMarker)
- isAddingPoint.value = false
- document.querySelector('.openseadragon-canvas')?.classList.remove('cursor-crosshair')
- selectMarker(newMarker)
- } else if (isAddingRectangle.value) {
- const point = Viewer?.value.viewport.pointFromPixel(event.position)
- const zoom = Viewer?.value.viewport.getZoom()
- const bounds = Viewer?.value.viewport.getBounds()
- } else if (isAMarkerSelected.value) {
- unselectMarker()
- }
- })
- }
-
- const editMarkerOnDragOrZoom = () => {
- Viewer?.value.addHandler('canvas-drag', (event) => {
- if (isAMarkerSelected.value) {
- Viewer.value.gestureSettingsMouse.dragToPan = false
- const point = Viewer?.value.viewport.pointFromPixel(event.position)
- selectedMarker.value.point = point
- selectedMarker.value.bounds = Viewer?.value.viewport.getBounds()
- const overlay = document.querySelector('.marker-id-' + selectedMarker.value.id)
- Viewer?.value.updateOverlay(overlay as Element, point)
- }
- })
-
- Viewer?.value.addHandler('canvas-release', () => {
- Viewer.value.gestureSettingsMouse.dragToPan = true
- })
-
- Viewer?.value.addHandler('zoom', (event) => {
- if (isAMarkerSelected.value) {
- selectedMarker.value.zoom = event.zoom
- selectedMarker.value.bounds = Viewer?.value.viewport.getBounds()
- }
- })
- }
-
- const loadStory = (markers: Marker[]) => {
- markers.forEach((marker) => {
- injectMarker(marker)
- })
- }
-
- const keyboardShortcut = () => {
- // if press on escpae key
- window.addEventListener('keydown', (e) => {
- if (e.key !== 'Escape') return
- unselectMarker()
- goHome()
- })
- }
-
- const initScalebar = () => {
- Viewer.value.scalebar({
- type: OpenSeadragon.ScalebarType.MAP,
- pixelsPerMeter: 37792223.52,
- minWidth: '74px',
- location: OpenSeadragon.ScalebarLocation.BOTTOM_RIGHT,
- color: 'black',
- fontColor: 'black',
- backgroundColor: 'rgba(255, 255, 255, 0.5)',
- barThickness: 1,
- stayInsideImage: false,
- xOffset: 20,
- yOffset: 20,
- })
- }
-
- const initUrl = () => {
- Viewer.value.bookmarkUrl()
- }
-
- onMounted(() => {
- nextTick(() => {
- Viewer?.value.clearOverlays()
- // Viewer.value.viewport.defaultZoomLevel = 1
- loadStory(props.markers)
- createMarkerOnClick()
- editMarkerOnDragOrZoom()
- keyboardShortcut()
- initScalebar()
- initUrl()
- })
- })
- </script>
-
- <style>
- .ui button {
- @apply border-2 border-black px-4 py-2 rounded-xl bg-slate-300 hover:bg-neutral-100 m-1;
- }
- .ui input,
- .ui textarea {
- @apply border-2 border-black px-4 py-2;
- }
- .marker {
- @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;
- }
- .marker-selected {
- @apply border-blue-400 border-8 bg-white animate-pulse hover:cursor-move hover:bg-white;
- }
- </style>
|