Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image block: Add aspect ratio support to lightbox #52765

Merged
merged 43 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
06c7ad7
Add docs for `afterLoad`
DAreRodz Aug 10, 2023
50f2ba9
Move store options to the end
DAreRodz Aug 10, 2023
9ddc96a
Initial aspect-ration support implementation
SantosGuillamot Jul 12, 2023
30bed25
Move `inherit` property to `wp-style` directive
SantosGuillamot Jul 19, 2023
fb66e7a
Add `object-fit` cover
SantosGuillamot Jul 19, 2023
006ad65
Fix big images aspect ratio
SantosGuillamot Jul 19, 2023
153cd20
Fix animation on mobile; add comments
artemiomorales Jul 19, 2023
f3b66d5
Remove obsolete variable declaration
artemiomorales Jul 19, 2023
4901179
Fix PHP spacing
artemiomorales Jul 19, 2023
62305ee
Optimize animation performance; add comments; fix fade and thumbnail …
artemiomorales Jul 21, 2023
679d13e
Handle images in content with aspect ratios that differ from source i…
artemiomorales Jul 25, 2023
b751766
Simplify zooming lightbox
SantosGuillamot Jul 26, 2023
0c001ef
Add support for thumbnails
SantosGuillamot Jul 27, 2023
2933cfd
Clean code and change variable names
SantosGuillamot Jul 27, 2023
c514820
Fix thumbnails with cropped width and height
SantosGuillamot Jul 28, 2023
d267107
Add support for contain setting
SantosGuillamot Jul 28, 2023
e6ffc39
Add 5% padding to the lightbox container
SantosGuillamot Jul 28, 2023
8476b81
Move CSS properties to style tag
SantosGuillamot Jul 28, 2023
be53060
Add wp prefix to CSS global variables
SantosGuillamot Jul 28, 2023
e15b8d4
Remove `!important` from CSS
SantosGuillamot Jul 28, 2023
74ebaf9
Reuse zooming logic for the fade animation
SantosGuillamot Jul 28, 2023
08e0753
Remove fade conditional
SantosGuillamot Jul 28, 2023
f213d3b
Fix php standards
cbravobernal Jul 28, 2023
23081b6
Remove unnecessary inheritSize selector
SantosGuillamot Jul 28, 2023
71d703e
Update e2e test to fit new style tag
cbravobernal Jul 28, 2023
4a61010
Fix width of image container so button isn't wider than image
artemiomorales Jul 28, 2023
412ef4b
Add variable horizontal padding and fixed vertical padding
artemiomorales Jul 28, 2023
8d71c44
Fix fade out animation; modify animation for image slightly
artemiomorales Jul 28, 2023
e16120f
Prevent scroll after focusing last image
SantosGuillamot Jul 31, 2023
33d5f86
Add support for contain in lightbox image button
SantosGuillamot Jul 31, 2023
c947661
Add logic to set button styles only when needed and on image load
artemiomorales Aug 1, 2023
2c5f900
Simplify lazy loading logic
artemiomorales Aug 1, 2023
3b144d5
Remove extra div from image markup
artemiomorales Aug 2, 2023
ecbacda
Add 1 pixel to container dimensions to fix style bug on iOS
artemiomorales Aug 2, 2023
eb9a7dc
Add logic to center button when using 'contain'
artemiomorales Aug 2, 2023
8ba67ec
(After merge) Add logic to set button styles on window resize
artemiomorales Aug 9, 2023
31c9e4b
Replace loading=lazy with manual setting of src attribute
artemiomorales Aug 10, 2023
8083c39
Add link to comment
artemiomorales Aug 10, 2023
c09b11a
Remove obsolete code
artemiomorales Aug 10, 2023
0a04a6c
Update tests
artemiomorales Aug 11, 2023
efe4582
Add clarifying comment to setting of responsive image src attribute
artemiomorales Aug 11, 2023
6dc3da2
Update clarifying comment on 1px bug in iOS
artemiomorales Aug 11, 2023
b9c84e4
Add namespace to state
artemiomorales Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 63 additions & 23 deletions lib/block-supports/behaviors.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {

$aria_label = __( 'Enlarge image', 'gutenberg' );

$alt_attribute = trim( $processor->get_attribute( 'alt' ) );
$alt_attribute = $processor->get_attribute( 'alt' );

if ( null !== $alt_attribute ) {
$alt_attribute = trim( $alt_attribute );
}

if ( $alt_attribute ) {
/* translators: %s: Image alt text. */
Expand All @@ -89,22 +93,27 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
$z->next_tag( 'img' );

if ( isset( $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'] );
$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'];
} else {
$img_uploaded_src = $z->get_attribute( 'src' );
$img_width = 'none';
$img_height = 'none';
$img_uploaded_srcset = '';
$img_uploaded_src = $z->get_attribute( 'src' );
$img_width = 'none';
$img_height = 'none';
}

if ( isset( $block['attrs']['scale'] ) ) {
$scale_attr = $block['attrs']['scale'];
} else {
$scale_attr = false;
}

$w = new WP_HTML_Tag_Processor( $content );
$w->next_tag( 'figure' );
$w->add_class( 'wp-lightbox-container' );
$w->set_attribute( 'data-wp-interactive', true );

$w->set_attribute(
'data-wp-context',
sprintf(
Expand All @@ -118,47 +127,78 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
"lightboxAnimation": "%s",
"imageUploadedSrc": "%s",
"imageCurrentSrc": "",
"imageSrcSet": "%s",
"targetWidth": "%s",
"targetHeight": "%s"
"targetHeight": "%s",
"scaleAttr": "%s"
}
}
}',
$lightbox_animation,
$img_uploaded_src,
$img_uploaded_srcset,
$img_width,
$img_height
$img_height,
$scale_attr
)
);
$w->next_tag( 'img' );
$w->set_attribute( 'data-wp-effect', 'effects.core.image.setCurrentSrc' );
$w->set_attribute( 'data-wp-init', 'effects.core.image.setCurrentSrc' );
$w->set_attribute( 'data-wp-on--load', 'actions.core.image.handleLoad' );
$w->set_attribute( 'data-wp-effect', 'effects.core.image.setButtonStyles' );
$body_content = $w->get_updated_html();

