2024-06-10 03:22:12 -04:00
|
|
|
<!--
|
|
|
|
|
@component
|
|
|
|
|
A component to help render images in a smooth and efficient way. The url passed will be fetched via Lazuli's
|
|
|
|
|
remoteImage API endpoint with size parameters that are dynamically calculated base off of the image's container's
|
|
|
|
|
width and height. Images are lazily loaded unless 'eager' loading is specified.
|
|
|
|
|
|
|
|
|
|
@param thumbnailUrl A string of a URL that points to the desired image.
|
|
|
|
|
@param alt Supplementary text in the event the image fails to load.
|
|
|
|
|
@param loadingMethod Optional. Either the string 'lazy' or 'eager', defaults to lazy. The method by which to load the image.
|
2024-06-11 03:55:34 -04:00
|
|
|
@param objectFit One of the following fill, contain, cover, none, scale-down. Specifies the object-fit styling of the image
|
|
|
|
|
@param objectPosistion Optional. Specifies the object-position styling of the image. Defaults to 'center'
|
2024-06-10 03:22:12 -04:00
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import { onMount } from 'svelte'
|
|
|
|
|
|
|
|
|
|
export let thumbnailUrl: string
|
|
|
|
|
export let alt: string
|
|
|
|
|
export let loadingMethod: 'lazy' | 'eager' = 'lazy'
|
2024-06-11 03:55:34 -04:00
|
|
|
export let objectFit: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down'
|
|
|
|
|
export let objectPosition: string = 'center'
|
2024-06-10 03:22:12 -04:00
|
|
|
|
|
|
|
|
let imageContainer: HTMLDivElement
|
|
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
// TODO: Implement auto-resizing
|
2024-06-10 03:22:12 -04:00
|
|
|
function updateImage(newThumbnailURL: string) {
|
|
|
|
|
if (!imageContainer) return
|
|
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
const width = imageContainer.clientWidth * 1.5 // 1.5x is a good compromise between sharpness and performance
|
|
|
|
|
const height = imageContainer.clientHeight * 1.5
|
2024-06-10 03:22:12 -04:00
|
|
|
|
|
|
|
|
const newImage = new Image(width, height)
|
|
|
|
|
imageContainer.appendChild(newImage)
|
|
|
|
|
|
|
|
|
|
newImage.loading = loadingMethod
|
|
|
|
|
newImage.src = `/api/remoteImage?url=${newThumbnailURL}&`.concat(width > height ? `maxWidth=${width}` : `maxHeight=${height}`)
|
|
|
|
|
newImage.alt = alt
|
|
|
|
|
|
|
|
|
|
newImage.style.width = '100%'
|
|
|
|
|
newImage.style.height = '100%'
|
2024-06-11 03:55:34 -04:00
|
|
|
newImage.style.objectFit = objectFit
|
|
|
|
|
newImage.style.objectPosition = objectPosition
|
2024-06-10 03:22:12 -04:00
|
|
|
newImage.style.opacity = '0'
|
|
|
|
|
newImage.style.position = 'absolute'
|
|
|
|
|
|
2024-06-11 03:55:34 -04:00
|
|
|
function removeOldImage() {
|
|
|
|
|
if (imageContainer.childElementCount > 1) {
|
|
|
|
|
const oldImage = imageContainer.firstChild! as HTMLImageElement
|
|
|
|
|
oldImage.style.opacity = '0'
|
|
|
|
|
setTimeout(() => imageContainer.removeChild(oldImage), 500)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-10 03:22:12 -04:00
|
|
|
newImage.onload = () => {
|
|
|
|
|
removeOldImage()
|
|
|
|
|
newImage.style.transition = 'opacity 500ms ease'
|
|
|
|
|
newImage.style.opacity = '1'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newImage.onerror = () => {
|
2024-06-21 03:35:00 -04:00
|
|
|
console.error(`Image from url: ${newThumbnailURL} failed to update`)
|
|
|
|
|
imageContainer.removeChild(newImage)
|
2024-06-10 03:22:12 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMount(() => updateImage(thumbnailUrl))
|
|
|
|
|
$: updateImage(thumbnailUrl)
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div bind:this={imageContainer} class="relative h-full w-full"></div>
|