dimsemenov/PhotoSwipe

v5-beta - Nicely working native-like fullscreen button

Opened this issue · 21 comments

This issue is related to #1749 and the fact that native fullscreen button will likely be removed.


Hi all,

I am so glad to see PhotoSwipe in beta-development for a shining new v5 that I couldn't wait for using it!

Thank to the PhotoSwipe 5.0 API and Documentation, I easily managed to add a full-screen button, so I wanted to share the solution with you and maybe get review for improving what I have done...

I created a simple SVG icon that contains both the toggle full-screen mode icon path and exit full-screen mode icon path:

<svg aria-hidden="true" class="pswp__icn" viewBox="0 0 32 32" width="32" height="32">
  <!-- duplicate the paths for adding strokes -->
  <use class="pswp__icn-shadow" xlink:href="#pswp__icn-fullscreen-exit"/>
  <use class="pswp__icn-shadow" xlink:href="#pswp__icn-fullscreen-request"/>
  <!-- toggle full-screen mode icon path (id="pswp__icn-fullscreen-request") -->
  <path d="M8 8v6.047h2.834v-3.213h3.213V8h-3.213zm9.953 0v2.834h3.213v3.213H24V8h-2.834zM8 17.953V24h6.047v-2.834h-3.213v-3.213zm13.166 0v3.213h-3.213V24H24v-6.047z" id="pswp__icn-fullscreen-request"/>
  <!-- exit full-screen mode icon path (id="pswp__icn-fullscreen-exit") -->
  <path d="M11.213 8v3.213H8v2.834h6.047V8zm6.74 0v6.047H24v-2.834h-3.213V8zM8 17.953v2.834h3.213V24h2.834v-6.047h-2.834zm9.953 0V24h2.834v-3.213H24v-2.834h-3.213z" id="pswp__icn-fullscreen-exit" style="display:none"/>
</svg>

Solution

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

// Simple fullscreen API, stolen from v5 documentation (Native fullscreen on open)
const fullscreenAPI = getFullscreenAPI();

// Simple fullscreen SVG icon
const fullscreenSVG = '<svg aria-hidden="true" class="pswp__icn" viewBox="0 0 32 32" width="32" height="32"><use class="pswp__icn-shadow" xlink:href="#pswp__icn-fullscreen-exit"/><use class="pswp__icn-shadow" xlink:href="#pswp__icn-fullscreen-request"/><path d="M8 8v6.047h2.834v-3.213h3.213V8h-3.213zm9.953 0v2.834h3.213v3.213H24V8h-2.834zM8 17.953V24h6.047v-2.834h-3.213v-3.213zm13.166 0v3.213h-3.213V24H24v-6.047z" id="pswp__icn-fullscreen-request"/><path d="M11.213 8v3.213H8v2.834h6.047V8zm6.74 0v6.047H24v-2.834h-3.213V8zM8 17.953v2.834h3.213V24h2.834v-6.047h-2.834zm9.953 0V24h2.834v-3.213H24v-2.834h-3.213z" id="pswp__icn-fullscreen-exit" style="display:none"/></svg>';

const options = {
  gallerySelector: '.gallery',
  childSelector: 'a',
  pswpModule: '/v5/photoswipe/photoswipe.esm.js',
  pswpCSS: '/v5/photoswipe/photoswipe.css'
};

const lightbox = new PhotoSwipeLightbox(options);

lightbox.on('uiRegister', function() {
  lightbox.pswp.ui.registerElement({
    name: 'fullscreen-button',
    title: 'Toggle fullscreen',
    order: 9,
    isButton: true,
    html: fullscreenSVG,
    onClick: (event, el) => {
      toggleFullscreen();
    }
  });
});

// Exit full-screen mode when closing the lightbox
lightbox.on('close', () => {
  if (fullscreenAPI && fullscreenAPI.isFullscreen()) {
    fullscreenAPI.exit();
  }
});

lightbox.init();

// Simple fullscreen API helper, stolen from v5 documentation (Native fullscreen on open)
function getFullscreenAPI() {
  let api;
  let enterFS;
  let exitFS;
  let elementFS;
  let changeEvent;
  let errorEvent;
  if (document.documentElement.requestFullscreen) {
    enterFS = 'requestFullscreen';
    exitFS = 'exitFullscreen';
    elementFS = 'fullscreenElement';
    changeEvent = 'fullscreenchange';
    errorEvent = 'fullscreenerror';
  } else if (document.documentElement.webkitRequestFullscreen) {
    enterFS = 'webkitRequestFullscreen';
    exitFS = 'webkitExitFullscreen';
    elementFS = 'webkitFullscreenElement';
    changeEvent = 'webkitfullscreenchange';
    errorEvent = 'webkitfullscreenerror';
  }
  if (enterFS) {
    api = {
      request: function (el) {
        if (enterFS === 'webkitRequestFullscreen') {
          el[enterFS](Element.ALLOW_KEYBOARD_INPUT);
        } else {
          el[enterFS]();
        }
      },
      exit: function () {
        return document[exitFS]();
      },
      isFullscreen: function () {
        return document[elementFS];
      },
      change: changeEvent,
      error: errorEvent
    };
  }
  return api;
};

