-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Is your feature request related to a problem? Please describe.
At the time of writing the image component uses the focus parameter only for passing it to the Storyblok API which works well for images with dimensions that are not relative to their container.
So imagine you have lets say a stage module which is width: 100vw; and height: 80vh; and the image should fill the whole area (similar to next/images layout="fill"):
<section className="stage" style={{ width: '100vw', height: '80vh' }}>
<Image {...imageProps} fluid={1920} width="100%" height="100%" />
{...children}
</section>Depending on the aspect ratio of the original image the Storyblok API tries to shift image to the provided focus as good as it can, but then the Images CSS applies object-position: center center which can lead to a scenario where the focus point is not even in the visible area of the component.
Describe the solution you'd like
What would you think about passing the focus coordinates down to the CSS to make sure the focus point/area is always visible?
Two things are to be considered:
- Storyblok treats
focusas area described by two pairs of coordinates whileobject-positionexpects a point which would make calculating the center of the focus area necessary (e.g. by<coordinateA> + <coordinateB> / 2) - Shifting the image using
object-positionby a percentage value can lead to moving it too far and create undesired whitespace so the amount of translation has to be clamped:const pictureStyles: CSSProperties = { /* ... */ objectFit: fit, objectPosition: `clamp(0%, ${focusX}, 100%) clamp(0%, ${focusY}, 100%)', };
Describe alternatives you've considered
For now we tried to patch the Image component from the outside to reflect the desired behavior, but this is not really a sustainable solution…
const getFocusPoint = (focus: string) =>
focus
.split(':')[0]
.split('x')
.map((p) => parseInt(p))
const getFocusPosition = (focus: string, src: string) => {
const [focusPointX, focusPointY] = getFocusPoint(focus)
const { width, height } = getImageProps(src)
const positionX = Math.floor(
Math.min(Math.max((focusPointX / width) * 100, 0), 100)
)
const positionY = Math.floor(
Math.min(Math.max((focusPointY / height) * 100, 0), 100)
)
return [`${positionX}%`, `${positionY}%`]
}
export const PatchedImage: React.FC<ImageProps> = ({ focus, src, ...restProps }) => {
const imageContainerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!focus || !src || !imageContainerRef?.current) {
return
}
const [positionX, positionY] = getFocusPosition(focus, src)
const images = imageContainerRef.current.querySelectorAll('img')
for (const image of images) {
image.style.objectPosition = `${positionX} ${positionY}`
}
}, [focus, src])
return (
<div ref={imageContainerRef} style={{ display: 'contents' }}>
<Image src={src} focus={focus} {...restProps} />
</div>
)
}Let me know what you think, happy to discuss the details or helping with the implementation :)
/cc @martinjuhasz