I'm trying to have a Snapchat story-like feature where an array of pictures is displayed for a set period of seconds and than go back to the previous navigation.
Something like the following:
if (props.length > 0) {
let timeout;
if (props.length - 1 === index) {
clearTimeout(timeout)
navigation.goBack()
}
if (props[index].image) {
timeout = setTimeout(() => {
setIndex(index + 1)
}, 3000)
return <Image source={{ uri: props[index].image }} style={{ flex: 1 }} />
}
}
But, this is showing unpredictable results.
You might wanna use setInterval instead. eg. in codesandbox here
Related
I haven't been able to find an answer to this question but I have seen this exact behaviour in many apps (calendars, agendas etc.). As you can see in the snippet below my container expands with scrolling to both sides - new divs are being inserted inside. When you scroll to the right it feels okay and natural, however, when you scroll to the left, it always adds the element and you stay at 0px needing to scroll a bit back and then to the left again to expand some more. Best if you try below:
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom/client';
function Test() {
const [span, setSpan] = useState<Array<number>>([-1, 0, 1]);
// Append item to the array - scrolling right
const append = () => {
setSpan([
...span,
span[span.length - 1] + 1,
]);
};
// Prepend item to the array - scrolling left
const prepend = () => {
setSpan([
span[0] - 1,
...span,
]);
};
// Center view on load - to the middle of element '0' - e.i. the center
useEffect(() => {
const element = document.getElementById('element-0');
if (element) {
element.scrollIntoView({ behavior: 'auto', inline: 'center' });
}
}, []);
// Register 'scroll' listener
useEffect(() => {
const element = document.getElementById('container');
const scrolling = () => {
if (element) {
if (element.scrollLeft === 0) {
prepend();
}
if (element.offsetWidth + element.scrollLeft >= (element.scrollWidth - 100)) {
append();
}
}
};
element.addEventListener('scroll', scrolling);
return () => {
element.removeEventListener('scroll', scrolling);
};
}, [span.length]);
return (
<div style={{
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}
>
<div
id="container"
style={{
maxWidth: '50vw', maxHeight: '50vh', overflowX: 'auto', whiteSpace: 'nowrap', backgroundColor: 'red',
}}
>
<div style={{ width: 'fit-content' }}>
<div style={{ width: 'fit-content' }}>
<div style={{ display: 'flex' }}>
{span.map((element) => (
<div key={`element-${element}`} id={`element-${element}`} style={{ minWidth: '40vw', minHeight: '100vh', border: '1px solid black' }}>
{ element }
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
}
const root = ReactDOM.createRoot(
document.getElementById('root')
);
root.render(
<React.StrictMode>
<Test />
</React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I tried programatically scrolling a bit to the right before prepending new item, but it only created more issues. Is there an easy way to solve it?
Prepending an element doesn't make its container's scrollLeft increase by as much as the element's width.
Instead, the scrollLeft's value remains the same, and so the new box effectively "pops" into the view:
Since, as you mentioned, the scrollLeft remains at zero after insertion, further mouse wheel movement doesn't result in the container's scroll, so the scroll event is not fired, and hence the box insertion logic is not evaluated.
That can be solved, for example, by listening for the wheel event rather than the scroll. The problem is, that the scrollLeft would still stay at zero, so the boxes would just appear in the view one by one rather than letting the user scroll onto them. Demo. Plus, the mouse wheel is not the only way to scroll.
As such, by the very definition of the problem, we need to manually adjust the scroll position so that the view remains at the same element as before the insertion. In this case, this amount is simply the offsetWidth of the box, so the solution could be as follows:
Demo
const boxWidth = document.getElementById("element-0").offsetWidth;
if (element.scrollLeft < 100) {
element.scrollBy(boxWidth, 0);
prepend();
}
else if (/*...*/) {
I hope this answers your question. :)
So I want to implement a progress bar like this in react native. I have found a library which is https://www.npmjs.com/package/react-native-progress, but this library does not have a progress bar like this. The issue is the circle part. Is there a library which has a progress bar like this one? If there isn't, then any idea on how to implement this would be appreciated. So as a fellow commenter got confused whether it is a slider or progress bar, I will include the full screen image. There is a timer and the progress bar or progress slider reflects that in real time.
We can still exploit react-native-community/slider for this purpose. This would make sense since we can allow the user to fast forward. If you do not want allow user interaction, you can still disable it via the slider component props.
For a smooth sliding, the slider changes its value at a higher tick rate. You might want to experiment with it a little bit to make it fit your needs.
Here is an example on how I did it.
const [progress, setProgress] = useState(-1)
// takes time in seconds
function interpolate(time) {
return time * 600
}
// runs 10 seconds, you can take any other value
const [time, setTime] = useState(interpolate(10))
useEffect(() => {
if (progress < time && progress > -1) {
const timer = setTimeout(() => setProgress(progress + 10), 10)
return () => clearTimeout(timer)
}
}, [progress, time])
const startTimer = React.useCallback(() => {
setProgress(0)
}, [])
return (
<SafeAreaView style={{ margin: 30 }}>
<Slider
style={{ width: 300, height: 40, backgroundColor: "red" }}
value={progress}
minimumValue={0}
maximumValue={time}
minimumTrackTintColor="#FFFFFF"
maximumTrackTintColor="#FFFFFF"
/>
<Pressable style={{ marginTop: 10 }} onPress={() => startTimer()}>
<Text>Toggle timer</Text>
</Pressable>
</SafeAreaView>
)
The above is a dummy implementation which will run a timer for 10 seconds and the slider will move automatically like a progress bar.
I'm making a carousel app, and I want to find out how to drag to scroll the items within the carousel. I want to be able to scroll through the children of "carousel-content" div by dragging them with the mouse.
Currently, "carousel-content-wrapper" is a flexbox component with overflow: scroll and on mobile swiping it with touch works fine. However, I want to be able to do the same on desktop - swipe through the items with the mouse, and have the items follow the mouse as though you were scrolling through them with your finger.
function Carousel(props) {
const {children} = props
const [index, setIndex] = useState(0)
const [array, setArray] = useState([])
const [length, setLength] = useState(children.length)
const mobile = window.innerWidth > 768 ? "false" : "true"
// setting up breakpoints for easy responsiveness
useEffect(() => {
setLength(children.length)
setArray(React.Children.toArray(children))
}, [children])
const next = () => {
if (index < (length-1)) {
setIndex(prevState => prevState + 1)
}
// sets the index to next if you are not on the last slide
}
const previous = () => {
if (index > 0) {
setIndex(prevState => prevState - 1)
}
// sets the index to be the previous if you are further than first slide
}
return (
<div className="carousel-outer-container" style={{
height: props.outerHeight,
width: props.outerWidth,
padding: props.outerPadding,
}}>
<div className="carousel-inner-container" style={{height: props.innerHeight, width: props.innerWidth}}>
<div className="carousel-wrapper"
>
{ index > 0 && <button className="left-arrow" onClick={previous}>
<
</button> }
<div className="carousel-content-wrapper" >
{ index < children.length - props.show && <button className="right-arrow" onClick={next}>
>
</button>}
{/* the sliding is done by the translateX below. it translates by (100% of the slides * the index of the slide) from the starting position. */}
<div className="carousel-content"
style={{transform: mobile === "false" && `translateX(-${index * 100 }%)`,
width: mobile === "false" && `${100 / props.show}%`}}
>
{children}
</div>
</div>
</div>
</div>
</div>
)
}
What is the best way to go about making this possible? A solution that doesn't use external packages or libraries and uses hooks instead of class components would be great.
here is my sample code
in the browser i want to scroll down the page the the navbar will say what color i am showing.
<div style={{height: "800px"}}>
<h2 style={{backgroundColor: `${nav}`,
position: "fixed",
width: "100%"
}}
>
NaveBar {nav ? "red" : "blue"}!
</h2>
</div>
it's somewhat not changing the name of the title and color also.i just dun know where is the problem.
can somebody help me on this please?
You initialized the state with a string useState("red");
and then you update the state to an object with setNav({ back });
To solve this just change it to setNav(back)
By the way - listening to scroll-events can be laggy, so you might want to "throttle" the event.
import throttle from lodash or just copy paste this function:
https://gist.github.com/abhinavnigam2207/a147abe0213d60467abacd33db7c6d2e
Then you use it by wrapping your function into it, like this:
useEffect(() => {
window.addEventListener(
"scroll",
throttle(() => {
const back = window.scrollY < 70 ? "red" : "blue";
setNav(back);
}, 100)
);
});
I been trying to make a Masonry gallery with a sequential fade-in effect so that the pictures fade in one by one. And there is also a shuffle feature which will randomize the images and they fade in again after being shuffled.
here is the demo and the code:
https://tuo1t.csb.app/
https://codesandbox.io/s/objective-swartz-tuo1t
When first visiting the page, the animation is correct. However once we click on the shuffle button, something weird happened: There are often some pictures don't fade-in sequentially after the image before them faded in, there is even no fade-in animation on them, they just show up out of order.
The way I achieved this animation is by adding a delay transition based on the index of the image, and use ref to track images.
first I initialize the ref
let refs = {};
for (let i = 0; i < images.length; i++) {
refs[i] = useRef(null);
}
and I render the gallery
<Mansory gap={"1em"} minWidth={minWidth}>
{imgs.map((img, i) => {
return (
<PicContainer
index={img.index}
selected={isSelected}
key={img.index}
>
<Enlarger
src={img.url}
index={img.index}
setIsSelected={setIsSelected}
onLoad={() => {
refs[i].current.toggleOpacity(1); <--- start with zero opacity images till those are loaded
}}
ref={refs[i]}
realIndex={i}
/>
</PicContainer>
);
})}
</Mansory>
for the every image component
class ZoomImg extends React.Component {
state = { zoomed: false, opacity: 0 };
toggleOpacity = o => {
console.log("here");
this.setState({ opacity: o }); <-- a setter function to change the opacity state via refs:
};
render() {
const {
realIndex,
index,
src,
enlargedSrc,
setIsSelected,
onLoad
} = this.props;
return (
<div style={{ margin: "0.25rem" }} onLoad={onLoad}>
<Image
style={{
opacity: this.state.opacity,
transition: "opacity 0.5s cubic-bezier(0.25,0.46,0.45,0.94)",
transitionDelay: `${realIndex * 0.1}s` <--- add a delay transition based on the index of the image.
}}
zoomed={this.state.zoomed}
src={src}
enlargedSrc={enlargedSrc}
onClick={() => {
this.setState({ zoomed: true });
setIsSelected(index);
}}
onRequestClose={() => {
this.setState({ zoomed: false });
setIsSelected(null);
}}
renderLoading={
<div
style={{
position: "absolute",
top: "50%",
color: "white",
left: "50%",
transform: "translateY(-50%} translateX(-50%)"
}}
>
Loading!
</div>
}
/>
</div>
);
}
}
I used console.log("here"); in the setter function, which will be called for changing the opacity state via refs. There are 16 images, so initially it is called 16 times. But when I clicked on the shuffle button, you can see that it is called fewer than 16 times because some of the pictures show up directly without fading in.
I been struggling with this problem for days and really hope someone can give me some hints.
The problem is that your are adding only some new images in the shuffle method, one approach is to apply 0 opacity to all refs first, then wait a few ms to add 1 opacity again, like here.
But, I would recommend a better approach for animation, I love shifty and its Tweenable module.