// Toggle full-screen mode function
function toggleFullscreen() {
  if (fullscreenAPI) {
    if (fullscreenAPI.isFullscreen()) {
      // Exit full-screen mode
      fullscreenAPI.exit();
      // Toggle "Exit" and "Enter" full-screen SVG icon display
      setTimeout(function() {
        document.getElementById('pswp__icn-fullscreen-exit').style.display = 'none';
        document.getElementById('pswp__icn-fullscreen-request').style.display = 'inline';
      }, 300);
    } else {
      // Enter full-screen mode
      fullscreenAPI.request(document.querySelector(`.pswp`));
      // Toggle "Exit" and "Enter" full-screen SVG icon display
      setTimeout(function() {
        document.getElementById('pswp__icn-fullscreen-exit').style.display = 'inline';
        document.getElementById('pswp__icn-fullscreen-request').style.display = 'none';
      }, 300);
    }
  }
}

Screenshots

Enter fullscreen SVG icon:
image

Exit fullscreen SVG icon:
image

Example gallery

You can see this working on my portfolio
(Caution: Very large gallery displaying 1300+ images, I am sorry)

Reference

Conclusion

The current rewriting of PhotoSwipe adds a lot of possibilities and I really like the fact that it does not grow heavy with lots of pre-chewed features but rather allows you to easily add what you want yourself (captions, buttons, etc.) through its API.

However, I still think that the fullscreen button is one of the fundamentals in lightboxes and could be integrated by default.
Of course my SVG icon is free to use and edit, and I would be happy if that could save integration time.
I did it a bit quickly so maybe it's too bold, it's based on the close icon weight... I will make two lighter alternatives later.

Thank you @dimsemenov for this great work!

Hi @BilouMaster,

thanks for your nice implementation of a fullscreen button!

I did add it to my jAlbum PhotoSwipe 5 test album.
Because the fullscreen function is not supported on an iPhone, I hide the fullsceeen button on an iPhone:

lightbox.on('change', () => {
    checkFullscreenBtn();
});

function checkFullscreenBtn() {
    const x = document.querySelector('#pswp__icn-fullscreen-request');
    if (iPhone) {
        x.style.display = "none";
    }
}

It works correctly on an iPad, but only with Safari, not with Chrome or Firefox.

Thanks @BilouMaster, for supplying this code.

Hm, I haven't checked on iPhone, you are right, it's not supported.

A solution may be to wrap the "uiRegister" part with an if statement like:

if (fullscreenAPI) {
  lightbox.on('uiRegister', function() {
    lightbox.pswp.ui.registerElement({
      name: 'fullscreen-button',
      title: 'Toggle fullscreen',
      order: 9,
      isButton: true,
      html: fullscreenSVG,
      onClick: (event, el) => {
        toggleFullscreen();
      }
    });
  });
}

This way the button won't be created when fullscreen is unsupported... I need to make a friend that have an iPhone now. 😅

Yes BilouMaster your proposal to solve this is much better:

It hides the fullscreen button on my iPhone.
On my iPad it shows the button in Safari, which is correct.
It does not show the button in Chrome on an iPad, which is also correct.
It shows the button in Firefox on my iPad, which is incorrect, because the fullscreen function is not working there.

Thanks again for your excellent work!

It shows the button in Firefox on my iPad, which is incorrect, because the fullscreen function is not working there

Ok then maybe we should use document.fullscreenEnabled instead of document.documentElement.requestFullscreen in the first if statement of the fullscreen API part:

// Simple fullscreen API helper, stolen from v5 documentation (Native fullscreen on open)
function getFullscreenAPI() {
  let api;
  let enterFS;
  let exitFS;
  let elementFS;
  let changeEvent;
  let errorEvent;
  if (document.fullscreenEnabled) {
    enterFS = 'requestFullscreen';
    exitFS = 'exitFullscreen';
    elementFS = 'fullscreenElement';
    changeEvent = 'fullscreenchange';
    errorEvent = 'fullscreenerror';
  } else if (document.webkitFullscreenEnabled) {
    enterFS = 'webkitRequestFullscreen';
    exitFS = 'webkitExitFullscreen';
    elementFS = 'webkitFullscreenElement';
    changeEvent = 'webkitfullscreenchange';
    errorEvent = 'webkitfullscreenerror';
  }
  if (enterFS) {
    api = {
      request: function (el) {
        if (enterFS === 'webkitRequestFullscreen') {
          el[enterFS](Element.ALLOW_KEYBOARD_INPUT);
        } else {
          el[enterFS]();
        }
      },
      exit: function () {
        return document[exitFS]();
      },
      isFullscreen: function () {
        return document[elementFS];
      },
      change: changeEvent,
      error: errorEvent
    };
  }
  return api;
};

