Skip to main content

Opening or closing transition

To adjust opening or closing transition type use lightbox option showHideAnimationType (String). It supports three values - zoom (default), fade (default if there is no thumbnail) and none.

zoom

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
gallery: '#gallery--zoom-transition',
children: 'a',
showHideAnimationType: 'zoom',
pswpModule: () => import('/photoswipe/photoswipe.esm.js')
});
lightbox.init();
<div class="pswp-gallery" id="gallery--zoom-transition">
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-2500.jpg"
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-2500.jpg"
data-pswp-width="1669"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1666"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-200.jpg" alt="" />
</a>
</div>

If you're using a different datasource, for example Array of images and you still want to use zoom transition, please refer to Separate DOM and data guide.

fade

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
gallery:'#gallery--fade-transition',
children:'a',
showHideAnimationType: 'fade',
pswpModule: () => import('/photoswipe/photoswipe.esm.js')
});
lightbox.init();
<div class="pswp-gallery" id="gallery--fade-transition">
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-2500.jpg"
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-2500.jpg"
data-pswp-width="1669"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1666"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1668"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-200.jpg" alt="" />
</a>
</div>

none

Automatically selected if user agent (prefers-reduced-motion: reduce).

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
gallery: '#gallery--none-transition',
children: 'a',
showHideAnimationType: 'none',

// optionally disable zoom transition,
// to create more consistent experience
zoomAnimationDuration: false,

pswpModule: () => import('/photoswipe/photoswipe.esm.js')
});
lightbox.init();
<div class="pswp-gallery" id="gallery--none-transition">
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-2500.jpg"
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-2500.jpg"
data-pswp-width="1669"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1666"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1668"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/6/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/6/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/7/img-2500.jpg"
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/7/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/8/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/8/img-200.jpg" alt="" />
</a>
</div>

Transition duration and easing

Use options showAnimationDuration and hideAnimationDuration (Integer, default 333).

Option easing (String, default cubic-bezier(.4,0,.22,1)) accepts any CSS timing-function. It is applied to any zoom transition (including double-tap).

Both options can be modified dynamically while PhotoSwipe is opened.

In the example below transition duration is set to 1000ms (1s). Easing is defined dynamically (opening transition gets ease-out-back, zoom transitions gets ease-in-out-back, and closing transition gets ease-in-back):

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';

const backEasing = {
in: 'cubic-bezier(0.6, -0.28, 0.7, 1)',
out: 'cubic-bezier(0.3, 0, 0.32, 1.275)',
inOut: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'
};

const lightbox = new PhotoSwipeLightbox({
gallery:'#gallery--customized-transition',
children:'a',

showHideAnimationType: 'zoom',
showAnimationDuration: 1000,
hideAnimationDuration: 1000,

pswpModule: () => import('/photoswipe/photoswipe.esm.js')
});
lightbox.on('firstUpdate', () => {
lightbox.pswp.options.easing = backEasing.out;
});
lightbox.on('initialZoomInEnd', () => {
lightbox.pswp.options.easing = backEasing.inOut;
});
lightbox.on('close', () => {
lightbox.pswp.options.easing = backEasing.in;
});
lightbox.init();
<div class="pswp-gallery" id="gallery--customized-transition">
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-2500.jpg"
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-2500.jpg"
data-pswp-width="1669"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-200.jpg" alt="" />
</a>
<a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1666"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-200.jpg" alt="" />
</a>
</div>

Animating from cropped thumbnail

Step 1: use thumbnail image that matches aspect ratio of the large image.

Step 2: crop thumbnail via CSS. For example, using object-fit:cover, or background-size:cover.

Step 3: add data-cropped="true" attribute to your links that opens PhotoSwipe.

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
gallery:'#gallery--cropped-thumbs',
children:'a',
pswpModule: () => import('/photoswipe/photoswipe.esm.js')
});
lightbox.init();
<div class="pswp-gallery" id="gallery--cropped-thumbs"><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-2500.jpg" 
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank"
data-cropped="true">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-200.jpg" alt="" />
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-2500.jpg"
data-pswp-width="1669"
data-pswp-height="2500"
target="_blank"
data-cropped="true">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-200.jpg" alt="" />
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1666"
target="_blank"
data-cropped="true">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-200.jpg" alt="" />
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank"
data-cropped="true">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-200.jpg" alt="" />
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1668"
target="_blank"
data-cropped="true">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-200.jpg" alt="" />
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/6/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank"
data-cropped="true">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/6/img-200.jpg" alt="" />
</a></div>

Hiding elements that overlap thumbnails

If you have some element that overlays thumbnail, you may want to fade it out when PhotoSwipe is opening, and fade it back in when PhotoSwipe is closed.

import PhotoSwipeLightbox from '/photoswipe/photoswipe-lightbox.esm.js';
const lightbox = new PhotoSwipeLightbox({
gallery:'#gallery--badges',
children: 'a',
pswpModule: () => import('/photoswipe/photoswipe.esm.js')
});

let firstElWithBadge;
let lastElWithBadge;

// Gallery is starting to open
lightbox.on('afterInit', () => {
firstElWithBadge = lightbox.pswp.currSlide.data.element;
hideBadge(firstElWithBadge);
});

// Gallery is starting to close
lightbox.on('close', () => {
lastElWithBadge = lightbox.pswp.currSlide.data.element;
if(lastElWithBadge !== firstElWithBadge) {
showBadge(firstElWithBadge);
hideBadge(lastElWithBadge);
}
});

// Gallery is closed
lightbox.on('destroy', () => {
showBadge(lastElWithBadge);
});

lightbox.init();

function hideBadge(el) {
el.querySelector('.badge')
.classList
.add('badge--hidden');
};
function showBadge(el) {
el.querySelector('.badge')
.classList
.remove('badge--hidden');
}
.badge {
position: absolute;
bottom: 5px;
left: 5px;
padding: 5px;
color: #FFF;
background: rgba(0,0,0,0.5);
line-height: 1;
transition: opacity 100ms linear;
}
.badge--hidden {
opacity: 0;
}
<div class="pswp-gallery pswp-gallery--single-column" id="gallery--badges"><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-2500.jpg" 
data-pswp-width="1875"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/1/img-200.jpg" alt="" />
<div class="badge">Badge 1</div>
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-2500.jpg"
data-pswp-width="1669"
data-pswp-height="2500"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/2/img-200.jpg" alt="" />
<div class="badge">Badge 2</div>
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1666"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/3/img-200.jpg" alt="" />
<div class="badge">Badge 3</div>
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/4/img-200.jpg" alt="" />
<div class="badge">Badge 4</div>
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1668"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/5/img-200.jpg" alt="" />
<div class="badge">Badge 5</div>
</a><a href="https://cdn.photoswipe.com/photoswipe-demo-images/photos/6/img-2500.jpg"
data-pswp-width="2500"
data-pswp-height="1667"
target="_blank">
<img src="https://cdn.photoswipe.com/photoswipe-demo-images/photos/6/img-200.jpg" alt="" />
<div class="badge">Badge 6</div>
</a></div>

You may use this technique to hide any element that might interfere with the transition. For example, fixed header, sharing icons or carousel arrows.