Slim Slider: Yet another Javascript slider


Last months we have been busy @ Namshi, re-writing our new mobile site with a hybrid approach of Preact and Server Rendered Nodejs pages.(more on that on our blog soon, tech.namshi.com)

For our product page, We started to use an open-source slider, which was a very nice and did almost the job but we faced a major problem ! it doesn’t support RTL!

Obviously we need to serve our website, Namshi.com, RTL first, and it seemed that the aforementioned slider maintainer doesn’t have plans to support RTL, and with the market has no simple enough solutions, we had to write our own.

live example form namshi.com

HammerJS

We’re already using hammerjs in our codebase to deliver pinch-to-zoom features via PhotoViewJS. So we used hammer for panning recognition.

Rest is straight-forward

Clearly, it took sometime to make it works perfectly as we expected, though, we faced some tricky bits.

Structural direction

Css transform like translate3d doesn’t switch direction if you change the css property direction:rtl

the solution was simple actually, just negating the values using a global operator, which allowed the code to be very generic and abstracted.
like the below example of determining translate value.


let operator = (this.options.dir === 'rtl' ? 1 : -1);
let position = operator * current * width; // let's say 1000px?
translateTo(position) // rtl : 1000px and if ltr: -1000px
// css : translate(position)

Even better, we don’t have directions, imagine that in LTR next means goes right, so in RTL next means the other way around!
So taking inspiration of CSS logical properties the operator will be the deterministic factor for us, and thats all with one global setting with no need of case handling all over the place.

// you don't to have to worry about which is next and which is 
// right! in fact I don't know myself :)
if(e.type == 'panleft') {
this.slideTo(this.current - this.operator );
} else if( e.type == 'panright'){
this.slideTo(this.current + this.operator );
} else {
this.slideTo(this.current);
}

Talking to other plugins

PhotoViewJS, which handles the zooming on images, also depends on hammer and the issue is to decide, when to pan to slide and when to pan to see the zoomed image.

Solved this problem, by having a method on the slider that disables and enables the slider handler.

setPan(enabled){    
this.panEnabled = enabled;
this.initGesture();
}

which will be called inside an event propagated from PhotoViewJs itself

document.addEventListener('photoview.scale.changed', e => {
Slider.setPan(e.detail.scale === 1) ;
})

The other way around:
we initialize PhotoViewJs after the slider itself initialize, using events as well.

carouselElem.addEventListener('after.slim.init', (e) => {    
new PhotoView('.product-carousel-item', {tapToZoom: true});
})
// And after each slide too.
carouselElem.addEventListener('after.slim.slide', (e) => {
new PhotoView('.product-carousel-item', {tapToZoom: true});
})

conclusion

Here is the github repo : https://github.com/namshi/slim-slider
PRs and suggestions are welcomed! :)

Like what you read? Give Mohamed Amin a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.