Js is not my cup of tea so thanks a lot for the feedback!

It shows the button in Firefox on my iPad, which is incorrect, because the fullscreen function is not working there

Ok then maybe we should use document.fullscreenEnabled instead of document.documentElement.requestFullscreen in the first if statement of the fullscreen API part:

That code works too on my iPhone and iPad, but with Firefox on my iPad it still shows the fullscreen button, which is incorrect.
But I should not bother about that, Firefoix is seldom used on an iPad I think.
It makes no difference with respect to your original code, so I keep the old code.

Js is not my cup of tea

Well, you are an absolute expert with respect to mine knowledge of js!

Hi Joke BilouMaster,

For your information:

I did use already a fullscreen button for the thumbnails page, as you can see here.
I did use the GitHub screenfull.js library which contains about the same code as you give here.

To make things smaller, I use now also that code for the fullscreen button in the lightbox , what is left over is this code:

    if (screenfull.isEnabled) {  
      lightbox.pswp.ui.registerElement({
          name: 'fullscreen-button',
          title: 'Toggle fullscreen',
          order: 7,
          isButton: true,
          html: fullscreenSVG,
          onClick: (event, el) => {
             fullScreenToggle();
          }
      });
    }

// Simple fullscreen SVG icon
const fullscreenSVG = '<svg aria-hidden="true" class="pswp__icn" viewBox="0 0 32 32" width="32" height="32"><use class="pswp__icn-shadow" xlink:href="#pswp__icn-fullscreen-exit"/><use class="pswp__icn-shadow" xlink:href="#pswp__icn-fullscreen-request"/><path d="M8 8v6.047h2.834v-3.213h3.213V8h-3.213zm9.953 0v2.834h3.213v3.213H24V8h-2.834zM8 17.953V24h6.047v-2.834h-3.213v-3.213zm13.166 0v3.213h-3.213V24H24v-6.047z" id="pswp__icn-fullscreen-request"/><path d="M11.213 8v3.213H8v2.834h6.047V8zm6.74 0v6.047H24v-2.834h-3.213V8zM8 17.953v2.834h3.213V24h2.834v-6.047h-2.834zm9.953 0V24h2.834v-3.213H24v-2.834h-3.213z" id="pswp__icn-fullscreen-exit" style="display:none"/></svg>';

// Exit full-screen mode when closing the lightbox
lightbox.on('close', () => {
  if ((screenfull.isEnabled) && (screenfull.isFullscreen)) {
    screenfull.exit();
  }
});

Swapping the FS-icons is now done as follows:

 screenfull.on('change', fullscreenchange);  // in  document.ready function

function fullscreenchange() {
    if (buttonsVisible) {  // true if  a fullscreen button is used in the thumbnails page or in the  lightbox
        if (screenfull.isEnabled) {
            if (screenfull.isFullscreen) {
                // Replace FS button by noFS button
                if (lightboxIsOpen() && showFsButton) {  // showFsButton is true if the lightbox shows a fullscreen button
                    document.getElementById('pswp__icn-fullscreen-exit').style.display = 'inline';
                    document.getElementById('pswp__icn-fullscreen-request').style.display = 'none';
                } else {
                    document.images['fs'].src = noFS_img.src  // swap image in thumbnails page
                }
            } else {
                // Replace noFS button by FS button
                if (lightboxIsOpen() && showFsButton) {
                    document.getElementById('pswp__icn-fullscreen-exit').style.display = 'none';
                    document.getElementById('pswp__icn-fullscreen-request').style.display = 'inline';
                } else {
                    document.images['fs'].src = FS_img.src;
                }
            }
        }
    }
}