// Wrap the image in the body content with a button.
$img = null;
preg_match( '/<img[^>]+>/', $body_content, $img );
$button = '<div class="img-container">
<button type="button" aria-haspopup="dialog" aria-label="' . esc_attr( $aria_label ) . '" data-wp-on--click="actions.core.image.showLightbox" data-wp-on--mouseenter="actions.core.image.preloadLightboxImage"></button>'
. $img[0] .
'</div>';
$button =
'<button
type="button"
aria-haspopup="dialog"
aria-label="' . esc_attr( $aria_label ) . '"
data-wp-on--click="actions.core.image.showLightbox"
data-wp-style--width="context.core.image.imageButtonWidth"
data-wp-style--height="context.core.image.imageButtonHeight"
data-wp-style--left="context.core.image.imageButtonLeft"
data-wp-style--top="context.core.image.imageButtonTop"
>
</button>'
. $img[0];
$body_content = preg_replace( '/<img[^>]+>/', $button, $body_content );

// Add src to the modal image.
// We need both a responsive image and an enlarged image to animate
// the zoom seamlessly on slow internet connections; the responsive
// image is a copy of the one in the body, which animates immediately
// as the lightbox is opened, while the enlarged one is a full-sized
// version that will likely still be loading as the animation begins.
$m = new WP_HTML_Tag_Processor( $content );
$m->next_tag( 'figure' );
$m->add_class( 'responsive-image' );
$m->next_tag( 'img' );
// We want to set the 'src' attribute to an empty string in the responsive image
// because otherwise, as of this writing, the wp_filter_content_tags() function in
// WordPress will automatically add a 'srcset' attribute to the image, which will at
// times cause the incorrectly sized image to be loaded in the lightbox on Firefox.
// Because of this, we bind the 'src' attribute explicitly the current src to reliably
// use the exact same image as in the content when the lightbox is first opened while
// we wait for the larger image to load.
$m->set_attribute( 'src', '' );
$m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.responsiveImgSrc' );
$m->set_attribute( 'data-wp-bind--src', 'context.core.image.imageCurrentSrc' );
$m->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
$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' );

// We set the 'src' attribute to an empty string to prevent the browser from loading the image
// on initial page load, then bind the attribute to a selector that returns the full-sized image src when
// the lightbox is opened. We could use 'loading=lazy' in combination with the 'hidden' attribute to
// accomplish the same behavior, but that approach breaks progressive loading of the image in Safari
// and Chrome (see https://github.com/WordPress/gutenberg/pull/52765#issuecomment-1674008151). Until that
// is resolved, manually setting the 'src' seems to be the best solution to load the large image on demand.
$q->set_attribute( 'src', '' );
$q->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' );
$q->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' );
$enlarged_image_content = $q->get_updated_html();

$background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) );
Expand All @@ -185,8 +225,8 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
<button type="button" aria-label="$close_button_label" style="fill: $close_button_color" class="close-button" data-wp-on--click="actions.core.image.hideLightbox">
$close_button_icon
</button>
$initial_image_content
$enlarged_image_content
<div class="lightbox-image-container">$initial_image_content</div>
<div class="lightbox-image-container">$enlarged_image_content</div>
<div class="scrim" style="background-color: $background_color"></div>
</div>
HTML;
Expand Down
156 changes: 72 additions & 84 deletions packages/block-library/src/image/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,7 @@
}

