diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php
index 58c8ca7200102..b435d769ef340 100644
--- a/lib/block-supports/behaviors.php
+++ b/lib/block-supports/behaviors.php
@@ -87,10 +87,19 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
// We want to store the src in the context so we can set it dynamically when the lightbox is opened.
$z = new WP_HTML_Tag_Processor( $content );
$z->next_tag( 'img' );
+
if ( isset( $block['attrs']['id'] ) ) {
- $img_src = wp_get_attachment_url( $block['attrs']['id'] );
+ $img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] );
+ $img_metadata = wp_get_attachment_metadata( $block['attrs']['id'] );
+ $img_width = $img_metadata['width'];
+ $img_height = $img_metadata['height'];
+ $img_uploaded_srcset = wp_get_attachment_image_srcset( $block['attrs']['id'] );
} else {
- $img_src = $z->get_attribute( 'src' );
+ $img_uploaded_src = $z->get_attribute( 'src' );
+ $img_dimensions = wp_getimagesize( $img_uploaded_src );
+ $img_width = $img_dimensions[0];
+ $img_height = $img_dimensions[1];
+ $img_uploaded_srcset = '';
}
$w = new WP_HTML_Tag_Processor( $content );
@@ -99,24 +108,59 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
$w->set_attribute( 'data-wp-interactive', true );
$w->set_attribute(
'data-wp-context',
- sprintf( '{ "core":{ "image": { "initialized": false, "imageSrc": "%s", "lightboxEnabled": false, "lightboxAnimation": "%s", "hideAnimationEnabled": false } } }', $img_src, $lightbox_animation )
+ sprintf(
+ '{ "core":
+ { "image":
+ { "imageLoaded": false,
+ "initialized": false,
+ "lightboxEnabled": false,
+ "hideAnimationEnabled": false,
+ "preloadInitialized": false,
+ "lightboxAnimation": "%s",
+ "imageUploadedSrc": "%s",
+ "imageCurrentSrc": "",
+ "imageSrcSet": "%s",
+ "targetWidth": "%s",
+ "targetHeight": "%s"
+ }
+ }
+ }',
+ $lightbox_animation,
+ $img_uploaded_src,
+ $img_uploaded_srcset,
+ $img_width,
+ $img_height
+ )
);
+ $w->next_tag( 'img' );
+ $w->set_attribute( 'data-wp-effect', 'effects.core.image.setCurrentSrc' );
$body_content = $w->get_updated_html();
// Wrap the image in the body content with a button.
$img = null;
- preg_match( '/]+>/', $content, $img );
+ preg_match( '/]+>/', $body_content, $img );
$button = '
- '
+ '
. $img[0] .
'
';
$body_content = preg_replace( '/]+>/', $button, $body_content );
// Add src to the modal image.
$m = new WP_HTML_Tag_Processor( $content );
+ $m->next_tag( 'figure' );
+ $m->add_class( 'responsive-image' );
$m->next_tag( 'img' );
- $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.imageSrc' );
- $modal_content = $m->get_updated_html();
+ $m->set_attribute( 'src', '' );
+ $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.responsiveImgSrc' );
+ $initial_image_content = $m->get_updated_html();
+
+ $q = new WP_HTML_Tag_Processor( $content );
+ $q->next_tag( 'figure' );
+ $q->add_class( 'enlarged-image' );
+ $q->next_tag( 'img' );
+ $q->set_attribute( 'src', '' );
+ $q->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' );
+ $enlarged_image_content = $q->get_updated_html();
$background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) );
@@ -142,7 +186,8 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
- $modal_content
+ $initial_image_content
+ $enlarged_image_content
HTML;
diff --git a/packages/block-library/src/image/interactivity.js b/packages/block-library/src/image/interactivity.js
index 2ef370496a894..6560cef9e0243 100644
--- a/packages/block-library/src/image/interactivity.js
+++ b/packages/block-library/src/image/interactivity.js
@@ -22,32 +22,43 @@ store( {
core: {
image: {
showLightbox: ( { context, event } ) => {
+ // We can't initialize the lightbox until the reference
+ // image is loaded, otherwise the UX is broken.
+ if ( ! context.core.image.imageLoaded ) {
+ return;
+ }
context.core.image.initialized = true;
context.core.image.lastFocusedElement =
window.document.activeElement;
context.core.image.scrollDelta = 0;
+ context.core.image.lightboxEnabled = true;
+ if ( context.core.image.lightboxAnimation === 'zoom' ) {
+ setZoomStyles(
+ event.target.nextElementSibling,
+ context,
+ event
+ );
+ }
+ // Hide overflow only when the animation is in progress,
+ // otherwise the removal of the scrollbars will draw attention
+ // to itself and look like an error
+ document.documentElement.classList.add(
+ 'has-lightbox-open'
+ );
+
// Since the img is hidden and its src not loaded until
// the lightbox is opened, let's create an img element on the fly
// so we can get the dimensions we need to calculate the styles
+ context.core.image.preloadInitialized = true;
const imgDom = document.createElement( 'img' );
-
imgDom.onload = function () {
- // Enable the lightbox only after the image
- // is loaded to prevent flashing of unstyled content
- context.core.image.lightboxEnabled = true;
- if ( context.core.image.lightboxAnimation === 'zoom' ) {
- setZoomStyles( imgDom, context, event );
- }
-
- // Hide overflow only when the animation is in progress,
- // otherwise the removal of the scrollbars will draw attention
- // to itself and look like an error
- document.documentElement.classList.add(
- 'has-lightbox-open'
- );
+ context.core.image.activateLargeImage = true;
};
- imgDom.setAttribute( 'src', context.core.image.imageSrc );
+ imgDom.setAttribute(
+ 'src',
+ context.core.image.imageUploadedSrc
+ );
},
hideLightbox: async ( { context, event } ) => {
context.core.image.hideAnimationEnabled = true;
@@ -131,9 +142,14 @@ store( {
roleAttribute: ( { context } ) => {
return context.core.image.lightboxEnabled ? 'dialog' : '';
},
- imageSrc: ( { context } ) => {
+ responsiveImgSrc: ( { context } ) => {
+ return context.core.image.activateLargeImage
+ ? ''
+ : context.core.image.imageCurrentSrc;
+ },
+ enlargedImgSrc: ( { context } ) => {
return context.core.image.initialized
- ? context.core.image.imageSrc
+ ? context.core.image.imageUploadedSrc
: '';
},
},
@@ -142,6 +158,30 @@ store( {
effects: {
core: {
image: {
+ setCurrentSrc: ( { context, ref } ) => {
+ if ( ref.complete ) {
+ context.core.image.imageLoaded = true;
+ context.core.image.imageCurrentSrc = ref.currentSrc;
+ } else {
+ ref.addEventListener( 'load', function () {
+ context.core.image.imageLoaded = true;
+ context.core.image.imageCurrentSrc =
+ this.currentSrc;
+ } );
+ }
+ },
+ preloadLightboxImage: ( { context, ref } ) => {
+ ref.addEventListener( 'mouseover', () => {
+ if ( ! context.core.image.preloadInitialized ) {
+ context.core.image.preloadInitialized = true;
+ const imgDom = document.createElement( 'img' );
+ imgDom.setAttribute(
+ 'src',
+ context.core.image.imageUploadedSrc
+ );
+ }
+ } );
+ },
initLightbox: async ( { context, ref } ) => {
context.core.image.figureRef =
ref.querySelector( 'figure' );
@@ -163,8 +203,8 @@ store( {
} );
function setZoomStyles( imgDom, context, event ) {
- let targetWidth = imgDom.naturalWidth;
- let targetHeight = imgDom.naturalHeight;
+ let targetWidth = context.core.image.targetWidth;
+ let targetHeight = context.core.image.targetHeight;
const verticalPadding = 40;
diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js
index 5f802d0e85063..0735b9a7573e6 100644
--- a/test/e2e/specs/editor/blocks/image.spec.js
+++ b/test/e2e/specs/editor/blocks/image.spec.js
@@ -987,13 +987,19 @@ test.describe( 'Image - interactivity', () => {
const lightbox = page.locator( '.wp-lightbox-overlay' );
await expect( lightbox ).toBeHidden();
- const image = lightbox.locator( 'img' );
+ const responsiveImage = lightbox.locator( '.responsive-image img' );
+ const enlargedImage = lightbox.locator( '.enlarged-image img' );
- await expect( image ).toHaveAttribute( 'src', '' );
+ await expect( responsiveImage ).toHaveAttribute(
+ 'src',
+ new RegExp( filename )
+ );
+ await expect( enlargedImage ).toHaveAttribute( 'src', '' );
await page.getByRole( 'button', { name: 'Enlarge image' } ).click();
- await expect( image ).toHaveAttribute(
+ await expect( responsiveImage ).toHaveAttribute( 'src', '' );
+ await expect( enlargedImage ).toHaveAttribute(
'src',
new RegExp( filename )
);
@@ -1176,12 +1182,19 @@ test.describe( 'Image - interactivity', () => {
await page.goto( `/?p=${ postId }` );
const lightbox = page.locator( '.wp-lightbox-overlay' );
- const imageDom = lightbox.locator( 'img' );
- await expect( imageDom ).toHaveAttribute( 'src', '' );
+ const responsiveImage = lightbox.locator( '.responsive-image img' );
+ const enlargedImage = lightbox.locator( '.enlarged-image img' );
+
+ await expect( responsiveImage ).toHaveAttribute(
+ 'src',
+ new RegExp( imgUrl )
+ );
+ await expect( enlargedImage ).toHaveAttribute( 'src', '' );
await page.getByRole( 'button', { name: 'Enlarge image' } ).click();
- await expect( imageDom ).toHaveAttribute( 'src', imgUrl );
+ await expect( responsiveImage ).toHaveAttribute( 'src', '' );
+ await expect( enlargedImage ).toHaveAttribute( 'src', imgUrl );
} );
} );