Is there a way in React Native to rebuild the iOS picker component completely in Java Script? I don't need the common picker, but a normal scroll view with a similar fade-out effect like the iOS picker.
EDIT – I think I have not explained my initial answer exactly enough. This is why I complete it here:
I want to build a scroll view that takes over the whole screen. It's not supposed to give the user the possibility to elect some item, like the the iOS Picker does. Nevertheless, it's supposed to be a 'normal' scroll view, that shows the user some information, e.g. different chats, tasks, news and so on.
The only difference to React Native's common scroll view should be the fade-out effect at the top: When the user scrolls the content up, it should not just leave the screen at its top edge, but it should use the iOS Picker's fade-out effect (see picture).
This fade-out effect is made up of two parts: First of all, it raises the content's transparency with a decreasing y-coordinate. Furthermore, this content seems to escape into the third dimension.
My problem is, that I don't see a way to achieve this three-dimensionality of the content in React Native. I've to add, that the content in my scroll view does not consist of small, equally sized items (like e.g. the texts 'Item 1', 'Item 2', 'Item 3',...), but of bigger items with different sizes like images or whole textboxes.
You can use this NPM module to get what you want. That module works the same in Android and iOS. Do not reinvent the wheel :)
EDIT: Now I've understood what you want. You can try this snack that I've made for you:
https://snack.expo.io/r1qnxSt9m
Of course you need to improve it, but it's a beginning.
You can achieve the desired effect with the Animated api. The idea is to set different input ranges to the items in your list. You then hook the opacity to the scroll value of your ScrollView (or any list component). I have simplified the code, but it should be enough to demonstrate the idea.
The example below only demonstrates an opacity effect, but you could easily add a translate effect to get the exact animation that you are looking for.
const data = []; // array that contains the text
const items = [];
for (let i = 0; i < data.length; ++i) {
const distanceFromViewCenter = Math.abs(i * ITEM_HEIGHT);
const inputRange = [
-distanceFromViewCenter - 2 * ITEM_HEIGHT,
-distanceFromViewCenter - ITEM_HEIGHT,
-distanceFromViewCenter, // Middle of picker
-distanceFromViewCenter + ITEM_HEIGHT,
-distanceFromViewCenter + 2 * ITEM_HEIGHT,
];
items.push(
<Animated.View
style={{
opacity: this._scrollValue.interpolate({
inputRange,
outputRange: [0.0, 0.3, 1.0, 0.3, 0.0],
}),
}}
>
<Text style={{ height: ITEM_HEIGHT }}>{data[i]}</Text>
</Animated.View>
)
}
<ScrollView
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this._scrollValue } } }],
{ useNativeDriver: true }
)}
>
{items}
</ScrollView>
Related
Some answers of our chatbot are very long. The webchat scrolls automatically to the bottom so users have to scroll up to get to the top of the bubble and start reading.
I've implemented a custom renderer (react) to wrap the answers into a custom component which simply wraps the answer into a div-tag. I also implemented a simple piece of code to scroll to the top of the bubble.
const MyCustomActivityContainer = ({ children }) => {
const triggerScrollTo = () => {
if (scrollRef && scrollRef.current) {
(scrollRef.current as any).scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
}
const scrollRef: React.RefObject<HTMLDivElement> = React.createRef()
return (
<div ref={ scrollRef } onClick={ triggerScrollTo }>
{ children }
</div>
)
}
export const activityMiddleware = () => next => card => {
if (/* some conditions */) {
return (
<MyCustomActivityContainer>
{ next(card) }
</MyCustomActivityContainer>
);
} else {
return (
{ next(card) }
)
}
};
But this only works if the scrollbar slider is not at its lowest position (there is at least 1 pixel left to scroll down, see here). The problem is the useScrollToBottom hook which always scrolls to bottom automatically if the scrollbar is completely scrolled down.
Is there any way to overwrite the scroll behavior or to temporarily disable the scrollToBottom feature?
As there is no reproducible example I can only guess.
And I'll have to make some guesses on the question too.
Because it's not clear what exactly in not working:
Do you mean that click on the <div> of MyCustomActivityContainer and subsequent call to triggerScrollTo doesn't result into a scroll?
That would be strange, but who knows. In this case I doubt anyone will help you without reproducible example.
Or do you mean that you can scroll the message into view, but if it is already in the view then new messages can result into a scroll while user is still reading a message.
That's so, but it contradicts with you statement that your messages are very long, because that would be the problem with short messages, not with the long ones.
But anyway, you should be able to fix that.
If it works fine with 1 pixel off the lowest position, then just scroll that 1 pixel. You'll need to find the scrollable element. And do scrollable_element.scrollTop -= 1. I tested this approach here. And it worked (there the scrollable element is the grandparent of <p>'s)
Or do you try to scroll automatically at the moment the message arrives? Аnd that is the real issue, but you forgot to mention it, and didn't posted the code that tries to auto-scroll?
In that case you can try to use setTimeout() and defer the scroll by, let's say, 200ms.
This number in based on what I gathered from the source:
BotFramework-WebChat uses react-scroll-to-bottom
In react-scroll-to-bottom there are some timeouts 100ms and 34ms
BotFramework-WebChat doesn't redefine them
There are some heuristics in react-scroll-to-bottom that probably coursing the trouble
https://github.com/compulim/react-scroll-to-bottom/blob/3eb21bc469ee5f5095a431ac584be29a0d2da950/packages/component/src/ScrollToBottom/Composer.js
Currently, there are no reliable way to check if the "scroll" event is trigger due to user gesture, programmatic scrolling, or Chrome-synthesized "scroll" event to compensate size change. Thus, we use our best-effort to guess if it is triggered by user gesture, and disable sticky if it is heading towards the start direction.
And
https://github.com/compulim/react-scroll-to-bottom/blob/f19b14d6db63dcb07ffa45b4433e72284a9d53b6/packages/component/src/ScrollToBottom/Composer.js#L91
For what we observed, #1 is fired about 20ms before #2. There is a chance that this stickyCheckTimeout is being scheduled between 1 and 2. That means, if we just look at #1 to decide if we should scroll, we will always scroll, in oppose to the user's intention.
That's why I think you should use setTimeout()
Since there isn't a reproducible code for me tweak and show you. My suggestion is tweak your code slightly. Chatbot requires constant streaming of data when a new message arrives calculate the height of the div element created for the message. If the div element is greater than the widget height scroll to the top else you can choose to leave it as it is.
In this example you can see some nice transition between pages triggered by the route change. (took from LevelUp Tutorials' React Animation course, thanks a lot Scott Tolinski).
Now I'd like to make these transitions happen in both directions, depending on which page it's transitioning to (and from), for instance:
Page One -> Page Two (both pages transition from left to right)
Page Three -> Page One (both pages transition from right to left)
etc
From that first example I created this example where the value of x is dynamic and should be evaluated to either 100 or -100, based on the direction of the transition.
I haven't fundamentally understood how useTransition() works, and the documentation is rather limited. The examples look amazing but are quite hard to understand.
This example seems to do a similar thing to what I'm trying to achieve but the code feels like black magic: the y property of each object returned from rows.map() appears to be related to the y value on the functions assigned to enter and update properties because if I remove that I get the error: Cannot read property 'replace' of undefined. How does that work?
This problem has two part.
determining the direction
change the animation
I created an example for solving the second part. When the user click page one I reverse the animation.
const reverse = location.pathname === '/';
const transitions = useTransition(location, location => location.key, {
from: { opacity: 0, transform: `translate3d(${reverse ? '-100%' : '100%'},0,0)` },
enter: { opacity: 1, transform: "translate3d(0,0,0)" },
leave: { opacity: 0, transform: `translate3d(${reverse ? '100%' : '-100%'},0,0)` },
// "initial: null" should only disable the 'from' initial transition, not the subsequent 'leave' transition (on the first manually triggered transition)
initial: null
});
Here is the sandbox: https://codesandbox.io/s/spring-transition-with-routes-215t8
For the first part to determine when to reverse the animation, I would store the path at each click and compare the next one to the previous. There is an example of storing the path here: Check history previous location before goBack() react router v4
I hope it helps.
I've been trying to create something along the lines of accordion component in React, (the code pen is in vanilla but demonstrates the problem).
Pen: https://codepen.io/tim-bitanov/pen/MXrVRE
Original issue:
Basically, the accordion changes its width when expanded and items begin to enter from below. My problem is that the items do not really come from below, but rather from bottom right since the left property of the container changing due to new width.
<TransitionGroup component={null}>
{this.props.expanded && (
React.Children.map(this.props.children, (child) => (
<Transition
timeout={500}
onEnter={this.onSubMenuEnter}
onExit={this.onSubMenuExit}
>
{child}
</Transition>
)))}
</TransitionGroup>
private onSubMenuEnter = (node: HTMLElement) => {
TweenLite.set(node, { clearProps: 'all' });
TweenLite.from(node, .5, { y: '+=50', opacity: 0 });
}
Can anyone suggest any way to tackle this?
I've tried using the TweenMax.ticker to get current parent left property but was unsure what to do with it
If you're still looking for a solution or someone else I forked your pen to make the items come from below.
You basically just need to translate them from half the width you're expanding the parent as long as you're using the same animation duration and easing.
see below for a container expanding from 0 to 130 as in your demo
TweenMax.from(items, 1, {y: '+=50'}); //not ok
TweenMax.from(items, 1, {y: '+=50', x:'-=65'}); //ok
The forked pen demo
I would like your suggestions here for the following points
Make peel effect more realistic if possible.
Reduce the size of the peel (to be able to see the full Ad).
Jsfiddle peel effect
I used a div pagePeelsmallrotate rotated at 24deg initially. Which i then animate its angle as well as dimensions .
Also div pagePeelcontainer dimensions are animated.
There are two functions
var pagePeelopen = function() {} /*opens the ad*/
var pagePeelclose = function() {} /*closes the ad */
I tried skewing only the pagePeelcontainer div and not its children to get small peel as provided here
css used to avoid child elements getting affected
#pagePeelcontainer > * {
transform:skew(-45deg);
}
But couldn't get the peel effect properly on click of open.
Jsfiddle skewed peel
Let me know how i can do it
Changing this section of code
$('#pagePeelcontainer').stop().animate({
width: 1000,
height: 1000
}, {
duration: 500
});
Will reveal the whole advert, as for the peeling it looks pretty good the way it is
Does anyone know how can we achieve this kind of view in React Native, or is there any available components out there that can help on this issue?
I've seen in F8 2016 app too, been searching on how to achieve the transition and the carousel-like view with horizontal scrolling.
I know that the question is old, but a co-worker and I recently had to create a component that answers this particular need. We ended up open-sourcing it, so it's all yours to try: react-native-snap-carousel.
The plugin is now built on top of FlatList (versions >= 3.0.0), which is great to handle huge numbers of items. It also provides previews (the effect you were after), snapping effect, parallax images, RTL support, and more.
You can take a look at the showcase to get a grasp of what can be achieved with it. Do not hesitate to share your experience with the plugin since we're always trying to improve it.
Edit : two new layouts have been introduced in version 3.6.0 (one with a stack of cards effect and the other with a tinder-like effect). Enjoy!
You can achieve this using ScrollView with paging enabled on iOS and ViewPagerAndroid on Android.
F8 being an open source app,
you can see that's what it's actually using:
https://github.com/fbsamples/f8app/blob/master/js/common/ViewPager.js
This component renders all pages.
If you only want to have the visible and left and right pages rendered to save memory, there's another component built on top of it that does it:
https://github.com/fbsamples/f8app/blob/master/js/common/Carousel.js
There are various other similar implementations available:
https://js.coach/react-native?search=carousel
https://js.coach/react-native?search=swiper
However I'm not recommending https://github.com/leecade/react-native-swiper as I've had several issues with it.
Speaking about the swiper-component claiming the best of the world, it still does not work out of the box (as of November 2018) as described in the official swiper-react-native documentation. The issue and a workaround is described in the swiper issue 444:
The error message (on Android) states console.error: "fontFamily 'Arial' is not a system font and has not been loaded through Exponent.Font.loadAsync.
Zach Dixon provided an elegant quick-fix which I repeat here for everybody's convenience. Simply use the following JSX-snippet inside your render()-function to avoid that a new font is required:
<Swiper style={styles.wrapper} showsButtons={true}
nextButton={<Text>></Text>} prevButton={<Text><</Text>}>
<View style={styles.slide1}><Text style>Slide 1</Text></View>
<View style={styles.slide2}><Text style>Slide 2</Text></View>
<View style={styles.slide3}><Text style>Slide 3</Text></View>
</Swiper>
For those interested in explanations on how to implement carousel with Scroll-View only, I recommend a tutorial on a simple image carousel with ScrollView. The tutorial is straight forward and elaborates on the things one has to take care of, but you cannot use it out of the box within or on top of other View-elements. In particular the snapping does not work to well (on Android).
You can create your own custom carousel. The Carousel end result looks like this-
goToNextPage = () => {
const childlenth = this.getCustomData().length;
selectedIndex = selectedIndex + 1;
this.clearTimer();
if (selectedIndex === childlenth) {
this.scrollRef.current.scrollTo({ offset: 0, animated: false, nofix: true });
selectedIndex = 1;
}
this.scrollRef.current.scrollTo({
animated: true,
x: this.props.childWidth * selectedIndex,
});
this.setUpTimer();
}
// pushing 1st element at last
getCustomData() {
const {data} = this.props;
const finaldata = [];
finaldata.push(...data);
finaldata.push(data[0]);
return finaldata;
}
This is the main logic used behind looped carousel.
Here we are pushing the first item at last in the list again and then when scroll reaches at last position we are making the scrollview to scroll to first position as first and last element are same now and we scroll to first position with animation like this
this.scrollRef.current.scrollTo({ offset: 0, animated: false, nofix: true });
For further reference go through the link provided.
https://goel-mohit56.medium.com/custom-horizontal-auto-scroll-looped-carousel-using-scrollview-42baa5262f95