PhotoSwipe
PhotoSwipe copied to clipboard
dynamic image dimensions
as you said in the docs its not possible, i even tried and went half way through...just one tiny piece missing. here my attempt:
var images = [
{ src: "http://foo.com/bar.gif", w:0, h:0}
];
var pswpElement = document.querySelectorAll('.pswp')[0];
var options = {
index: 0,
showHideOpacity: true
};
var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, images, options);
gallery.listen('imageLoadComplete', function(index, item) {
var img = new Image();
item.w = img.width;
item.h = img.height;
});
gallery.init();
sadly it only works good for the lazyloaded images "left" and "right" but not for the current one requested. in my use case i cant encode the size into the filename as the images are thumbnailed to fit in a box (e.g. image 760+420 should fit in a box of 400x250) and i dont know the resulting dimension, i even dont care ;). i dont want to make backend requests to get the size, thats not performant. as the example above is half way working i think its possible by PhotoSwipe itself (and it should be a feature anyway ;) )
i think the easiest fix would be to start all displaying stuff after the event is done?
In documentation there is an example on how to add or edit slides dynamically, see http://photoswipe.com/documentation/api.html (at the bottom). You may preload image, retrieve its size, and append (or replace) the dummy image.
Or if you have just 1 or 2 images, you can preload it, and only then initialize PhotoSwipe.
For now there are no plans to add such feature in core, as PhotoSwipe displays and animates image before it's loaded.
Is there any safe way to do this, so that the user sees no clipping or the image missing? I tried to implement it too, but replacing the dummy image works only when the image was previously loaded and the user hasn't reached that slide. If the user scrolls fast through the photos, PhotoSwipe will not have time to replace the current slide's width and height, because the preloaded image didn't load yet. Is there any way to reload the current slide? I just need to change the width and height properties of the current item in the items array. I have been banging my head against the wall to find a solution for this for hours and hours and hours on end...
May be small questions about code example. What happened with the memory usage after nn* clicks and nn* new images in memory? Whether to reset the variable 'img = null;'?
I solved the issue by adding the following css at the end of my stylesheet
.pswp__img {
height: auto !important;
}
and then in the js files where it has the following code I asked it to calculate the width and height of the window and fit our image inside.
for(var i = 0; i < numNodes; i++) {
figureEl = thumbElements[i]; // <figure> element
// include only element nodes
if(figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0]; // <a> element
//Instead of asking for height and width please calculate it for us We are lazy ;) Juni
var j_width = $(window).width();
var j_height = $(window).height();
size = [j_width, j_height]
// create slide object
item = {
src: linkEl.getAttribute('href'),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if(figureEl.children.length > 1) {
// <figcaption> content
item.title = figureEl.children[1].innerHTML;
}
if(linkEl.children.length > 0) {
// <img> thumbnail element, retrieving thumbnail url
item.msrc = linkEl.children[0].getAttribute('src');
}
item.el = figureEl; // save link to element for getThumbBoundsFn
items.push(item);
}
Be Careful I am using jQuery selector because I am too lazy. If you are not using jQuery please find a javascript way to calculate the height and width of your window!
I am using PhotoSwipe in my hexo theme with or without predefined dimension for photos. Here is my approach:
// Pass data to PhotoSwipe and initialize it
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.listen('imageLoadComplete', function(index, item) {
var linkEl = item.el.children[0];
var img = item.container.children[0];
if (!linkEl.getAttribute('data-size')) {
linkEl.setAttribute('data-size', img.naturalWidth + 'x' + img.naturalHeight);
item.w = img.naturalWidth;
item.h = img.naturalHeight;
gallery.invalidateCurrItems();
gallery.updateSize(true);
}
});
gallery.init();
You can easily do this by enclosing the code inside a function and load the dimensions from data attributes when photoswipe is called into view like so:
galleryTrigger = $('.pswp-call');
openPhotoSwipe = function( img, index ) {
var pswpElement = document.querySelectorAll('.pswp')[0];
// build items array
var items = [];
x = 0;
galleryTrigger.each(function(){
items.push({
src : $(this).data('url'),
w : $(this).data('width'),
h : $(this).data('height'),
title : $(this).data('caption'),
});
x++;
})
// define options if needed
var options = {
index : index,
showHideOpacity : true,
// buttons
closeEl : true,
captionEl : true,
fullscreenEl : true,
zoomEl : true,
shareEl : true,
counterEl : true,
arrowEl : true,
preloaderEl : true,
}
var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
}
galleryTrigger.click(function(){
var img = $(this).data('url');
var index = $(this).data('index');
openPhotoSwipe( img, index );
});
@denjones thanks for the sharing solution. But naturalWidth
and naturalHeight
are undefined
in my case.
I've updated your solution a bit and added an ability to define data-size="auto"
for verbosity.
gallery.listen('imageLoadComplete', function (index, item) {
var linkEl = item.el.children[0];
if (!linkEl.getAttribute('data-size') || linkEl.getAttribute('data-size') == 'auto') {
var img = new Image();
img.src = linkEl.getAttribute('href');
linkEl.setAttribute('data-size', img.naturalWidth + 'x' + img.naturalHeight);
item.w = img.naturalWidth;
item.h = img.naturalHeight;
gallery.invalidateCurrItems();
gallery.updateSize(true);
}
});
I found 2 issues with this way:
- If
gallery.invalidateCurrItems();
present - first click on image displaying it without animation.
// What bad could happen if it will be omitted?
- If animation is disabled - PhotoSwipe perform init and only after will fire
imageLoadComplete
event. Because of it exception will be thrown:
photoswipe.min.js:4 Uncaught TypeError: Cannot read property 'x' of undefined
With gettingData
event situation it's more strange - because it's firing before init and sometimes after, different on each page refresh.
@a-komarev maybe you should reference an existing img element rather then creating one dynamically.
@denjones but I have only thumbnails on the page wrapped in <a>
with href of original images.
The photogallery can be opened only one time in Android but works well in iOS, why? everyone can help me?
@denjones Are you sure that this is working for you? I can't get the <img>
at that part of the code. Only after the imageLoadComplete
event handler exits I see it there. What does your markup look like for each slide?
I will try @a-komarev 's approach.
@TwentyOneSolutions It does work for me in my case. check this sample http://blog.sprabbit.com/hexo-theme-chan/2013/12/26/images/
Yes, I saw it, but when I tried that technique with many (about 40) images, and with disabling the browser's cache, I was getting errors (though TBH I think that the errors were due to the fact that I was using a different markup with no <img>
tags).
I ended up with the following solution:
first, I added a boolean variable to each slide specifying if doGetSlideDimensions
is required, so if there is no data-size
it is set to true.
then, I wrote a function to get the image dimensions for a given slide, like so:
function getSlideDimensions(slide) {
if (!slide.doGetSlideDimensions)
return; // make sure we don't keep requesting the image if it doesn't exist etc.
var img = new Image();
$(img).on("error", function(evt){
slide.doGetSlideDimensions = false;
});
$(img).on("load", function(evt){
slide.doGetSlideDimensions = false;
slide.w = img.naturalWidth;
slide.h = img.naturalHeight;
photoSwipe.invalidateCurrItems();
photoSwipe.updateSize(true);
});
img.src = slide.src;
}
I'm using here a global variable named photoSwipe
which points to the PhotoSwipe object, but it can be passed as a variable instead, or written as a function inside the initializer where there is a reference to that instance.
The last thing is to call that function from two event handlers, gettingData
and imageLoadComplete
:
photoSwipe.listen("gettingData", function(index, slide){
if (slide.doGetSlideDimensions) {
setTimeout(
// use setTimeout so that it runs in the event loop
function(){ getSlideDimensions(slide); }
,300
);
}
});
photoSwipe.listen("imageLoadComplete", function(index, slide){
if (slide.doGetSlideDimensions)
getSlideDimensions(slide);
});
You can see a working example at http://www.intelliflame.com/gallery.htm
Anyone found a fix for this for us who have thumbnails?
@elbasankadrija my solution works well. see above.
Can isapi's script behavior made default within photoswipe?
@isapi Your solution works for me, but img blinks when it loaded on Android and IOS
I missed this thread while trying to figure out the dynamic sizing issue :(
Good news is I may be on to something, but I haven't done any work with thumbnails yet.
I tested in Firefox on my android and it works quite well. The odd time it gets confused a bit with sizing when I reorient on the fly but I'm willing to live with that for now
// note: photoswipe.js: don't forget to move "_shout('imageLoadComplete', index, item);" before "item.img = null;"
gallery.listen('imageLoadComplete',
function(index, item) {
var actualImageWidth = item.img.naturalWidth;
var actualImageHeight = item.img.naturalHeight;
// recalculate the fit ratio based on actual image dimensions instead of those which would have been specified in image collection
// adapted from photoswipe.js:2743
var viewportSize = gallery.viewportSize;
var availableX = viewportSize.x;
var availableY = viewportSize.y - item.vGap.top - item.vGap.bottom;
var hRatio = availableX / actualImageWidth;
var vRatio = availableY / actualImageHeight;
item.fitRatio = hRatio < vRatio ? hRatio : vRatio;
item.w = Math.round(actualImageWidth * item.fitRatio),
item.h = Math.round(actualImageHeight * item.fitRatio),
gallery.updateSize();
});
- A side effect is that images take up more of the screen (zoomed in a bit).
- I've also increased my maxSpreadZoom to 3 to allow a bit more zooming.
- I'm planning on serving images that are 800x600 or 1024x768.
I've figured out an approach that should work in all supported browsers. Instead of using an img
tag to render images, use the following:
<svg
class="pswp__img"
width="${item.w}"
height="${item.h}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<image
xlink:href="${item.src}"
x="0"
y="0"
height="100"
width="100"
/>
</svg>
This lets you specify the width/height you want the photoswipe container to have; if the image has different dimensions, its larger dimension will fill the container and the smaller one is centred.
SVG support is quite good - IE9+, android 3+ etc.
Have I missed something important or does this enable fluid sized images?
- [ ] Progressive loading - may have to create an
img
tag and not display it to keep the 'is loaded' check - [ ] Stretched placeholder - SVG stretches images really nicely
- [ ] Initial zoom-in transition - SVG has a fixed size and animates nicely
- [ ] Panning / Zooming - SVG has a fixed size and animates nicely
- [ ] Caption positioning: I'm not clear from reading the code how this is used, but a fixed size element should keep working anyways.
Any news on adding that feature to core?
It's not a small job and I certainly wouldn't expect @dimsemenov to do it for us.
I would be keen to hear whether a patch would be welcome or not before attempting it. If not, I might still do it on a fork, but would have to really think about it. @piernik / @isapir hit me up if you're thinking about taking it on yourself, happy to talk about it more.
@DanielHeath I have submitted a couple of PRs a while back but it doesn't seem like anyone accepts them: https://github.com/dimsemenov/PhotoSwipe/pull/1178 https://github.com/dimsemenov/PhotoSwipe/pull/1179
It's looking like he's taking a well-deserved holiday (no facebook/twitter/github activity). Hopefully he's having a great time!
The feature to dynamically specify dimensions will unlikely be added to core. I specially do not remove PRs related to it, so people can use them as a start if needed. As stated in FAQ: it's recommended to find a way to retrieve and store dimensions, or use another gallery script that is more suitable for such task.
@isapir, The same goes for "moving" animation when switching slides. There is a performance issue when trying to move large images on a weak PCs, as well as UX issue - a large part of users dislike very large and long moving transition - they just want to view photos.
Though the "fading" transition is currently in development, as it's much more visually elegant and you can pre-render the next view for ~60ms, you can play with a prototype here http://codepen.io/dimsemenov/pen/zNNaNy .
Along with the feature that allows opening in a zoomed state & panning with mouse movement http://codepen.io/dimsemenov/pen/jrpwEZ
@dimsemenov Why not making other version/branch that is more advanced (with calculating dimensions, better animations, even with defined gallery html) so it would be as easy as other lightboxes. If someone is performace geek he would use this library - everyone else new branch/library?
Your library is very good but very complicated to use right away...
Why not making other version/branch that is more advanced (with calculating dimensions, better animations, even with defined gallery html)
I don't have resources to maintain two separate versions of the script.
Your library is very good but very complicated to use right away...
I agree with that, that's why there will be a module (or maybe a separate file) that would allow easier initialization directly from a list of thumbnails, take a look at what markup is required for this example - http://codepen.io/dimsemenov/pen/zNNaNy Though it's still in development.
Oh thats great. Will the new module has also embed html of gallery? Whe do You plan to release? Should I wait or search for other lightbox?
And why not adding official module for dynamic diamentions?
That's fine by me. I just don't want to create a PR that will be forever lost in GithubSpace.
Also, I've concluded that for my projects I will use @dimsemenov 's Royal Slider (http://dimsemenov.com/plugins/royal-slider/). Seems to be well worth its affordable price.
I'd love to see a feature comparison between the two; $WORK are generally OK to pay for stuff but I'd have to be able to write up why we should use the paid one over the free one.
@dimsemenov When You plan to release new version?