Skip to content
Native Animation
Four Seasons Tree Scroll animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add four-seasons-tree-scroll
Skia

Four Seasons Tree Scroll

A scroll-driven seasonal tree with growing blossoms and falling leaves, with configurable chapter content, action label, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A scroll-driven seasonal tree with growing blossoms and falling leaves, with configurable chapter content, action label, and fonts.

Why this one

The setting stays recognizable throughout the year instead of replacing a menu photo every season. Snow follows the upper edges of actual branches, and falling leaves remain on the hill. Both effects reverse naturally when you scroll backward. The Ember & Oat demo pairs the tree with a seasonal café menu and a working full-menu button.

How it works

  1. Generate recursive branch geometry and 400 branch-tip particles once with seed 170.
  2. Measure the component with onLayout; each viewport height is one chapter.
  3. Derive one continuous progress p, from 0 to 3, from useAnimatedScrollHandler and scrollY. Skia paths, sky, hill light, snow thickness, and seasonal colors follow it on the UI thread.
  4. Rest around chapter centers (20% on each side), then use smoothstep for bloom and leaf-fall poses. Leaves follow individual delayed paths with sinusoidal wind and settle at the foot of the tree.
  5. Stack atmospheric photos with entry-only opacity. Add dark top/bottom veils and nonoverlapping headline windows: entry (p - (c - 0.52)) / 0.4, exit (p - (c + 0.14)) / 0.28.
  6. Programmatic travel drives scrollTo, so autoplay and touch use the same scroll-derived state. The demo clock starts its winter-to-spring transition immediately, advances every 2.6 seconds with a 2.2-second (0.77, 0, 0.175, 1) move, and resets behind a black fade. Touch permanently stops the current autoplay session.

Install

npx expo install @shopify/react-native-skia@2.14.0 expo-blur@~58.0.5 expo-image@~58.0.17 expo-linear-gradient@~58.0.3 react-native-reanimated@4.7.1 react-native-worklets@0.13.0

Copy this folder, excluding demo/, into your project. Follow the installed Skia version's web setup and load CanvasKit before rendering this component on web (for example, a lazy component under WithSkiaWeb). Supply fonts from your host app; the core has no dependency on catalog fonts or demo assets. Icons: Lucide (ISC); copied paths are in icons.ts.

Usage

import FourSeasonsTreeScroll from './four-seasons-tree-scroll';

<FourSeasonsTreeScroll onMenuPress={() => openMenu()} />;

Customize the four chapter objects and typography:

import { useRef } from 'react';
import {
  FourSeasonsTreeScroll,
  type FourSeasonsTreeScrollRef,
} from './four-seasons-tree-scroll';

const story = useRef<FourSeasonsTreeScrollRef>(null);

<FourSeasonsTreeScroll
  ref={story}
  chapters={seasonalChapters}
  menuTitle="The year in four cups"
  ctaLabel="Browse the menu"
  fontFamily="YourInterfaceFont"
  serifFontFamily="YourSerifFont"
  italicFontFamily="YourItalicFont"
  monoFontFamily="YourMonoFont"
  onMenuPress={() => openMenu()}
  onChapterChange={(chapter) => trackSeason(chapter)}
/>;

story.current?.scrollToChapter(1, 2200);
story.current?.reset();

seasonalChapters is a four-item tuple of SeasonChapter: season, title, subtitle, description, drink, price, accent, optional image and optional background. Images accept React Native image sources. Load local assets before the story starts.

Props

PropTypeDefaultDescription
chaptersreadonly four-item SeasonChapter tupleGeneric seasonal copyText, accents, menu photos, atmospheric backgrounds
headerReactNode—Optional brand lockup
menuTitlestringA seasonal storyHeader caption
ctaLabelstringExplore the menuFinal button text and accessibility label
onMenuPress() => void—Final button action
onChapterChange(chapter: number) => void—Chapter callback, on the RN thread
onInteraction() => void—Stop host autoplay when touched or dragged
fontFamilystringSystemInterface text
serifFontFamilystringSystemFirst headline line
italicFontFamilystringSystem italicAccent headline line
monoFontFamilystringSystemChapter counter and prices
snapToChaptersbooleantrueSnap on native platforms only
styleStyleProp<ViewStyle>—Root layout overrides
testIDstring—Root test identifier
durationnumber900Scroll hint half-cycle in milliseconds. Spring physics and reduced-motion timing are unchanged.
chaptersreadonly [ SeasonChapter, SeasonChapter, SeasonChapter, SeasonChapter, ]DEFAULT_CHAPTERSFour seasonal chapters, in winter-to-fall order.
headerReactNodeundefinedOptional header, such as a brand lockup.
menuTitlestring'A seasonal story'Label above the seasonal story.
ctaLabelstring'Explore the menu'Final button label.
onMenuPress() => voidundefinedFinal button action.
onChapterChange( chapter: number, ) => voidundefinedCalled on chapter changes on the React Native thread.
onInteraction() => voidundefinedCalled when a touch or drag stops demo playback.
fontFamilystringundefinedInterface font; falls back to the system font.
serifFontFamilystringundefinedEditorial headline font.
italicFontFamilystringundefinedItalic headline font.
monoFontFamilystringundefinedChapter counter font.
snapToChaptersbooleantrueEnable chapter snap on native platforms.
styleStyleProp<ViewStyle>undefinedOuter layout overrides.
testIDstringundefinedRoot test identifier.

Ref methods: scrollToChapter(chapter, duration = 1000) clamps to 0–3; reset() returns to winter behind a 220 ms black fade, then reveals over 450 ms. The autoplay clock is confined to the demo.

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, Skia 2.14.0. Intended for iOS, Android, and web, including SDK-compatible Expo Go. Native Android blur uses BlurTargetView with dimezisBlurViewSdk31Plus; older Android versions retain the tinted glass fallback.

This folder does not register itself in the host catalog; load Skia lazily when adding it there.

Accessibility

Each active chapter exposes a heading, description, drink, and price; inactive chapters are hidden from screen readers. Four labeled chapter buttons provide a touch alternative to scrolling, with selected states and 44 pt hit areas. The final CTA is inside scroll content and has a button role and label. The demo's menu has a labeled close button and Android back support.

Reduced motion uses discrete chapters with 200 ms transitions, no headline movement or falling snow, and instant programmatic scrolling. There is no parallax, zoom, or velocity trail. Button scale feedback stays subtle at 0.97. Layout uses the component's own dimensions and reserves the upper-right corner for the host viewer's close control.

Customization notes

Set chapters[].accent to theme the seasonal headings and navigation.