|
|
@@ -1,260 +0,0 @@ |
|
|
|
<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> |