dimsemenov / PhotoSwipe

JavaScript image gallery for mobile and desktop, modular, framework independent
http://photoswipe.com
MIT License
24.29k stars 3.31k forks source link

V5-Beta Video support #1780

Closed acwolff closed 3 years ago

acwolff commented 3 years ago

With the previous version I could implement video with the html code:

   var items = [
  { 
    fn: 'IMG_0877.mp4',
    videosrc: 'slides/IMG_0877.mp4',
    vw: 608,
    vh: 1080, 
    html: '<video controls poster="slides/IMG_0877.jpg" ><source src="slides/IMG_0877.mp4" type="video/mp4"></video>',
    title:   ''  
  },

This worked OK, to see it click on the first thumbnail of this album.

I try now to port this to PhotoSwipe 5, but not in n array but in html code:

<div id="gallery" class="thumbs-jg thumbsgallery" >
<a href="slides/IMG_0877.mp4" data-pswp-width="608" data-pswp-height="1080"
data-pswp-html='<video controls poster="slides/IMG_0877.jpg" ><source src="slides/IMG_0877.mp4" type="video/mp4"> 
</video>' data-caption=''>
  <img src="slides/IMG_0877.jpg" class="video" width="45" height="80" alt="video" title="video" />
</a>

This does not work, see this test

So I have three questions:

  1. Is this code correct, is attribute data-pswp-html a supported attribute?
  2. If not how can I add html code in this type of gallery implementation?
  3. Is there a better way to support a video file in PhotoSwipe 5?
dimsemenov commented 3 years ago

You can do something like:

const lightbox = new PhotoSwipeLightbox({
 // ...
});

lightbox.on('itemData', (e) => {
  const element = e.itemData.element;

  if (element  && element.dataset.pswpIsVideo) {
    const videoURL = element.href;
    e.itemData = {
      html: `<video controls><source src="${videoURL}" type="video/mp4"> </video>`
    };
  }
});

lightbox.init();
<div id="gallery">
    <a href="slides/IMG_0877.mp4" data-pswp-is-video="true">
      <img src="slides/IMG_0877.jpg" alt="video" />
    </a>
</a>

There might be a plugin for video in future.

mallardduck commented 3 years ago

@dimsemenov - this looks good - however I wonder how one should adjust the wrapper that gets created around the itemData.html that's provided? The pswp__zoom-wrap element that's created has inline styles and it'd be nice to adjust the transform3d value to center the video.

dimsemenov commented 3 years ago

@mallardduck If html slide is used, pswp__zoom-wrap should only have styles that stretch it to 100% width/height It isn't for you?

Custom HTML within a slide should be centered and scaled manually via CSS or JS - up to you.

mallardduck commented 3 years ago

Cool - good to know that makes enough sense.

acwolff commented 3 years ago

@dimsemenov thanks for providing example video code.

Your example works at my place as you can see in my test album.

Thanks for your help!

acwolff commented 3 years ago

For mobile devices it was rquired to add a poster image, so I adapted the code:

<div id="gallery">
    <a href="slides/IMG_0877.mp4" data-pswp-is-video="true" data-pswp-is-poster="slides/IMG_0205.jpg" >
      <img src="slides/IMG_0877.jpg" alt="video" />
    </a>
</a>

lightbox.on('change', () => {
     showZoomBtn(lightbox.pswp.currSlide.isZoomable());
});
lightbox.on('itemData', (e) => {
   const element = e.itemData.element;
   if (element  && element.dataset.pswpIsVideo) {
     const videoURL = element.href;
      const imgPoster= element.dataset.pswpIsPoster;
     e.itemData = {
       html: `<video controls  poster="${imgPoster}"><source src="${videoURL}" type="video/mp4"> </video>`
     };
   }
});
lightbox.init();

As you see I hide the zoom button for videos.

I use now the following CSS code for a video:

video {
    position: relative;
    top: 50%;
    transform: translateY(-50%);
    height: 100%;
}

You see the reslut in my video test album.

acwolff commented 3 years ago

I added too support for YouTube and Vimeo videos and weblocations:

<div id="gallery">
    <a href="slides/IMG_0877.mp4" data-pswp-is-video="true" data-pswp-is-poster="slides/IMG_0205.jpg" >
      <img src="slides/IMG_0877.jpg" class="video" />
    </a>
    <a href="https://www.youtube.com/embed/QbULyApj9Sw" data-pswp-is-webloc="true" >   
      <img src="slides/www.youtube.com-178.jpg" class="video" />
    </a>
    <a href="https://www.andrewolff.nl/SkinsManual/" data-pswp-is-webloc="true"  >
      <img src="thumbs/www.andrewolff.nl-780.jpg"   />
    </a>
</div>

lightbox.on('itemData', (e) => {
   const element = e.itemData.element;
   if (element  && element.dataset.pswpIsVideo) {
     const videoURL = element.href;
     const imgPoster= element.dataset.pswpIsPoster;
     e.itemData = {
       html: `<video controls  poster="${imgPoster}"><source src="${videoURL}" type="video/mp4"> </video>`
     };
   }
   else if (element  && element.dataset.pswpIsWebloc) {
     const weblocURL = element.href;
     e.itemData = {
       html: `<iframe frameborder="0" style=border:"0" src="${weblocURL}" height="100%" width="100%"> </iframe>`
     };
   }

You see the reslut in my video test album.

mallardduck commented 3 years ago

@acwolff / @dimsemenov - do either of you have an idea on how to combine this technique for videos with the dynamic generation via API? I have an api based gallery working but only for images not videos.

I have to use XMLHttpRequest with a synchronous request as I found async requests didn't work. I parse the JSON the API provides then assign the following things for an image:

itemData.msrc = '{{ asset('placeholder.png') }}';
itemData.src = parsed.data.src;
itemData.w = parsed.data.w;
itemData.h = parsed.data.h;

While these techniques for video seemed to work for when the info is embedded into the clicked element. It seems like using the html attribute in that context doesn't work. I just opted to add an if/else to check for the value of an is_video property the API exposes.

UPDATE: Sorry - after some further testing I needed to correct my logic around testing the video status. I wasn't properly setting the html attribute for those cases. Once that was things work as expected.

dimsemenov commented 3 years ago

@mallardduck Not sure if I fully understand your question. There is no option to have both video (or any HTML) and zoomable image at the same time on the same slide. At least for now.

To add a video to the slide - all you have to do is define itemData.html with your video markup. If your data is asynchronous, you may set it to some placeholder HTML and then replace it once the data is loaded.