I removed your setTimeout(function(), because I don't think it is required in this implementation.

You can see the result here.
This version shows no fullscreen button with Firefox on my iPad, which is correctly, because the fullscreen function is there not supported.

Thanks again for using your code!

Regards,

André Wolff

If anyone is interested in that: I created a plugin based on the ideas described here.

See https://github.com/arnowelzel/photoswipe-fullscreen

This is also used in my plugin Lightbox with PhotoSwipe for WordPress.

See examples here:

https://wordpress-demo.arnowelzel.de/lightbox-with-photoswipe-5/
https://wordpress-demo.arnowelzel.de/lightbox-with-photoswipe-5-overlay-caption/

@arnowelzel I use the code decribed in #1759 and this works fine as you can see in my test album.
So why should I use your plugin? What are the advantages?

I did test your example album on an iPad: I see a fullscreen button with Google Chrome, but clicking on that button has no effect. With Safari, it works as expected.

If I open my test album on an iPad, the full screen button works as expected with Safari. Google Crome and Firefox shows in my implementation no fullscreen button.

I like to test your plugin not in Wordpress, but in a normal webpage. Could you please give an example album of your plugin for a normal webpage?

@acwolff Using the plugin may be easier - just import one file and create the plugin:

<script type="module">
import PhotoSwipeLightbox from 'photoswipe/dist/photoswipe-lightbox.esm.min.js';
import PhotoSwipeFullscreen from 'photoswipe-fullscreen/photoswipe-fullscreen.esm.min.js';

const lightbox = new PhotoSwipeLightbox({
  gallerySelector: '#gallery',
  childSelector: '.pswp-gallery__item',
  pswpModule: () => import('photoswipe/dist/photoswipe.esm.js'),
});

const fullscreenPlugin = new PhotoSwipeFullscreen(lightbox);

// make sure you init photoswipe core after plugins are added
lightbox.init();
</script>

Furthermore I will update the plugin if needed as I use it for my WordPress plugin as well (current user base is more than 20.000 installations - if anything might be wrong I will get feedback very quick). So you don't have to take care of keeping a "manual" fullscreen solution functional when PhotoSwipe itself gets updated.

Edit: My plugin also supports using the keyboard shortcut [F] to enter fullscreen mode (similar to YouTube etc.) and I also refined the fullscreen icon a bit to better match the other icons.

But of course it's your decision - if you feel happy with the manual solution you don't need to change.

Edit 2: the bug showing a fullscreen button even if fullscreen mode is not possible was fixed with release 1.0.1 of my fullscreen plugni.

Due to an issue I had to investigate I also created a sample web page showing the use of the fullscreen plugin in HTML without WordPress:

https://arnowelzel.de/samples/photoswipe5

Just make sure, that you use the latest version (1.0.3) which fixes the issue that PhotoSwipe may trigger init and uiRegister multiple times after the lightbox was closed and is opening again when clicking a linked image again.

This does not work on an iPad, it shows no full screen button

This does not work on an iPad, it shows no full screen button

Safari (or any other browser as well) on iPad does not support fullscreen API.

Edit: the fullscreen button is only added if the browser supports the fullscreen API. If the API is not supported, it makes no sense to display a button which would not do anything at all anyway.

@arnowelzel as you can see in this album, the implementation of @BilouMaster works on an iPad with Safari.

It works only with Safari, not with Chrome or Firefox on an iPad.

@arnowelzel as you can see in this album, the implementation of @BilouMaster works on an iPad with Safari.

It works only with Safari, not with Chrome or Firefox on an iPad.

I used exactly his implementation - just compare the code:

My version: https://github.com/arnowelzel/photoswipe-fullscreen/blob/1.0.3/photoswipe-fullscreen.esm.js#L91-L132

Implementation of @BilouMaster : #1759 (comment)

So - what should I change? I have no idea at all.

I also don't have an iPad to test. But at least on an current iPhone SE fullscreen does not work either - also not on https://andrewolff.jalbum.net/PS_JustifiedGallery/ - no fullscreen button at all.

Anyway - pull requests to https://github.com/arnowelzel/photoswipe-fullscreen/ are welcome.

Ok, I see one difference:

document.documentElement.requestFullscreen
document.documentElement.webkitRequestFullscreen

vs:

document.fullscreenEnabled
document.webkitFullscreenEnabled

I'll change that later and let you know when you can test it.

@acwolff I updated the code for the example at https://arnowelzel.de/samples/photoswipe5/ - can you please check if it works on an iPad as well now?

Yes I see now the full screen button in Safari on my iPad and that button works correctly, well done!

Thanks for testing and the feedback!

JFTR: the changes are also part of the official release of https://github.com/arnowelzel/photoswipe-fullscreen/

JFTR: the changes are also part of the official release of https://github.com/arnowelzel/photoswipe-fullscreen/

can u pls tell me how i can use your plugin in react?

Sorry, I don't have any experience with React and JSX so far. In general just follow the documentation at https://github.com/arnowelzel/photoswipe-fullscreen/?tab=readme-ov-file#using-the-plugin. Of course you have to provide the files for PhotoSwipe and the fullscreen plugin.