Files
speckle-server/packages/frontend-2/components/preview/Image.vue
T
Kristaps Fabians Geikins b02a07e2b6 feat: Frontend 2.0 MVP
2023-05-08 10:47:01 +03:00

122 lines
3.7 KiB
Vue

<template>
<!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->
<div
ref="parent"
class="relative w-full h-full"
@mouseenter="hovered = true"
@mouseleave="hovered = false"
>
<ClientOnly>
<Transition
enter-from-class="opacity-0"
enter-active-class="transition duration-300"
leave-to-class="opacity-0"
leave-active-class="transition duration-300"
>
<div
v-show="
(!hovered && previewUrl) || isLoadingPanorama || !props.panoramaOnHover
"
class="w-full h-full bg-contain bg-no-repeat bg-center transition"
:style="{
backgroundImage: `url('${previewUrl}')`
}"
/>
</Transition>
<Transition
enter-from-class="opacity-0"
enter-active-class="transition duration-300"
leave-to-class="opacity-0"
leave-active-class="transition duration-300"
>
<!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->
<div
v-show="hovered && panoramaPreviewUrl && props.panoramaOnHover"
ref="panorama"
:style="{
backgroundImage: `url('${panoramaPreviewUrl}')`,
backgroundSize: 'cover',
backgroundPosition: `${positionMagic}px 0`,
position: 'absolute',
top: '0',
width: '100%',
height: '100%'
}"
@mousemove="(e: MouseEvent) => calculatePanoramaStyle(e)"
@touchmove="(e:TouchEvent) =>
calculatePanoramaStyle({
target: e.target,
clientX: e.touches[0].clientX,
clientY: e.touches[0].clientY
} as MouseEvent)"
/>
</Transition>
<Transition
enter-from-class="opacity-0"
leave-to-class="opacity-0"
enter-active-class="transition duration-300"
leave-active-class="transition duration-300"
>
<CommonLoadingBar
:loading="isLoadingPanorama && hovered"
class="absolute bottom-0 w-full"
/>
</Transition>
</ClientOnly>
</div>
</template>
<script setup lang="ts">
import { Nullable } from '@speckle/shared'
import { useResizeObserver } from '@vueuse/core'
import { usePreviewImageBlob } from '~~/lib/projects/composables/previewImage'
const props = withDefaults(
defineProps<{
previewUrl: string
panoramaOnHover?: boolean
}>(),
{
panoramaOnHover: true
}
)
const basePreviewUrl = computed(() => props.previewUrl)
const { previewUrl, panoramaPreviewUrl, shouldLoadPanorama, isLoadingPanorama } =
usePreviewImageBlob(basePreviewUrl)
const hovered = ref(false)
watch(hovered, (newVal) => {
if (newVal && !panoramaPreviewUrl.value && props.panoramaOnHover)
shouldLoadPanorama.value = true
})
const panorama = ref(null as Nullable<HTMLDivElement>)
const parent = ref(null as Nullable<HTMLDivElement>)
let parentWidth = 1
let parentHeight = 1
const setParentDimensions = () => {
parentWidth = parent.value?.getBoundingClientRect().width as number
parentHeight = parent.value?.getBoundingClientRect().height as number
}
onMounted(() => setParentDimensions())
if (process.client) useResizeObserver(document.body, () => setParentDimensions())
const positionMagic = ref(0)
const calculatePanoramaStyle = (e: MouseEvent) => {
const rect = panorama.value?.getBoundingClientRect()
if (!rect) return
const x = e.clientX - rect.left
const step = rect.width / 24
let index = Math.abs(24 - Math.round(x / step))
if (index >= 24) index = 24 - 1
const scaleFactor = parentHeight / 400
const actualWidth = scaleFactor * 700
const widthDiff = (parentWidth - actualWidth) * 0.5
positionMagic.value = -(actualWidth * (2 * index + 1) - widthDiff)
}
</script>