1

SITUATION

I have been trying to trigger the 'slide to next picture' animation when the NEXT button is clicked, but i have not found a solution for this.

There is an ongoing discussion about this on GitHub, but it is only about adding the option for a slide animation, not about how to actually do it with PS as it is right now.

There was an option for it in 3.0 but as 4.0 is a complete rewrite it does not work anymore.

QUESTION

Instead of just 'jumping' to the next/prev picture when an arrow is clicked, i need the 'slide transition' that is also used when swiping/dragging the image.

There is no option to trigger that, so how can i manually trigger this effect with JS?

Larzan
  • 9,389
  • 3
  • 42
  • 41

4 Answers4

3

PhotoSwipe Slide Transitions
So, I added slide transitions to Photoswipe, and it's working nicely without disturbing native behavior. http://codepen.io/mjau-mjau/full/XbqBbp/ http://codepen.io/mjau-mjau/pen/XbqBbp

The only limitation is that transition will not be applied between seams in loop mode (for example when looping from last slide to slide 1). In examples I have used jQuery.

Essentially, it works by simply adding a CSS transition class to the .pswp__container on demand, but we need to add some javascript events to prevent the transition from interfering with Swipe, and only if mouseUsed. We also add a patch so the transition does not get added between loop seams.

1. Add the below to your CSS
It will be applied on-demand from javascript when required.

.pswp__container_transition {
  -webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
  transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
}

2. Add javascript events to assist in assigning the transition class This can go anywhere, but must be triggered after jQuery is loaded.

var mouseUsed = false;
$('body').on('mousedown', '.pswp__scroll-wrap', function(event) {
  // On mousedown, temporarily remove the transition class in preparation for swipe.     $(this).children('.pswp__container_transition').removeClass('pswp__container_transition');
}).on('mousedown', '.pswp__button--arrow--left, .pswp__button--arrow--right', function(event) {
  // Exlude navigation arrows from the above event.
  event.stopPropagation();
}).on('mousemove.detect', function(event) {
  // Detect mouseUsed before as early as possible to feed PhotoSwipe
  mouseUsed = true;
  $('body').off('mousemove.detect');
});

3. Add beforeChange listener to re-assign transition class on photoswipe init
The below needs to be added in your PhotoSwipe init logic.

// Create your photoswipe gallery element as usual
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);

// Transition Manager function (triggers only on mouseUsed)
function transitionManager() {

  // Create var to store slide index
  var currentSlide = options.index;

  // Listen for photoswipe change event to re-apply transition class
  gallery.listen('beforeChange', function() {

    // Only apply transition class if difference between last and next slide is < 2
    // If difference > 1, it means we are at the loop seam.
    var transition = Math.abs(gallery.getCurrentIndex()-currentSlide) < 2;

    // Apply transition class depending on above
    $('.pswp__container').toggleClass('pswp__container_transition', transition);

    // Update currentSlide
    currentSlide = gallery.getCurrentIndex();
  });
}

// Only apply transition manager functionality if mouse
if(mouseUsed) {
  transitionManager();
} else {
  gallery.listen('mouseUsed', function(){
    mouseUsed = true;
    transitionManager();
  });
}

