Habituellement quand on demande à un développeur de faire du scroll jacking sur mobile nous avons tendance à dire NON. Mais pour une expérience de navigation pleine page, le fait d’être positionné parfaitement sur chaque page rend l’expérience plus esthétique.
Nous allons voir comment faire cela simplement, sans que l’expérience utilisateur ne soit altérée. Pour ça, il est important de conserver le mode contracté du navigateur mobile.
Normal vs Minimal
On peut voir que le navigateur est plus ou moins haut en fonction du scroll réalisé par l’utilisateur
Réalisation
L’exemple sera réalisé avec React mais pourrait être réalisé avec n’importe quelle framework JS ou même en vanilla.
Vous pouvez directement aller voir le code sur ce repo et voir le résultat sur votre mobile à cette adresse.
Nous allons créer un composant <Scroll /> qui nous permettra de gérer le scroll jacking.
Dans votre application React, créez la structure suivante:
src -- Scroll ---- index.css ---- index.js -- App.js -- App.css
Modifier le fichier App.js pour créer notre stucture
import React, { Component } from 'react'; import './App.css'; import Scroll from './Scroll'; class App extends Component { render() { return ( <divclassName="App"> <Scroll> <divclassName="content">hello</div> <divclassName="content">hello 2</div> <divclassName="content">hello 3</div> <divclassName="content">hello 4</div> <divclassName="content">hello 5</div> </Scroll> </div> ); } } export default App;
Maintenant que notre application est encadrée dans notre composant <Scroll /> nous allons voir comment faire le scroll jacking:
- Ajouter un wrapper pour le scroll (scroll-wrapper)
- Ajouter un wrapper en position fixed pour écouter les événements touchstart, touchmove et touchend et modifier la position top en conséquence
- Plusieurs wrapper pour nos pages full browser
Maintenant nous allons implémenter notre composant <Scroll />
import React, { Component } from 'react'; import './index.css'; class Scroll extends Component { constructor() { super(); this.state= { page:0, direction:null }; this.scrollRef=null; } componentDidMount() { if(this.scrollRef) { this.scrollRef.addEventListener('touchstart', (e) =>this.onTouchStart(e)); this.scrollRef.addEventListener('touchmove', (e) =>this.onTouchMove(e)); this.scrollRef.addEventListener('touchend', (e) =>this.onTouchEnd(e)); } } onTouchStart(e) { this.setState({position:e.touches[0].pageY}); } onTouchMove(e) { const { position } =this.state; if(position<e.touches[0].pageY) { this.setState({ direction:'down' }); }else if(position > e.touches[0].pageY) { this.setState({ direction:'up' }); } } onTouchEnd() { const { direction, page } =this.state; if(direction==='down'&&page>0) { this.setPage(page-1); }else if(direction === 'up') { this.setPage(page+1); } } setPage(page) { this.setState({page:page, direction:null}); window.scrollTo(0, window.innerHeight*page); } render() { const { scroll } =this.props; const { page } =this.state; constchildrens=this.props.children &&this.props.children.map((child, i) =><divclassName='page'>{React.cloneElement(child, {})}</div>); return ( <div className={`scroll`} style={{height:`${childrens.length*100}%`}} ref={node=>this.scrollRef = node}> <divclassName="overflow-container" style={{position:'fixed', height:'100%'}}> <divclassName="scroll-container" style={{top:`-${page * 100}%` || '0'}}> {childrens} </div> </div> </div> ); } } export default Scroll;
Maintenant il suffit de rajouter un peu de CSS :
.scroll { position: relative; width: 100%; top: 0; left: 0; } .overflow-container { position: relative; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; } .scroll-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transition: top0.5sease; } .page { display: inline-block; width: 100%; height: 100%; }
Vous pouvez aller voir le résultat sur mobile, le scroll est fluide, le fonctionnement du navigateur est respecté et le scroll full browser est lui aussi fluide.