.wp-lightbox-container {

.img-container {
position: relative;
}
position: relative;

button {
border: none;
Expand All @@ -183,6 +180,7 @@
overflow: hidden;
width: 100vw;
height: 100vh;
box-sizing: border-box;
visibility: hidden;
cursor: zoom-out;

Expand All @@ -195,25 +193,39 @@
z-index: 5000000;
}

.lightbox-image-container {
position: absolute;
overflow: hidden;
top: 50%;
left: 50%;
transform-origin: top left;
transform: translate(-50%, -50%);
width: var(--wp--lightbox-container-width);
height: var(--wp--lightbox-container-height);
z-index: 9999999999;
}

.wp-block-image {
position: relative;
transform-origin: 0 0;
display: flex;
width: 100%;
height: 100%;
position: absolute;
z-index: 3000000;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
box-sizing: border-box;
z-index: 3000000;
margin: 0;

figcaption {
display: none;
img {
min-width: var(--wp--lightbox-image-width);
min-height: var(--wp--lightbox-image-height);
width: var(--wp--lightbox-image-width);
height: var(--wp--lightbox-image-height);
}

img {
max-width: 100%;
max-height: 100%;
width: auto;
figcaption {
display: none;
}
}

Expand All @@ -231,78 +243,61 @@
opacity: 0.9;
}

&.fade {
.wp-block-image {
padding: 40px 0;

@media screen and (min-width: 480px) {
padding: 40px;
}

@media screen and (min-width: 1920px) {
padding: 40px 80px;
}
// When fading, make the image come in slightly slower
// or faster than the scrim to give a sense of depth.
&.active {
visibility: visible;
animation: both turn-on-visibility 0.25s;
img {
animation: both turn-on-visibility 0.35s;
}

&.active {
visibility: visible;
animation: both turn-on-visibility 0.25s;

}
&.hideanimationenabled {
&:not(.active) {
animation: both turn-off-visibility 0.35s;
img {
animation: both turn-on-visibility 0.3s;
}
}
&.hideanimationenabled {
&:not(.active) {
animation: both turn-off-visibility 0.3s;

img {
animation: both turn-off-visibility 0.25s;
}
animation: both turn-off-visibility 0.25s;
}
}
}

&.zoom {
img {
position: absolute;
transform-origin: top left;
width: var(--lightbox-image-max-width);
height: var(--lightbox-image-max-height);
}

&.active {
opacity: 1;
visibility: visible;
.wp-block-image img {
animation: lightbox-zoom-in 0.4s forwards;

@media (prefers-reduced-motion) {
animation: both turn-on-visibility 0.4s;
}
}
.scrim {
animation: turn-on-visibility 0.4s forwards;
}
}
&.hideanimationenabled {
&:not(.active) {
.wp-block-image img {
animation: lightbox-zoom-out 0.4s forwards;

@media (prefers-reduced-motion) {
animation: both turn-off-visibility 0.4s;
@media (prefers-reduced-motion: no-preference) {
&.zoom {
&.active {
opacity: 1;
visibility: visible;
animation: none;
.lightbox-image-container {
animation: lightbox-zoom-in 0.4s;
// Override fade animation for image
img {
animation: none;
}
}
.scrim {
animation: turn-off-visibility 0.4s forwards;
animation: turn-on-visibility 0.4s forwards;
}
}
&.hideanimationenabled {
&:not(.active) {
animation: none;
.lightbox-image-container {
animation: lightbox-zoom-out 0.4s;
// Override fade animation for image
img {
animation: none;
}
}
.scrim {
animation: turn-off-visibility 0.4s forwards;
}
}
}
}
}
}

html.has-lightbox-open {
html.wp-has-lightbox-open {
overflow: hidden;
}

Expand Down Expand Up @@ -332,30 +327,23 @@ html.has-lightbox-open {

@keyframes lightbox-zoom-in {
0% {
left: var(--lightbox-initial-left-position);
top: var(--lightbox-initial-top-position);
transform: scale(var(--lightbox-scale-width), var(--lightbox-scale-height));
transform: translate(calc(-50vw + var(--wp--lightbox-initial-left-position)), calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale));
}
100% {
left: var(--lightbox-target-left-position);
top: var(--lightbox-target-top-position);
transform: scale(1, 1);
transform: translate(-50%, -50%) scale(1, 1);
}
}

@keyframes lightbox-zoom-out {
0% {
visibility: visible;
left: var(--lightbox-target-left-position);
top: var(--lightbox-target-top-position);
transform: scale(1, 1);
transform: translate(-50%, -50%) scale(1, 1);
}
99% {
visibility: visible;
}
100% {
left: var(--lightbox-initial-left-position);
top: var(--lightbox-initial-top-position);
transform: scale(var(--lightbox-scale-width), var(--lightbox-scale-height));
visibility: hidden;
transform: translate(calc(-50vw + var(--wp--lightbox-initial-left-position)), calc(-50vh + var(--wp--lightbox-initial-top-position))) scale(var(--wp--lightbox-scale));
}
}
Loading