BackgroundImage component: support specific image sizes and lazy loading

This commit is contained in:
2026-01-05 14:49:33 +00:00
parent b5a751e237
commit abde60aecc
4 changed files with 113 additions and 69 deletions

View File

@@ -1,4 +1,4 @@
import { useSelect } from '@wordpress/data'; import { select } from '@wordpress/data';
/** /**
* BackgroundImage * BackgroundImage
@@ -6,28 +6,48 @@ import { useSelect } from '@wordpress/data';
* This component is used to display a background image for a block based on its attributes. * This component is used to display a background image for a block based on its attributes.
* *
* @param {object} props * @param {object} props
* @param {string} props.background_image The url of the background image. * @param {string} props.background_url The desired full size url.
* @param {string} props.background_url_lazy Tiny lazy url.
* @param {boolean} props.background_lazy Whether or not to lazy load the background image.
* @param {string} props.background_position The background-position property. * @param {string} props.background_position The background-position property.
* @param {boolean} props.background_fixed Toggle for background-attachment: fixed. * @param {boolean} props.background_fixed Toggle for background-attachment: fixed.
* @param {number} props.background_opacity The opacity value applied to the image. * @param {number} props.background_opacity The opacity value applied to the image.
* @returns {*} React JSX * @returns {*} React JSX
*/ */
export default function BackgroundImage({ background_url, background_position = 'center', background_fixed = false, background_opacity = 70 }) { export default function BackgroundImage({
background_url,
background_url_lazy,
background_lazy,
background_position = 'center',
background_fixed = false,
background_opacity = 70,
}) {
if (background_url) {
let styles = {
backgroundImage: `url(${background_url})`,
backgroundPosition: background_position,
backgroundSize: 'cover',
backgroundAttachment: background_fixed ? 'fixed' : 'scroll',
opacity: Number(background_opacity) * 0.01,
}
let attributes = {
className: 'badegg-block-background',
style: styles,
};
if(background_lazy) {
attributes['data-bg'] = background_url;
attributes.style.backgroundImage = `url(${background_url_lazy})`;
attributes.className += ' lazy-bg';
}
return (
<div { ...attributes } />
)
} else {
return;
}
return (
<>
{background_url && (
<div
className="badegg-block-background"
style={{
backgroundImage: `url(${background_url})`,
backgroundPosition: background_position,
backgroundSize: 'cover',
backgroundAttachment: background_fixed ? 'fixed' : 'scroll',
opacity: Number(background_opacity) * 0.01,
}}
/>
)}
</>
)
} }

View File

