So i'm working on this feedback board website. And i have this roadmap route where users get to see what is the website improvement roadmap. And on Mobile i would like to have a three section slide : planned , in-progress and live So i got this code from stackOverflow that enables me to detect touch sliding events. So my goal was to navigate from a roadmap stage to another whenever the user swipes left or right.
BUT whenever i swip to "Live" or "Planned" section ,and then try go go back to the "In-Progress" section , it jumps it and go directly to the section after it
To repreduce this : here is the live link
- set Mobile mode using the dev tools
- click on the menu icon
- click on the view link of the roadmap div to navigate to the roadmap route
- navigate to "Live" using the touch swiping "in this case the mouse"
- try to go back to the "In-Progress" section
here is the event listiners that i used:
document.querySelector('#roadmap_stages').addEventListener('gesture-right',()=>{
onScroll("roadmap_stages","r")
switch(currentStage){
case "In-Progress":
dispatch(setcurrentStage("Planned"))
break;
case "Live":
dispatch(setcurrentStage("In-Progress"))
break;
}
})
document.querySelector('#roadmap_stages').addEventListener('gesture-left',()=>{
onScroll("roadmap_stages","l")
switch(currentStage){
case "Planned":
dispatch(setcurrentStage("In-Progress"))
break;
case "In-Progress":
dispatch(setcurrentStage("Live"))
break;
}
})
here is the onScroll function : the goal of this function is to take care of the animations
function onScroll (id,direction){
const navigater = document.querySelector(".roadmap_roadmap_stages__FAUDD")
const currentPosition = window.getComputedStyle(navigater).left
let to;
if(direction === "r"){
switch (currentStage){
case "Planned":
to = ""
break;
case "In-Progress":
to = "one"
break;
case "Live":
to = "two"
break;
}
}
else{
switch (currentStage){
case "Planned":
to = "two"
break;
case "In-Progress":
to = "three"
break;
case "Live":
to = ""
break;
}
}
navigater.style.left = `${currentPosition}`
navigater.style.animationName = `${to}`
}
here is my redux slice:
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
screenWidth:null,
isMenuOpen:false,
isSortOpen:false,
sortMethode:"Most Upvotes",
filter:"all",
currentStage:"In-Progress",
}
const uiSlice = createSlice({
name:"ui",
initialState,
reducers:{
setScreenWidth:(state,{payload})=>{
state.screenWidth = payload
},
toggleMenu:(state,{payload})=>{
state.isMenuOpen = payload
},
toggleSort:(state,{payload})=>{
state.isSortOpen = payload
},
setSortMethode:(state,{payload})=>{
state.sortMethode = payload
},
setFilter:(state,{payload})=>{
state.filter = payload
},
setcurrentStage:(state,{payload})=>{
console.log(state.currentStage)
state.currentStage = payload
console.log(state.currentStage)
},
}
})
export default uiSlice.reducer
export const {setScreenWidth,toggleMenu,toggleSort,setSortMethode,setFilter,setcurrentStage} = uiSlice.actions
and here are the animations
@keyframes one {
100%{left: 0;}
}
@keyframes two {
100%{left: -100%;}
}
@keyframes three {
100%{left: -200%;}
}
and here is the whole function just for reference :
import React, { useEffect, useRef } from 'react'
//components
import Stage from './Stage'
//styles
import styles from "@/styles/css/roadmap.module.css"
//state
import { useDispatch, useSelector } from 'react-redux'
import { store } from '@/state/store'
import { setcurrentStage } from '@/state/slices/uiSlice'
export default function RoadmapStages(props) {
const {planned,inProgress,live} = props.roadmapData
const stages = useRef(null)
const dispatch = useDispatch()
const currentStage = store.getState().ui.currentStage
// dispatch(setcurrentStage("tagopi"))
function onScroll (id,direction){
const navigater = document.querySelector(".roadmap_roadmap_stages__FAUDD")
const currentPosition = window.getComputedStyle(navigater).left
let to;
if(direction === "r"){
switch (currentStage){
case "Planned":
to = ""
break;
case "In-Progress":
to = "one"
break;
case "Live":
to = "two"
break;
}
}
else{
switch (currentStage){
case "Planned":
to = "two"
break;
case "In-Progress":
to = "three"
break;
case "Live":
to = ""
break;
}
}
navigater.style.left = `${currentPosition}`
navigater.style.animationName = `${to}`
}
useEffect(()=>{
//mobile-scrolling-event-listener
(function(d) {
// based on original source: https://stackoverflow.com/a/17567696/334451
var newEvent = function(e, name) {
// This style is already deprecated but very well supported in real world: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent
// in future we want to use CustomEvent function: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
var a = document.createEvent("CustomEvent");
a.initCustomEvent(name, true, true, e.target);
e.target.dispatchEvent(a);
a = null;
return false
};
var debug = false; // emit info to JS console for all touch events?
var active = false; // flag to tell if touchend should complete the gesture
var min_gesture_length = 20; // minimum gesture length in pixels
var tolerance = 0.3; // value 0 means pixel perfect movement up or down/left or right is required, 0.5 or more means any diagonal will do, values between can be tweaked
var sp = { x: 0, y: 0, px: 0, py: 0 }; // start point
var ep = { x: 0, y: 0, px: 0, py: 0 }; // end point
var touch = {
touchstart: function(e) {
active = true;
var t = e.touches[0];
sp = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
ep = sp; // make sure we have a sensible end poin in case next event is touchend
debug && console.log("start", sp);
},
touchmove: function(e) {
if (e.touches.length > 1) {
active = false;
debug && console.log("aborting gesture because multiple touches detected");
return;
}
var t = e.touches[0];
ep = { x: t.screenX, y: t.screenY, px: t.pageX, py: t.pageY };
debug && console.log("move", ep, sp);
},
touchend: function(e) {
if (!active)
return;
debug && console.log("end", ep, sp);
var dx = Math.abs(ep.x - sp.x);
var dy = Math.abs(ep.y - sp.y);
if (Math.max(dx, dy) < min_gesture_length) {
debug && console.log("ignoring short gesture");
return; // too short gesture, ignore
}
if (dy > dx && dx/dy < tolerance && Math.abs(sp.py - ep.py) > min_gesture_length) { // up or down, ignore if page scrolled with touch
newEvent(e, (ep.y - sp.y < 0 ? 'gesture-up' : 'gesture-down'));
//e.cancelable && e.preventDefault();
}
else if (dx > dy && dy/dx < tolerance && Math.abs(sp.px - ep.px) > min_gesture_length) { // left or right, ignore if page scrolled with touch
newEvent(e, (ep.x - sp.x < 0 ? 'gesture-left' : 'gesture-right'));
//e.cancelable && e.preventDefault();
}
else {
debug && console.log("ignoring diagonal gesture or scrolled content");
}
active = false;
},
touchcancel: function(e) {
debug && console.log("cancelling gesture");
active = false;
}
};
for (var a in touch) {
d.addEventListener(a, touch[a], false);
// TODO: MSIE touch support: https://github.com/CamHenlin/TouchPolyfill
}
})(window.document);
document.querySelector('#roadmap_stages').addEventListener('gesture-right',()=>{
onScroll("roadmap_stages","r")
switch(currentStage){
case "In-Progress":
dispatch(setcurrentStage("Planned"))
break;
case "Live":
dispatch(setcurrentStage("In-Progress"))
break;
}
})
document.querySelector('#roadmap_stages').addEventListener('gesture-left',()=>{
onScroll("roadmap_stages","l")
switch(currentStage){
case "Planned":
dispatch(setcurrentStage("In-Progress"))
break;
case "In-Progress":
dispatch(setcurrentStage("Live"))
break;
}
})
},[])
return (
<div ref={stages} className={styles.roadmap_stages} id="roadmap_stages" >
<Stage stageData={planned} />
<Stage stageData={inProgress} />
<Stage stageData={live} />
</div>
)
}
here is the github link
i was and im still stuck on this bug for two days and would highly any help from the stackOverflow community
thanks a lot :)