I'm trying to set up a swipeable container with clickable elements. The component is the following:
const SectionBar = (): JSX.Element => {
const [translatePercent, setTranslatePercent] = useState(0);
const [x0, setX0] = useState(0);
const [isMediumOrLarger, setIsMediumOrLarger] = useState(false);
useEffect(() => {
const mediaQuery = window.matchMedia("(min-width: 768px)");
setIsMediumOrLarger(mediaQuery.matches);
mediaQuery.addEventListener("change", (event) =>
setIsMediumOrLarger(event.matches)
);
}, []);
const setSnapPercent = (index: number) => {
console.log(index);
if (index === 3) {
setTranslatePercent(index * 10 + 6);
} else if (index >= 4) {
setTranslatePercent((index + 1) * 10);
} else {
setTranslatePercent(index * 10);
}
};
const swipe: TouchEventHandler<HTMLUListElement> = (
event: TouchEvent<HTMLUListElement>
) => {
const dx = -(event.changedTouches[0].clientX - x0) / 5;
const percent = Math.max(0, Math.min(dx, MAX_PERCENT));
setTranslatePercent(percent);
};
return (
<nav className={classes["section-bar"]}>
<ul
className={classes["section-list"]}
style={
isMediumOrLarger
? {}
: {
transform: `translateX(-${translatePercent}%)`,
}
}
onTouchStart={(event) => setX0(event.changedTouches[0].clientX)}
onTouchEnd={swipe}
onTouchMove={swipe}
>
{Object.entries(createSections).map(([key, value], index) => (
<li key={key}>
<Link href={`/create/race#`}>
<a className={classes.link} onClick={() => setSnapPercent(index)}>
{value}
</a>
</Link>
</li>
))}
</ul>
</nav>
);
};
When I try testing the component out in the simulated phone in Chrome developer tools I can swipe just fine, but tapping on one of the links only seems to register clicks on the links at index 0 and sometimes 1 regardless of which link I press. When I don't pass the touch event props to the ul the links work just fine, so I think the problem has something to do with the touch events.
I got it working. in swipe, I subtracted the change in x from the current transform percent. I have no idea why that fixed it.
Related
How to partially add data to an array and display it? The array is reset each time. Is it possible to save the previous state of an array before adding new data?
const ICONS_PER_PAGE = 5
const IconsList = () => {
const { iconArray, setIconArray } = useContext(IconArray)
const [triggerToLoad, setTriggerToLoad] = useState(true)
const [iconArrayLazy, setIconArrayLazy] = useState([])
const scrollHandler = (e) => {
let scrollGalleryValue = document.querySelector('.list__container').getBoundingClientRect().y + document.querySelector('.list__container').clientHeight - window.innerHeight
if (scrollGalleryValue <= 0 ) {
setTriggerToLoad(!triggerToLoad)
console.log(iconArrayLazy); // [] empty array
}
}
useEffect(() => {
setIconArrayLazy(iconArrayLazy => [...iconArrayLazy, ...iconArray.slice(iconArrayLazy.length, iconArrayLazy.length + ICONS_PER_PAGE)])
}, [triggerToLoad])
useEffect(() => {
setIconArrayLazy(iconArrayLazy => [...iconArrayLazy, ...iconArray.slice(iconArrayLazy.length, iconArrayLazy.length + ICONS_PER_PAGE)])
document.addEventListener('scroll', scrollHandler)
return function () {
document.removeEventListener('scroll', scrollHandler)
}
}, [])
return (
<>
<ul className="list__container">
{iconArrayLazy.map(({id, title, img}) =>
<li className="icon_container" key={id}>
<Link to={`/icon-${id}`}>
<img className="icon_container__image" src={img} alt={title} />
</Link>
</li>
)}
</ul>
</>
)
}
export default IconsList
Is there a way I can generate multiple SET VALUES per second using react?
What am I trying to accomplish?
I have created this "tap game" where you could generate $1 per tap up to $250 per tap depending on where you tap. In this game I want to be able to "hire employees." After designating the hired employee to, lets say "$1 team", my "Score" will increase by 1 per second. If I decided to hire another employee and I decided to assign it to "$5 team", it would increase by 5. Since I have 2 employees at this time, I do not want to cancel one or the other out, I would like them to run simultaneously (e.g. 6 per second in this example).
What Have I tried?
I have not tried, or tested, anything specifically because of course I am unsure of how to accomplish this. I have researched other Stack Overflow examples and I have came across one very similar, however, they are using an API where as I am not. Stack Overflow Example I am not sure if I would even need an API, I would assume I would not?
Request:
If at all possible, a solution that I could use dynamically would be extremely appreciated. If I decided to hire 10 employees and stick them all on $1 team, I would like to be able to do so.
Attached is a fully functional code.
import React, {useState, useEffect} from 'react'
const App = () => {
// ======================================
// HOOKS
// ======================================
const [score, setScore] = useState(0)
const [showEZBake, setShowEZBake] = useState(false)
const [showToasterOven, setShowToasterOven] = useState(false)
const [showConvectionOven, setShowConvectionOven] = useState(false)
const [showSmallFactory, setShowSmallFactory] = useState(false)
// ======================================
// FUNCTIONS
// ======================================
const winCondition = () => {
if (score >= 100000) {
return (
<h1>YOURE A WINNER!!</h1>
)
}
}
// EARN REVENUE FUNCTIONS
const earn1 = () => {
setScore(score + 1)
winCondition()
}
const earn5 = () => {
setScore(score + 5)
winCondition()
}
const earn25 = () => {
setScore(score + 25)
winCondition()
}
const earn50 = () => {
setScore(score + 50)
winCondition()
}
const earn250 = () => {
setScore(score + 250)
winCondition()
}
// PURCHASE ITEMS FUNCTIONS
const buyEZOven = () => {
setScore(score - 25)
}
const buyToasterOven = () => {
setScore(score - 250)
}
const buyConvectionOven = () => {
setScore(score - 1000)
}
const buySmallFactory = () => {
setScore(score - 15000)
}
const upgradeEZOven = () => {
if (score >= 25) {
setShowEZBake(true)
buyEZOven()
}
}
const upgradeToasterOven = () => {
if (score >= 250 ) {
setShowToasterOven(true)
buyToasterOven()
}
}
const upgradeConvectionOven = () => {
if (score >= 1000) {
setShowConvectionOven(true)
buyConvectionOven()
}
}
const upgradeSmallFactory = () => {
if (score >= 15000) {
setShowSmallFactory(true)
buySmallFactory()
}
}
// ======================================
// DISPLAY
// ======================================
return (
<div>
<h1>Bakery</h1>
<h2>Revenue {score}</h2>
<h3>No Bake Pudding</h3><button onClick={earn1}>$1</button>
{/* EZ BAKE OVEN */}
{showEZBake ? (
<>
<h3>Easy Bake Oven</h3>
<button onClick={earn5}>$5</button>
</>
) : (
<>
<h3>Purchase Easy Bake Oven</h3>
<button onClick={upgradeEZOven}>$25</button>
</>
)
}
{/* TOASTER OVEN */}
{showToasterOven ? (
<>
<h3>Toaster Oven</h3>
<button onClick={earn25}>$25</button>
</>
) : (
<>
<h3>Purchase Toaster Oven</h3>
<button onClick={upgradeToasterOven}>$250</button>
</>
)}
{/* CONVECTION OVEN */}
{showConvectionOven ? (
<>
<h3>Convection Oven</h3>
<button onClick={earn50}>$50</button>
</>
) : (
<>
<h3>Purchase Convection Oven</h3>
<button onClick={upgradeConvectionOven}>$1000</button>
</>
)}
{/* FACTORY */}
{showSmallFactory ? (
<>
<h3>Small Factory Production</h3>
<button onClick={earn250}>$250</button>
</>
) : (
<>
<h3>Purchase Small Factory</h3>
<button onClick={upgradeSmallFactory}>$15,000</button>
</>
)}
{/* WIN CONDITION */}
{
winCondition()
}
</div>
)
}
export default App
You can generate a value every second pretty easily with useEffect:
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount(count + 1);
}, 1000);
return () => clearTimeout(timer);
}, [count, setCount]);
I am using a package called npm i --save react-audio-player and I am trying to use to have music auto play when the page is loaded. At this time, I am able to get the audio player to display, so the package is installed properly, however, the music does not show up at all.
I have had my audio directory both inside, and outside of the the project directory and I have copied the path directly from vscode, but it does not show to be active. Im not exactly sure what could possibly be causing this error. At first I thought maybe it was just chrome blocking the autoplay, but that would not explain why the audio file is displaying at all.
Below is both my code and a photo of my files
import React, {useState, useEffect} from 'react'
import ReactAudioPlayer from 'react-audio-player'
const App = () => {
// ======================================
// HOOKS
// ======================================
const [score, setScore] = useState(0)
const [showEZBake, setShowEZBake] = useState(false)
const [showToasterOven, setShowToasterOven] = useState(false)
const [showConvectionOven, setShowConvectionOven] = useState(false)
const [showSmallFactory, setShowSmallFactory] = useState(false)
// const [counter, setCounter] = useState(score)
// ======================================
// FUNCTIONS
// ======================================
const winCondition = () => {
if (score >= 100000) {
return (
<h1>YOURE A WINNER!!</h1>
)
}
}
// EARN REVENUE FUNCTIONS
const earn1 = () => {
setScore(score + 1)
winCondition()
}
const earn5 = () => {
setScore(score + 5)
winCondition()
}
const earn25 = () => {
setScore(score + 25)
winCondition()
}
const earn50 = () => {
setScore(score + 50)
winCondition()
}
const earn250 = () => {
setScore(score + 250)
winCondition()
}
// PURCHASE ITEMS FUNCTIONS
const buyEZOven = () => {
setScore(score - 25)
}
const buyToasterOven = () => {
setScore(score - 250)
}
const buyConvectionOven = () => {
setScore(score - 1000)
}
const buySmallFactory = () => {
setScore(score - 15000)
}
// THIS IS AN EXAMPLE OF HOW TO FORCE TEXT ONTO A PAGE
// const reveal = () => {
// // If the score is greater than or equal to 5, return the <h1> element
// if (score >= 5) {
// return (
// <h1>TEST</h1>
// )
// } else {
// // Otherwise, return null
// return null
// }
// }
const upgradeEZOven = () => {
if (score >= 25) {
setShowEZBake(true)
buyEZOven()
}
}
const upgradeToasterOven = () => {
if (score >= 250 ) {
setShowToasterOven(true)
buyToasterOven()
}
}
const upgradeConvectionOven = () => {
if (score >= 1000) {
setShowConvectionOven(true)
buyConvectionOven()
}
}
const upgradeSmallFactory = () => {
if (score >= 15000) {
setShowSmallFactory(true)
buySmallFactory()
}
}
// useEffect(() => {
// const timer = setInterval(() => {
// setCounter((prevCounter) => prevCounter + 1)
// }, 1000)
// return () => clearTimeout(timer);
// }, [counter, setCounter])
// ======================================
// DISPLAY
// ======================================
return (
<div>
<ReactAudioPlayer
src='../audio/mainMusic.mp3'
autoPlay
controls
/>
<h1>Bakery</h1>
<div className='header-grid-container'>
<h2>Revenue ${score}</h2>
<h2>Goal: $100,000</h2>
</div>
<div className='grid-container'>
{/* EZ BAKE OVEN */}
<img src="https://i.imgur.com/gDIbzJa.png" onClick={earn1}></img>
{showEZBake ? (
<img src="https://i.imgur.com/NQ0vFjF.png" onClick={earn5}></img>
) : (
<img onClick={upgradeEZOven} src="https://i.imgur.com/mwp9tL5.png"></img>
)}
{/* TOASTER OVEN */}
{showToasterOven ? (
<img src='https://i.imgur.com/k5m7lCM.png' onClick={earn25}></img>
) : (
<img src='https://i.imgur.com/hg12R4H.png' onClick={upgradeToasterOven}></img>
)}
{/* CONVECTION OVEN */}
{showConvectionOven ? (
<img src='https://i.imgur.com/JEzQkHL.png' onClick={earn50}></img>
) : (
<img src='https://i.imgur.com/x7i3ZeE.png' onClick={upgradeConvectionOven}></img>
)}
</div>
<div className='grid-container2'>
{showSmallFactory ? (
<img className='factory' src='https://i.imgur.com/HugCDVu.png' onClick={earn250}></img>
) : (
<img className='factory' src='https://i.imgur.com/HLsiH2r.png' onClick={upgradeSmallFactory}></img>
)}
{/* WIN CONDITION */}
{
winCondition()
}
{/* THIS IS AN EXAMPLE OF HOW TO CALL A FUNCTION
{
reveal()
} */}
</div>
</div>
)
}
export default App
Browsers does not allow pages to play audio unless there was a interaction done on the domain playing the sound. For Chrome this is the case since 2018.
Shown here: https://developer.chrome.com/blog/autoplay/
I am not familiar with package you are using, but I doubt it would change it. This means that you should not be able to play audio as soon as page is loaded and will have to force user to make any sort of interaction.
Aside from that I feel like the path is wrong. If you use those sort of relative paths and do not use import to get the resource they are in 90% instances resolved relative to public folder an project structure. So I'd try placing your audio folder into public and change src so that you are grabbing it relative to what browser sees. I sadly do not remember if that's public/audio/mainMusic.mp3 or audio/mainMusic.mp3or something else, but the simple test is, if you paste it into browser, it will either open, or start downloading the file.
I am fetching data from the Backend and loading them in the card using react-tinder-card
Swiping works properly but unable to swipe using the buttons
I follow the documentation but still did not work
Here is the sample
Swiping gestures are working fine.
But when implement by checking documentation things did not work
Things are not working and tomorrow is my project day
import React, { useEffect, useState, useContext, useRef } from "react";
function Wink() {
const [people, setPeople] = useState([]);
const [loading, setLoading] = useState(false);
const [currentIndex, setCurrentIndex] = useState(people.length - 1)
const [lastDirection, setLastDirection] = useState()
// used for outOfFrame closure
const currentIndexRef = useRef(currentIndex)
const childRefs = useMemo(
() =>
Array(people.length)
.fill(0)
.map((i) => React.createRef()),
[]
)
const updateCurrentIndex = (val) => {
setCurrentIndex(val)
currentIndexRef.current = val
}
const canGoBack = currentIndex < people.length - 1
const canSwipe = currentIndex >= 0
// set last direction and decrease current index
const swiped = (direction, nameToDelete, index) => {
setLastDirection(direction)
updateCurrentIndex(index - 1)
}
const outOfFrame = (name, idx) => {
console.log(`${name} (${idx}) left the screen!`, currentIndexRef.current)
// handle the case in which go back is pressed before card goes outOfFrame
currentIndexRef.current >= idx && childRefs[idx].current.restoreCard()
// TODO: when quickly swipe and restore multiple times the same card,
// it happens multiple outOfFrame events are queued and the card disappear
// during latest swipes. Only the last outOfFrame event should be considered valid
}
const swipe = async (dir) => {
if (canSwipe && currentIndex < db.length) {
await childRefs[currentIndex].current.swipe(dir) // Swipe the card!
}
}
// increase current index and show card
const goBack = async () => {
if (!canGoBack) return
const newIndex = currentIndex + 1
updateCurrentIndex(newIndex)
await childRefs[newIndex].current.restoreCard()
}
useEffect(() => {
setLoading(true);
axios
.post("http://localhost:4000/api/all-profile", { email })
.then(function (response) {
setPeople(response.data);
setCurrentIndex(response.data.length);
}
}, []);
return (
<div className="DateMainDiv">
<Header />
<div className="ProfieCards">
{people.map((person) => (
<TinderCard
className="swipe"
key={person.email}
ref={childRefs[index]}
preventSwipe={swipe}
onSwipe={(dir) => swiped(dir, person.name, person.email)}
onCardLeftScreen={onCardLeftScreen}
onCardUpScreen={onCardUpScreen}
>
<div
style={{ backgroundImage: `url(${person.image})` }}
className="Winkcard"
>
<img
onLoad={handleLoad}
src={person.image}
alt="Image"
className="TinderImage"
/>
<h3>
{person.name}{" "}
<IconButton
style={{ color: "#fbab7e" }}
onClick={() => handleOpen(person.email)}
>
<PersonPinSharpIcon fontSize="large" />
{parseInt(person.dist/1000)+"KM Away"}
</IconButton>
</h3>
</div>
</TinderCard>
))}
<SwipeButtonsLeft onClick={()=>{swipe("left")}} />
<SwipeButtonsLeft onClick={()=>{goback()}} />
<SwipeButtonsLeft onClick={()=>{swipe("right")}} />
</div>
</div>
);
}
export default Wink;
I have a Navigation component in which the Menu Items float in separately on load and float out on click.
When I added Router and changed the items to Links, the exit animation didn't work because it loaded the new Route component right away.
I want to keep the items individual animation with Link functionality.
Here is the link:
https://codesandbox.io/s/elastic-leaf-fxsswo?file=/src/components/Navigation.js
Code:
export const Navigation = () => {
const navRef = useRef(null);
const onResize = () => {
setIsColumn(window.innerWidth <= 715);
};
const [clickOnMenu, setClick] = useState(false);
const [itemtransition, setTransition] = useState(
Array(menuItems.length).fill(0)
);
const [isColumn, setIsColumn] = useState(window.innerWidth <= 715);
const click = (e) => {
const copy = [...itemtransition];
const index = e.target.id;
setTransition(copy.map((e, i) => (Math.abs(index - i) + 1) / 10));
setTimeout(() => setClick(true), 50);
};
useEffect(() => {
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return (
<AnimatePresence exitBeforeEnter>
{!clickOnMenu && (
<Nav ref={navRef}>
{menuItems.map((e, i) => {
const text = Object.keys(e)[0];
const value = Object.values(e)[0];
return (
<Item
id={i}
key={value}
animate={{
x: 0,
y: 0,
opacity: 1,
transition: { delay: (i + 1) / 10 }
}}
initial={{
x: isColumn ? 1000 : 0,
y: isColumn ? 0 : 1000,
opacity: 0
}}
exit={{
x: isColumn ? -1000 : 0,
y: isColumn ? 0 : -1000,
opacity: 0,
transition: { delay: itemtransition[i] }
}}
onClick={click}
>
{/*<Link to={`/${value}`}>{text}</Link>*/}
{text}
</Item>
);
})}
</Nav>
)}
</AnimatePresence>
);
};
In the sandbox in Navigation.js 69-70. row:
This is the desired animation.
69. {/*<Link to={`/${value}`}>{text}</Link>*/}
70. {text}
But when I use Link there is no exit animation
69. <Link to={`/${value}`}>{text}</Link>
70. {/*text*/}
Is there a workaround or I should forget router-dom.
Thank you in forward!
This may be a bit hackish, but with routing and transitions sometimes that is the nature. I suggest rendering the Link so the semantic HTML is correct and add an onClick handler to prevent the default navigation action from occurring. This allows any transitions/animations to go through. Then update the click handler of the Item component to consume the link target and issue an imperative navigation action on a timeout to allow transitions/animations to complete.
I used a 750ms timeout but you may need to tune this value to better suit your needs.
Example:
...
import { Link, useNavigate } from "react-router-dom";
...
export const Navigation = () => {
const navRef = useRef(null);
const navigate = useNavigate(); // <-- access navigate function
...
const click = target => (e) => { // <-- consume target
const copy = [...itemtransition];
const index = e.target.id;
setTransition(copy.map((e, i) => (Math.abs(index - i) + 1) / 10));
setTimeout(() => {
setClick(true);
}, 50);
setTimeout(() => {
navigate(target); // <-- navigate after some delta
}, 750);
};
...
return (
<AnimatePresence exitBeforeEnter>
{!clickOnMenu && (
<Nav ref={navRef}>
{menuItems.map((e, i) => {
const text = Object.keys(e)[0];
const value = Object.values(e)[0];
return (
<Item
...
onClick={click(`/${value}`)} // <-- pass target to handler
>
<Link
to={`/${value}`}
onClick={e => e.preventDefault()} // <-- prevent link click
>
{text}
</Link>
</Item>
);
})}
</Nav>
)}
</AnimatePresence>
);
};
...