// init your gallery per usual
gallery.init();
suncat100
  • 2,118
  • 1
  • 17
  • 22
  • I don't understand the problem that you have with the original solution, do you have an example of a situation where it doesn't work? – Larzan Jul 11 '15 at 17:27
  • To be honest, I didn't try it, but it seems slightly more hacky. Essentially, my solution is just CSS, and then some JS to prevent corruption of 'swipe' mechanism. Also, it doesn't replace the UI or override photoSwipe in any way. I can't see that your solution deals with arrow key-press either, and are you sure it doesn't interfere with swipe/drag? I would have liked to try a demo on codepen ... Sorry, I'm sure your solution is good also! I was just poking around, and saw that transition could easily be added to photoSwipe simply with a CSS transition class, so I decided to go with that. – suncat100 Jul 12 '15 at 13:00
  • Well, then you shouldn't say sth. doesn't work if you haven't even tested it. You are right that an example is missing, but still. Also i don't use the arrow keys, correct, your implementation tries to do that (even though it does not work either when looping last-slide > first-slide). I might add key support later. – Larzan Jul 13 '15 at 11:14
  • Say sth? Don't know what that means, but sorry you have to take it personal. I made an improved solution simply because I saw it could be solved with a CSS transition, instead of just hacking the arrow buttons like you have done. – suncat100 Jul 13 '15 at 15:30
  • :) i was just curious because in the github thread you implied that the presented solution did not work in some cases, glad to hear that you just didn't test it and assumed... and yes, your solution seems to be cleaner and has key support. if you could fix the last-slide > first-slide bug it would probably be the better one. – Larzan Jul 13 '15 at 17:36
  • @suncat100 Where you currently disable the transition at a loop-seam, it would be cool to provide something alternative instead. Like either adjusting the animation speed (2nd transition class with different speed) or doing something like a fade in/out effect instead. – Berend Jun 21 '16 at 04:06
2

You can just use a css transition:

.pswp__container{ transition:.3s ease-in-out all; }

This might not be ideal for performance on mobile, but I just add this transition in a media query and allow users to use the swipe functionality on smaller screens.

tekstar
  • 21
  • 2
1

I finally bit the bullet and spent some time making this work as nobody seemed to have a solution for that, not here neither on GitHub or anywhere else.

SOLUTION

I used the fact that a click on the arrow jumps to the next item and triggers the loading of the next image and sets the whole slide state to represent the correct situation in an instant. So i just added custom buttons which would initiate a slide transition and then triggered a click on the original buttons (which i hid via CSS) which would update the slide state to represent the situation i created visually.

  1. Added NEW next and prev arrows
  2. Hid the ORIGINAL next and prev arrows via css
  3. Animated the slide myself when the NEW next or prev arrows were clicked
  4. Then triggered the click on the ORIGINAL next or prev arrows programmatically

So here is the code:

HTML

// THE NEW BUTTONS
<button class="NEW-button-left" title="Previous (arrow left)">PREV</button>
<button class="NEW-button-right" title="Next (arrow right)">NEXT</button>

// added right before this original lines of the example code
<button class="pswp__button pswp__button--arrow--left ...

CSS

pswp__button--arrow--left,
pswp__button--arrow--right {
  display: none;
}
NEW-button-left,
NEW-button-right {
  /* whatever you fancy */
}

JAVASCRIPT (helper functions)

var tx = 0; // current translation
var tdir = 0;
var slidepseactive = false;

// helper function to get current translate3d positions 
// as per https://stackoverflow.com/a/7982594/826194
function getTransform(el) {
  var results = $(el).css('-webkit-transform').match(/matrix(?:(3d)\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))(?:, (-{0,1}\d+)), -{0,1}\d+\)|\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))\))/)
  if(!results) return [0, 0, 0];
  if(results[1] == '3d') return results.slice(2,5);
  results.push(0);
  return results.slice(5, 8);
}

// set the translate x position of an element
function translate3dX($e, x) {
  $e.css({
    // TODO: depending on the browser we need one of those, for now just chrome
    //'-webkit-transform': 'translate3d(' +String(x) + 'px, 0px, 0px)'
    //, '-moz-transform': 'translate3d(' +String(x) + 'px, 0px, 0px)'
    'transform': 'translate3d(' +String(x) + 'px, 0px, 0px)'
  });
};

JAVASCRIPT (main)