@@ -1,5 +1,3 @@
import { useSelect } from '@wordpress/data';
/** /**
* BlockSettings * BlockSettings
* *
@@ -12,6 +10,7 @@ import { useSelect } from '@wordpress/data';
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { select } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element'; import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch'; import apiFetch from '@wordpress/api-fetch';
@@ -85,7 +84,8 @@ export default function BlockSettings({ attributes, setAttributes }) {
background_hex, background_hex,
background_tint, background_tint,
background_image, background_image,
background_url, background_lazy,
background_size,
background_opacity, background_opacity,
background_contrast, background_contrast,
background_fixed, background_fixed,
@@ -159,7 +159,7 @@ export default function BlockSettings({ attributes, setAttributes }) {
{ 'background_colour' in attributes && attributes.background_colour && ![0, '0', 'white', 'black'].includes(attributes.background_colour) ? ( { 'background_colour' in attributes && attributes.background_colour && ![0, '0', 'white', 'black'].includes(attributes.background_colour) ? (
<> <>
<SelectControl <SelectControl
label={ __("Background Tint", "badegg") } label={ __("Tint", "badegg") }
value={ background_tint } value={ background_tint }
options={ configOptions.tints } options={ configOptions.tints }
onChange={ (value) => setAttributes({ background_tint: value }) } onChange={ (value) => setAttributes({ background_tint: value }) }
@@ -183,6 +183,12 @@ export default function BlockSettings({ attributes, setAttributes }) {
onChange={(value) => setAttributes({ background_fixed: value }) } onChange={(value) => setAttributes({ background_fixed: value }) }
__nextHasNoMarginBottom __nextHasNoMarginBottom
/> />
<ToggleControl
label={ __('Lazy Loaded', 'badegg') }
checked={ background_lazy }
onChange={(value) => setAttributes({ background_lazy: value }) }
__nextHasNoMarginBottom
/>
<RangeControl <RangeControl
__next40pxDefaultSize __next40pxDefaultSize
__nextHasNoMarginBottom __nextHasNoMarginBottom
@@ -199,10 +205,21 @@ export default function BlockSettings({ attributes, setAttributes }) {
<MediaUploadCheck> <MediaUploadCheck>
<MediaUpload <MediaUpload
onSelect={ (media) => { onSelect={ (media) => {
setAttributes({
let bgAttributes = {
background_image: media.id, background_image: media.id,
background_url: media.url, background_url: media.url,
}); background_url_lazy: '',
};
const image = select('core').getEntityRecord('postType', 'attachment', media.id);
if(image && image.media_details) {
bgAttributes.background_url_lazy = image.media_details.sizes.lazy.source_url;
bgAttributes.background_url = image.media_details.sizes.hero.source_url;
}
setAttributes( bgAttributes );
}} }}
allowedTypes={ ['image'] } allowedTypes={ ['image'] }
value={ background_image } value={ background_image }
@@ -219,7 +236,11 @@ export default function BlockSettings({ attributes, setAttributes }) {
{ background_image != 0 && ( { background_image != 0 && (
<Button <Button
onClick={ () => setAttributes({ background_image: 0, background_url: '' }) } onClick={ () => setAttributes({
background_image: 0,
background_url: '',
background_url_lazy: '',
}) }
isDestructive isDestructive
variant="secondary" variant="secondary"
> >

View File

@@ -1,59 +1,54 @@
export default function LazyLoadInit() export default function LazyLoadInit()
{ {
document.addEventListener('DOMContentLoaded', LazyLoad()); document.addEventListener('DOMContentLoaded', LazyLoad());
document.addEventListener('DOMContentLoaded', LazyBG());
} }
function LazyLoad() { function LazyLoad() {
var lazyImages = [].slice.call(document.querySelectorAll('img.lazy')); const lazyElements = [].slice.call(
document.querySelectorAll('img.lazy, .lazy-bg')
);
if ('IntersectionObserver' in window) { if ('IntersectionObserver' in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) { const lazyObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(function(entry) { entries.forEach(entry => {
if (entry.isIntersecting) { if (!entry.isIntersecting) return;
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
if(lazyImage.dataset.srcset) { const el = entry.target;
lazyImage.srcset = lazyImage.dataset.srcset;
// Handle <img>
if (el.tagName === 'IMG') {
el.src = el.dataset.src;
if (el.dataset.srcset) {
el.srcset = el.dataset.srcset;
} }
lazyImage.classList.remove('lazy'); el.classList.remove('lazy');
lazyImageObserver.unobserve(lazyImage);
} }
// Handle background images
else {
if (el.dataset.bg) {
el.style.backgroundImage = `url("${el.dataset.bg}")`;
el.classList.remove('lazy-bg');
el.classList.add('lazy-loaded');
}
}
observer.unobserve(el);
}); });
}); });
lazyImages.forEach(function(lazyImage) { lazyElements.forEach(el => lazyObserver.observe(el));
lazyImageObserver.observe(lazyImage);
});
} else { } else {
// Possibly fall back to a more compatible method here // Optional fallback: load everything immediately
} lazyElements.forEach(el => {
} if (el.tagName === 'IMG' && el.dataset.src) {
el.src = el.dataset.src;
function LazyBG() { } else if (el.dataset.bg) {
el.style.backgroundImage = `url("${el.dataset.bg}")`;
var lazyImages = [].slice.call(document.querySelectorAll('.lazy-bg')); }
});
if ('IntersectionObserver' in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.computedStyleMap.backgroundImage = `url(${lazyImage.dataset.bg})`;
lazyImage.classList.remove('lazy-bg');
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to a more compatible method here
} }
} }

View File

@@ -31,8 +31,16 @@
"default": 0 "default": 0
}, },
"background_url": { "background_url": {
"type": "string", "type": "string",
"default": "" "default": ""
},
"background_url_lazy": {
"type": "string",
"default": ""
},
"background_lazy": {
"type": "boolean",
"default": true
}, },
"background_opacity": { "background_opacity": {
"type": "integer", "type": "integer",
@@ -40,7 +48,7 @@
}, },
"background_position": { "background_position": {
"type": "string", "type": "string",
"default": "" "default": "center center"
}, },
"background_fixed": { "background_fixed": {
"type": "boolean", "type": "boolean",