Search code examples
javascriptjqueryphotoswipe

Implementing RTL on a slider plugin


After several requests ignored by the developer to support RTL, I'm trying to do it myself.

Reversing the array order was pretty straightforward:

self.items = !!(_options.rtl) ? _items = items.reverse() : _items = items; // Reverse the array for RTL

.. however the other challenge is swiping RTL. So swiping right from slide 4 (which is the first slide) should slide to the next slide (Slide 3).

enter image description here

All the magic happens in this function I believe, but I can't quite figure what to tweak:

_moveMainScroll = function(x, dragging) {

    if(!_options.loop && dragging) {

        if (_options.rtl) {
            console.log('RTL enabled');
            console.log('_currentItemIndex: ', _currentItemIndex);
            console.log('_slideSize.x: ', _slideSize.x);
            console.log('_currPositionIndex: ', _currPositionIndex);
            console.log('x: ', x);
            var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x) / _slideSize.x,
                delta = Math.round(x - _mainScrollPos.x);
            console.log(newSlideIndexOffset, delta);
        }
        else {
            var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x) / _slideSize.x,
            delta = Math.round(x - _mainScrollPos.x);
            console.log(newSlideIndexOffset, delta);
        }

        if( (newSlideIndexOffset < 0 && delta > 0) || (newSlideIndexOffset >= _getNumItems() - 1 && delta < 0) ) {
                x = _mainScrollPos.x + delta * _options.mainScrollEndFriction;
        } 
    }

    _mainScrollPos.x = x;
    _setTranslateX(x, _containerStyle);
}

Here's what my code so far: https://jsfiddle.net/tdx3p1p3/


Solution

  • If I understand correctly, you want to use PhotoSwipe the regular way, in particular passing your items array as usual (i.e. first slide at index 0), but have the gallery place this slide 0 at the "right-most" position, then slide 1 on its left, etc. and have the top left counter show the slide "index" (offset by 1 to avoid displaying "0") accordingly (therefore just using options.index: 3 is not enough).

    If this is correct, you would like the 2nd layout imagined by @e_i_pi:

                                ********
     Slide4 < Slide3 < Slide2 < *Slide1*
                                ********
    

    Therefore you need 3 things:

    1. Reverse your items array.
    2. Start at the right-most slide.
    3. Modify the UI counter so that the reported slide index is "reversed".

    You do not need to do these 3 operations by tweaking the code within PhotoSwipe script. You could just use a "wrapper" function that performs those operations, and still use the PhotoSwipe code unmodified (so that it is much easier for you to upgrade later on, and benefit from future improvements in the library).

    1. Reverse your items array:

    As you figured out, that is the simplest operation: items = items.reverse()

    2. Start at the right-most slide:

    As proposed by @e_i_pi, you just need to pass an index option that is equal to the new index (i.e. after reversal) of your "first" slide: index = items.length - 1

    It was initially at index 0, and after reversal it is now the "last" item.

    3. Reverse the UI counter:

    That is slightly more advanced, but nothing too complicated.

    As shown by PhotoSwipe's author, it is easy to customize the UI counter, but the gallery requires to be initialized first. Therefore, the "wrapper" function needs to initialize the gallery internally.

    Then logic to build the new UI counter content is not too complex. You just need to take the total number of items and subtract the current index in the items JS array:

                                ********
     Slide4 < Slide3 < Slide2 < *Slide1*
                                ********
        0        1       2         3     <= index in items JS array
        4        3       2         1     <= "index" to be shown in UI counter
    

    So you could have something like:

    var total = gallery.options.getNumItemsFn();
    var current = gallery.getCurrentIndex();
    var reversed = total - current;
    var separator = gallery.options.indexIndicatorSep;
    indexIndicatorDOMElement.innerHTML = reversed + separator + total;
    

    Put everything together, and you have a wrapper function that you can put in its own JS file, so that you can use it anywhere you wish.

    // Wrapper initialize function that does the RTL conversion automatically.
    // Note: it will also automatically initialize the PhotoSwipe, because it needs to be initialized for UI counter to be modified.
    function photoSwipeRTL(pswpElement, uiClass, items, options) {
      // 1. Reverse the items.
      items = items.reverse();
    
      // 2. Override the start index.
      var itemsIndexMax = items.length - 1;
      var index = options.index || 0; // If not provided, use 0.
      // Now reverse the start index.
      options.index = itemsIndexMax - index;
    
      // Now instantiate a PhotoSwipe and initialize it, so that we can modify its UI counter.
      var pswp = new PhotoSwipe(pswpElement, uiClass, items, options);
      pswp.init();
    
      // Get the counter element provided in options, or get it from DOM.
      var indexIndicatorDOMElement = options.indexIndicatorDOMElement || document.querySelectorAll('.pswp__counter')[0];
    
      // 3. Modify the UI counter.
      pswp.ui.updateIndexIndicator = function() {
        // This code reverses the current index compared to the total number of items in the gallery.
        var total = pswp.options.getNumItemsFn();
        var current = pswp.getCurrentIndex();
        var reversed = total - current;
        var separator = pswp.options.indexIndicatorSep;
        indexIndicatorDOMElement.innerHTML = reversed + separator + total;
      };
      // force index update
      pswp.ui.updateIndexIndicator();
    
      return pswp;
    }
    

    And you can use it very simply, almost like the standard PhotoSwipe, except that you just execute the factory (no need for new keyword), and no need to initialize the gallery, because it is already done internally.

    // INITIALIZE
    var pswpElement = document.querySelectorAll('.pswp')[0];
    
    // build items array
    var items = [slide1, slide2, slide3, slide4];
    
    // define options (if needed)
    var options = {
      indexIndicatorSep: ' of ',
      indexIndicatorDOMElement: document.querySelectorAll('.pswp__counter')[0]
    };
    
    // Initializes and opens PhotoSwipe
    var gallery = photoSwipeRTL(pswpElement, PhotoSwipeUI_Default, items, options);
    

    Live demo: https://plnkr.co/edit/kPeFeZbwhqHzbz8A9VRK?p=preview