160 lines
3.9 KiB
JavaScript
160 lines
3.9 KiB
JavaScript
// @ts-check
|
|
import crypto from "node:crypto";
|
|
import getImage from "./utils/getImage.js";
|
|
import getLinkElement from "./utils/getLinkElement.js";
|
|
import getStyleElement from "./utils/getStyleElement.js";
|
|
import getFilteredProps from "./utils/getFilteredProps.js";
|
|
import getContainerElement from "./utils/getContainerElement.js";
|
|
|
|
export default async function renderBackgroundImage(props) {
|
|
const type = "BackgroundImage";
|
|
|
|
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
|
|
|
const {
|
|
src,
|
|
tag,
|
|
content,
|
|
preload,
|
|
attributes,
|
|
placeholder,
|
|
breakpoints,
|
|
backgroundSize,
|
|
backgroundPosition,
|
|
format,
|
|
fallbackFormat,
|
|
includeSourceFormat,
|
|
formatOptions,
|
|
artDirectives,
|
|
} = filteredProps;
|
|
|
|
const {
|
|
link: linkAttributes = {},
|
|
style: styleAttributes = {},
|
|
container: containerAttributes = {},
|
|
} = attributes;
|
|
|
|
const sizes = "";
|
|
|
|
const { uuid, images } = await getImage({
|
|
src,
|
|
type,
|
|
sizes,
|
|
format,
|
|
breakpoints,
|
|
placeholder,
|
|
artDirectives,
|
|
fallbackFormat,
|
|
includeSourceFormat,
|
|
formatOptions,
|
|
transformConfigs,
|
|
});
|
|
|
|
const className = `astro-imagetools-background-image-${uuid}`;
|
|
|
|
const { imagesizes } = images[images.length - 1];
|
|
|
|
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
|
|
|
const backgroundImageStylesArray = images.map(({ media, sources }) => {
|
|
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
|
|
|
const fallbackUrlCustomVariable = `--astro-imagetools-background-image-fallback-url${uuid}`;
|
|
|
|
const newSources = {};
|
|
|
|
sources.forEach(({ src, format, srcset }) => {
|
|
const sources = srcset
|
|
.split(", ")
|
|
.map((source) => [
|
|
source.slice(0, source.lastIndexOf(" ")),
|
|
source.slice(source.lastIndexOf(" ") + 1, -1),
|
|
]);
|
|
|
|
sources.forEach(([path, width]) => {
|
|
if (!newSources[width]) {
|
|
newSources[width] = [];
|
|
}
|
|
|
|
newSources[width].push({ src, format, path });
|
|
});
|
|
});
|
|
|
|
const widths = Object.keys(newSources)
|
|
.map((width) => parseInt(width))
|
|
.reverse();
|
|
|
|
const maxWidth = Math.max(...widths);
|
|
|
|
const styles = widths
|
|
.map((width) => {
|
|
const sources = newSources[width];
|
|
|
|
const styles = sources
|
|
.map(
|
|
({ format, path }, i) =>
|
|
`
|
|
${i !== sources.length - 1 ? `.${format} ` : ""}.${className} {
|
|
background-repeat: no-repeat;
|
|
background-image: url(${path}),
|
|
var(${fallbackUrlCustomVariable});
|
|
background-size: ${backgroundSize};
|
|
background-position: ${backgroundPosition};
|
|
}
|
|
`
|
|
)
|
|
.reverse()
|
|
.join("");
|
|
|
|
return width === maxWidth
|
|
? styles
|
|
: `
|
|
@media screen and (max-width: ${width}px) {
|
|
${styles}
|
|
}
|
|
`;
|
|
})
|
|
.join("");
|
|
|
|
return {
|
|
fallbackUrlCustomVariable,
|
|
styles: media
|
|
? `
|
|
@media ${media} {
|
|
${styles}
|
|
}
|
|
`
|
|
: styles,
|
|
};
|
|
});
|
|
|
|
const containerStyles = `
|
|
.${className} {
|
|
position: relative;
|
|
${images
|
|
.map(({ fallback }, i) => {
|
|
const fallbackUrlCustomVariable =
|
|
backgroundImageStylesArray[i].fallbackUrlCustomVariable;
|
|
|
|
return `${fallbackUrlCustomVariable}: url("${encodeURI(fallback)}");`;
|
|
})
|
|
.join("\n")}
|
|
}
|
|
`;
|
|
|
|
const backgroundStyles =
|
|
backgroundImageStylesArray.map(({ styles }) => styles).join("\n") +
|
|
containerStyles;
|
|
|
|
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
|
|
|
const htmlElement = getContainerElement({
|
|
tag,
|
|
content,
|
|
className,
|
|
containerAttributes,
|
|
});
|
|
|
|
return { link, style, htmlElement };
|
|
}
|