// will slide to the left or to the right
function slidePS(direction) {
  if (slidepseactive) // prevent interruptions
    return;

  tdir = -1;
  if (direction == "left") {
    tdir = 1;
  }

  // get the current slides transition position
  var t = getTransform(".pswp__container");
  tx = parseInt(t[0]);

  // reset anim counter (you can use any property as anim counter)
  $(".pswp__container").css("text-indent", "0px");

  slidepseactive = true;
  $(".pswp__container").animate(
    {textIndent: 100},{
      step: function (now, fx) {
        // here 8.7 is the no. of pixels we move per animation step %
        // so in this case we slide a total of 870px, depends on your setup
        // you might want to use a percentage value, in this case it was
        // a popup thats why it is a a fixed value per step
        translate3dX($(this), tx + Math.round(8.7 * now * tdir));
      },
      duration: '300ms',
      done: function () {
        // now that we finished sliding trigger the original buttons so 
        // that the photoswipe state reflects the new situation
        slidepseactive = false;
        if (tdir == -1)
          $(".pswp__button--arrow--right").trigger("click");
        else
          $(".pswp__button--arrow--left").trigger("click");
      }
    },
    'linear');
}

// now activate our buttons
$(function(){

  $(".NEW-button-left").click(function(){
    slidePS("left");
  });

  $(".NEW-button-right").click(function(){
    slidePS("right");
 });

});

I used info from those SE answers:

Community
  • 1
  • 1
Larzan
  • 9,389
  • 3
  • 42
  • 41
1

The PhotoSwipe can do this by itself when you use swipe gesture. So why not to use the internal code instead of something that doesn't work well?

With my solution everything works well, the arrow clicks, the cursor keys and even the loop back at the end and it doesn't break anything.

Simply edit the photoswipe.js file and replace the goTo function with this code:

    goTo: function(index) {
    var itemsDiff;

    if (index == _currentItemIndex + 1) { //Next
        itemsDiff = 1;
    }
    else { //Prev
        itemsDiff = -1;
    }

    var itemChanged;
    if(!_mainScrollAnimating) {
        _currZoomedItemIndex = _currentItemIndex;
    }

    var nextCircle;

    _currentItemIndex += itemsDiff;

    if(_currentItemIndex < 0) {
        _currentItemIndex = _options.loop ? _getNumItems()-1 : 0;
        nextCircle = true;
    } else if(_currentItemIndex >= _getNumItems()) {
        _currentItemIndex = _options.loop ? 0 : _getNumItems()-1;
        nextCircle = true;
    }

    if(!nextCircle || _options.loop) {
        _indexDiff += itemsDiff;
        _currPositionIndex -= itemsDiff;
        itemChanged = true;
    }

    var animateToX = _slideSize.x * _currPositionIndex;
    var animateToDist = Math.abs( animateToX - _mainScrollPos.x );
    var finishAnimDuration = 333; 

    if(_currZoomedItemIndex === _currentItemIndex) {
        itemChanged = false;
    }

    _mainScrollAnimating = true;

    _shout('mainScrollAnimStart');

    _animateProp('mainScroll', _mainScrollPos.x, animateToX, finishAnimDuration, framework.easing.cubic.out, 
        _moveMainScroll,
        function() {
            _stopAllAnimations();
            _mainScrollAnimating = false;
            _currZoomedItemIndex = -1;

            if(itemChanged || _currZoomedItemIndex !== _currentItemIndex) {
                self.updateCurrItem();
            }

            _shout('mainScrollAnimComplete');
        }
    );

    if(itemChanged) {
        self.updateCurrItem(true);
    }

    return itemChanged;

},
Fenistil
  • 3,734
  • 1
  • 27
  • 31
  • I agree that the best solution would be to use some internal mechanism, but hacking around the photoswipe code directly is hardly best practice. – Larzan Dec 11 '17 at 10:58
  • Why not add this as a PR to photoswipe on github? Have an `if` statement that checks for an option, and if the option is true, have the `goTo` function use this code, otherwise have it use the original. – BryanGrezeszak Jun 12 '18 at 17:04
  • 1
    Fiddling the original code his way might not be elegant, but for me it's the first solution that works! Performance is great on my AMD-Phenom-powered (thus old) pc, as well as on iPad Pro. On my 2014 Android device, the transition is not really fluid. This could be the reason why the original developer of PhotoSwipe didn't make it in such a manner, but my pragmatic decision is that the modification gives a better experience for most of my photo viewers. – Hauke Dec 22 '19 at 16:38