Skip to content

Commit

Permalink
fix: reduce loading latency of lightbox photos by capping width/heigh…
Browse files Browse the repository at this point in the history
…t to window

PiperOrigin-RevId: 616284696
  • Loading branch information
Extended Component Library Team authored and copybara-github committed Mar 16, 2024
1 parent c14d28d commit 29e02ea
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ interface TileSize {
heightPx: number;
}

/**
* Returns the desired photo size in pixels based on CSS pixels and max size,
* accounting for diff between physical and CSS pixels on current device; see:
* https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio.
*/
function getPhotoSize(cssPx: number, max: number) {
const devicePx = Math.ceil(cssPx * window.devicePixelRatio);
return Math.min(devicePx, max);
}

/**
* Formats a `google.maps.places.Photo` object for display based on tile size.
*
Expand All @@ -60,23 +70,19 @@ interface TileSize {
*/
function formatPhoto(photo: Photo, tileSize: TileSize): FormattedPhoto {
const photoSizeRatio = photo.widthPx / photo.heightPx;
const windowSizeRatio = window.innerWidth / window.innerHeight;
const tileSizeRatio = tileSize.widthPx / tileSize.heightPx;

// Account for diff between physical and CSS pixels on current device; see:
// https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio.
const lightboxPhotoOptions = photoSizeRatio > windowSizeRatio ?
{maxHeight: getPhotoSize(window.innerHeight, MAX_PHOTO_SIZE_PX)} :
{maxWidth: getPhotoSize(window.innerWidth, MAX_PHOTO_SIZE_PX)};
const tilePhotoOptions = photoSizeRatio > tileSizeRatio ?
{maxHeight: Math.ceil(tileSize.heightPx * window.devicePixelRatio)} :
{maxWidth: Math.ceil(tileSize.widthPx * window.devicePixelRatio)};
{maxHeight: getPhotoSize(tileSize.heightPx, MAX_TILE_PHOTO_SIZE_PX)} :
{maxWidth: getPhotoSize(tileSize.widthPx, MAX_TILE_PHOTO_SIZE_PX)};

return {
uri: photo.getURI({
maxHeight: MAX_PHOTO_SIZE_PX,
maxWidth: MAX_PHOTO_SIZE_PX,
}),
tileUri: photo.getURI({
maxHeight: tilePhotoOptions.maxHeight || MAX_TILE_PHOTO_SIZE_PX,
maxWidth: tilePhotoOptions.maxWidth || MAX_TILE_PHOTO_SIZE_PX,
}),
uri: photo.getURI(lightboxPhotoOptions),
tileUri: photo.getURI(tilePhotoOptions),
attributions: photo.authorAttributions,
};
}
Expand Down Expand Up @@ -358,9 +364,13 @@ export class PlacePhotoGallery extends PlaceDataConsumer {

protected override updated() {
if (!this.tileSize && this.firstTileElement) {
// Note that sometimes a tile's BoundingClientRect becomes defined outside
// Lit's reactive update cycle. In such cases the tile size will be zero,
// and we cap width/height at `MAX_TILE_PHOTO_SIZE_PX` to avoid requesting
// an overly large image.
this.tileSize = {
widthPx: this.firstTileElement.clientWidth,
heightPx: this.firstTileElement.clientHeight,
widthPx: this.firstTileElement.clientWidth || MAX_TILE_PHOTO_SIZE_PX,
heightPx: this.firstTileElement.clientHeight || MAX_TILE_PHOTO_SIZE_PX,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import type {Place, PlaceResult} from '../../utils/googlemaps_types.js';

import {PlacePhotoGallery} from './place_photo_gallery.js';


const fakePlace = makeFakePlace({
id: '1234567890',
displayName: 'Place Name',
Expand Down Expand Up @@ -103,12 +102,14 @@ describe('PlacePhotoGallery', () => {
maxTiles?: number,
place?: Place|PlaceResult,
clickOnTile?: number,
hidden?: boolean,
}) {
const root = env.render(html`
<gmpx-place-photo-gallery
dir=${config?.rtl ? 'rtl' : 'ltr'}
max-tiles=${ifDefined(config?.maxTiles)}
.place=${config?.place}
.hidden=${config?.hidden ?? false}
></gmpx-place-photo-gallery>
`);

Expand Down Expand Up @@ -161,10 +162,12 @@ describe('PlacePhotoGallery', () => {
const {tiles} = await prepareState({place: fakePlace});

expect(tiles.length).toBe(3);
expect(getUri0Spy).toHaveBeenCalledWith({maxHeight: 4800, maxWidth: 4800});
expect(getUri0Spy).toHaveBeenCalledWith({maxHeight: 134, maxWidth: 1200});
expect(getUri1Spy).toHaveBeenCalledWith({maxHeight: 1200, maxWidth: 142});
expect(getUri2Spy).toHaveBeenCalledWith({maxHeight: 1200, maxWidth: 142});
expect(getUri0Spy).toHaveBeenCalledWith({maxHeight: window.innerHeight});
expect(getUri0Spy).toHaveBeenCalledWith({maxHeight: 134});
expect(getUri1Spy).toHaveBeenCalledWith({maxWidth: window.innerWidth});
expect(getUri1Spy).toHaveBeenCalledWith({maxWidth: 142});
expect(getUri2Spy).toHaveBeenCalledWith({maxWidth: window.innerWidth});
expect(getUri2Spy).toHaveBeenCalledWith({maxWidth: 142});
expect(getComputedStyle(tiles[0]).backgroundImage)
.toBe('url("https://lh3.googleusercontent.com/places/A")');
expect(getComputedStyle(tiles[1]).backgroundImage)
Expand All @@ -173,6 +176,26 @@ describe('PlacePhotoGallery', () => {
.toBe('url("https://lh3.googleusercontent.com/places/C")');
});

it('accounts for device pixel ratio when generating image URIs', async () => {
spyOnProperty(window, 'devicePixelRatio').and.returnValue(2);
const getUriSpy = spyOn(fakePlace.photos![0], 'getURI').and.callThrough();

await prepareState({place: fakePlace});

expect(getUriSpy).toHaveBeenCalledWith({maxHeight: window.innerHeight * 2});
expect(getUriSpy).toHaveBeenCalledWith({maxHeight: 134 * 2});
});

it('gets images with max tile size when tile has unknown size', async () => {
const getUri0Spy = spyOn(fakePlace.photos![0], 'getURI').and.callThrough();
const getUri1Spy = spyOn(fakePlace.photos![1], 'getURI').and.callThrough();

await prepareState({place: fakePlace, hidden: true});

expect(getUri0Spy).toHaveBeenCalledWith({maxHeight: 1200});
expect(getUri1Spy).toHaveBeenCalledWith({maxWidth: 1200});
});

it('shows all photos from PlaceResult data as tiles, in order', async () => {
const getUrl0Spy =
spyOn(fakePlaceResult.photos![0], 'getUrl').and.callThrough();
Expand All @@ -184,10 +207,12 @@ describe('PlacePhotoGallery', () => {
const {tiles} = await prepareState({place: fakePlaceResult});

expect(tiles.length).toBe(3);
expect(getUrl0Spy).toHaveBeenCalledWith({maxHeight: 4800, maxWidth: 4800});
expect(getUrl0Spy).toHaveBeenCalledWith({maxHeight: 134, maxWidth: 1200});
expect(getUrl1Spy).toHaveBeenCalledWith({maxHeight: 1200, maxWidth: 142});
expect(getUrl2Spy).toHaveBeenCalledWith({maxHeight: 1200, maxWidth: 142});
expect(getUrl0Spy).toHaveBeenCalledWith({maxHeight: window.innerHeight});
expect(getUrl0Spy).toHaveBeenCalledWith({maxHeight: 134});
expect(getUrl1Spy).toHaveBeenCalledWith({maxWidth: window.innerWidth});
expect(getUrl1Spy).toHaveBeenCalledWith({maxWidth: 142});
expect(getUrl2Spy).toHaveBeenCalledWith({maxWidth: window.innerWidth});
expect(getUrl2Spy).toHaveBeenCalledWith({maxWidth: 142});
expect(getComputedStyle(tiles[0]).backgroundImage)
.toBe('url("https://lh3.googleusercontent.com/places/A")');
expect(getComputedStyle(tiles[1]).backgroundImage)
Expand Down

0 comments on commit 29e02ea

Please sign in to comment.