{"id":19076112,"url":"https://github.com/gapur/react-native-scrollable-animated-header","last_synced_at":"2025-04-16T14:27:30.212Z","repository":{"id":38868453,"uuid":"277592601","full_name":"Gapur/react-native-scrollable-animated-header","owner":"Gapur","description":"🤯 React Native Animated Header with ScrollView","archived":false,"fork":false,"pushed_at":"2023-01-06T10:46:38.000Z","size":3980,"stargazers_count":114,"open_issues_count":11,"forks_count":15,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T05:22:42.261Z","etag":null,"topics":["animated","animation","react","react-hooks","react-native","scrollview"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gapur.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-06T16:24:12.000Z","updated_at":"2025-02-28T03:14:38.000Z","dependencies_parsed_at":"2023-02-05T22:16:37.540Z","dependency_job_id":null,"html_url":"https://github.com/Gapur/react-native-scrollable-animated-header","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-scrollable-animated-header","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-scrollable-animated-header/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-scrollable-animated-header/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gapur%2Freact-native-scrollable-animated-header/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gapur","download_url":"https://codeload.github.com/Gapur/react-native-scrollable-animated-header/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249250427,"owners_count":21237895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animated","animation","react","react-hooks","react-native","scrollview"],"created_at":"2024-11-09T01:57:04.521Z","updated_at":"2025-04-16T14:27:30.192Z","avatar_url":"https://github.com/Gapur.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg width=\"300\"src=\"https://github.com/Gapur/react-native-scrollable-animated-header/blob/master/assets/react-native-example.gif\"\u003e\n\u003c/p\u003e\n\n# React Native ScrollView Animated Header\n\nReact Native Animated Header App with ScrollView\n\nAnimated header is the most common design pattern in today’s apps. Animations are an important part of mobile applications.\n\n## Getting Started\n\n1. Clone this repository\n```\ngit clone https://github.com/Gapur/react-native-scrollview-animated-header.git\n```\n2. Install dependencies\n```\nyarn\n```\n3. Launch app\n```\nnpm run ios # for npm\n```\n\n## Making Magic Code\n\nWe need to define some constants for the animated header which will be used to interpolate the scroll position value.\n\n```js\nconst HEADER_MAX_HEIGHT = 240;\nconst HEADER_MIN_HEIGHT = 84;\nconst HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT;\n```\n\nI will display user data with ScrollView component. So We should to change App.js file like this:\n\n```js\nconst HEADER_MAX_HEIGHT = 240; // max header height\nconst HEADER_MIN_HEIGHT = 84; // min header height\nconst HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT; // header scrolling value\n\n// create array by 10 fake user data\nconst DATA = Array(10)\n  .fill(null)\n  .map((_, idx) =\u003e ({\n    id: idx,\n    avatar: faker.image.avatar(),\n    fullName: `${faker.name.firstName()} ${faker.name.lastName()}`,\n  }));\n\nfunction App() {\n  const renderListItem = (item) =\u003e (\n    \u003cView key={item.id} style={styles.card}\u003e\n      \u003cImage style={styles.avatar} source={{uri: item.avatar}} /\u003e\n      \u003cText style={styles.fullNameText}\u003e{item.fullName}\u003c/Text\u003e\n    \u003c/View\u003e\n  );\n\n  return (\n    \u003cSafeAreaView style={styles.saveArea}\u003e\n      \u003cScrollView contentContainerStyle={{ paddingTop: HEADER_MAX_HEIGHT - 32 }}\u003e // it should be under the header \n        {DATA.map(renderListItem)}\n      \u003c/ScrollView\u003e\n    \u003c/SafeAreaView\u003e\n  );\n}\n```\n\nWe need to create the header under the ScrollView. We will use Animated.View\n\n```js\n\u003cAnimated.View\n  style={[\n    styles.topBar,\n    {\n      transform: [{scale: titleScale}, {translateY: titleTranslateY}],\n    },\n  ]}\u003e\n  \u003cText style={styles.title}\u003eManagement\u003c/Text\u003e\n\u003c/Animated.View\u003e\n```\n\n## Magic Animation\n\nReact Native provides Animated API for animations. Animated API focuses on declarative relationships between inputs and outputs, with configurable transforms in between, and start/stop methods to control time-based animation execution.\n\nWe are going to use Animated.ScrollView to make the scroll view, and attach a callback to listen to the onScroll event when it is changed. Then, using interpolation to map value between the y-axis and opacity. The interpolation maps input ranges to output ranges, typically using a linear interpolation but also supports easing functions.\n\nLet’s update our App.js file with the following lines of code:\n\n```js\nconst scrollY = useRef(new Animated.Value(0)).current; // our animated value\n\n// our header y-axis animated from 0 to HEADER_SCROLL_DISTANCE,\n// we move our element for -HEADER_SCROLL_DISTANCE at the same time.\nconst headerTranslateY = scrollY.interpolate({\n  inputRange: [0, HEADER_SCROLL_DISTANCE],\n  outputRange: [0, -HEADER_SCROLL_DISTANCE],\n  extrapolate: 'clamp',\n});\n\n// our opacity animated from 0 to 1 and our opacity will be 0\nconst imageOpacity = scrollY.interpolate({\n  inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],\n  outputRange: [1, 1, 0],\n  extrapolate: 'clamp',\n});\nconst imageTranslateY = scrollY.interpolate({\n  inputRange: [0, HEADER_SCROLL_DISTANCE],\n  outputRange: [0, 100],\n  extrapolate: 'clamp',\n});\n\n// change header title size from 1 to 0.9\nconst titleScale = scrollY.interpolate({\n  inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],\n  outputRange: [1, 1, 0.9],\n  extrapolate: 'clamp',\n});\n// change header title y-axis\nconst titleTranslateY = scrollY.interpolate({\n  inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],\n  outputRange: [0, 0, -8],\n  extrapolate: 'clamp',\n});\n```\nAbove, we use useRef to persist Animated value. useRef returns a mutable ref object whose .current property is initialized to the passed argument.\n\nNext, We need to update Animated value when we are scrolling ScrollView. We will catch event by Animated.event. It maps directly to animated value scrollY and update it when we are panning or scrolling.\n\nWe can use the native driver by specifying useNativeDriver: true in our animation configuration.\n\nLet’s animate our components by the following codes:\n\n```js\n\u003cSafeAreaView style={styles.saveArea}\u003e\n  \u003cAnimated.ScrollView\n    contentContainerStyle={{ paddingTop: HEADER_MAX_HEIGHT - 32 }}\n    scrollEventThrottle={16} // \n    onScroll={Animated.event(\n      [{ nativeEvent: { contentOffset: { y: scrollY } } }], // event.nativeEvent.contentOffset.x to scrollX\n      { useNativeDriver: true }, // use native driver for animation\n    )}\u003e\n    {DATA.map(renderListItem)}\n  \u003c/Animated.ScrollView\u003e\n  \u003cAnimated.View\n    style={[styles.header, { transform: [{ translateY: headerTranslateY }] }]}\u003e\n    \u003cAnimated.Image\n      style={[\n        styles.headerBackground,\n        {\n          opacity: imageOpacity,\n          transform: [{ translateY: imageTranslateY }],\n        },\n      ]}\n      source={require('./assets/management.jpg')}\n    /\u003e\n  \u003c/Animated.View\u003e\n  \u003cAnimated.View\n    style={[\n      styles.topBar,\n      {\n        transform: [{ scale: titleScale }, { translateY: titleTranslateY }],\n      },\n    ]}\u003e\n    \u003cText style={styles.title}\u003eManagement\u003c/Text\u003e\n  \u003c/Animated.View\u003e\n\u003c/SafeAreaView\u003e\n```\n\n## Article on Medium\n\n[React Native Scrollable Animated Header](https://medium.com/javascript-in-plain-english/react-native-scrollable-animated-header-6dfe453d7078)\n\n## How to contribute?\n\n1. Fork this repo\n2. Clone your fork\n3. Code 🤓\n4. Test your changes\n5. Submit a PR!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgapur%2Freact-native-scrollable-animated-header","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgapur%2Freact-native-scrollable-animated-header","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgapur%2Freact-native-scrollable-animated-header/lists"}