75 lines
2.0 KiB
Vue
75 lines
2.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
|
import { resolveImageSource } from '../utils/resolve-image'
|
|
|
|
type ImagePlacement = 'auto' | 'left' | 'right' | 'top' | 'bottom'
|
|
|
|
const props = withDefaults(defineProps<{
|
|
image?: string
|
|
imageFit?: string
|
|
imagePosition?: string
|
|
imagePlacement?: ImagePlacement
|
|
}>(), {
|
|
imageFit: 'contain',
|
|
imagePosition: 'center',
|
|
imagePlacement: 'auto',
|
|
})
|
|
|
|
const imagePane = ref<HTMLElement>()
|
|
const detectedPlacement = ref<Exclude<ImagePlacement, 'auto'>>('right')
|
|
let observedImage: HTMLImageElement | undefined
|
|
let observer: MutationObserver | undefined
|
|
|
|
const placement = computed(() => props.imagePlacement === 'auto'
|
|
? detectedPlacement.value
|
|
: props.imagePlacement)
|
|
|
|
function detectPlacement() {
|
|
const image = imagePane.value?.querySelector<HTMLImageElement>('img')
|
|
if (!image)
|
|
return
|
|
|
|
if (observedImage !== image) {
|
|
observedImage?.removeEventListener('load', detectPlacement)
|
|
observedImage = image
|
|
observedImage.addEventListener('load', detectPlacement)
|
|
}
|
|
|
|
if (image.naturalWidth > 0 && image.naturalHeight > 0)
|
|
detectedPlacement.value = image.naturalWidth / image.naturalHeight >= 1.6 ? 'bottom' : 'right'
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await nextTick()
|
|
detectPlacement()
|
|
if (imagePane.value) {
|
|
observer = new MutationObserver(detectPlacement)
|
|
observer.observe(imagePane.value, { childList: true, subtree: true })
|
|
}
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
observedImage?.removeEventListener('load', detectPlacement)
|
|
observer?.disconnect()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="slidev-layout easy-layout-image-split easy-layout-image-auto"
|
|
:class="`easy-image-${placement}`"
|
|
>
|
|
<div class="easy-image-content"><slot /></div>
|
|
<div ref="imagePane" class="easy-image-pane">
|
|
<slot name="image">
|
|
<img
|
|
v-if="image"
|
|
:src="resolveImageSource(image)"
|
|
alt=""
|
|
:style="{ objectFit: imageFit, objectPosition: imagePosition }"
|
|
>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</template>
|