ScrollView
<SkiaScrollView />
Use <SkiaScrollView />
as a replacement for the React Native <ScrollView />
component.
It uses the Skia rendering engine to render the content so you can't use React Native components inside it.
You must specify the height
prop of ScrollGestureProps to make the list scrollable.
Example
const paint = Skia.Paint();
paint.setColor(Skia.Color("rgb(91, 128, 218)"));
const circleCount = 100;
<SkiaScrollView height={circleCount * 100} style={{ flex: 1 }}>
{Array.from({ length: circleCount }, (_, i) => (
<Circle key={i} cx={50} cy={50 + i * 100} r={50} paint={paint} />
))}
</SkiaScrollView>
Example with useSkiaScrollView
You can manage the list state yourself by using useSkiaScrollView
:
This is useful when you need to build custom behavior on top of the list, e.g. custom gestures/renderer.
const state = useSkiaScrollView({ height: 1000 });
const content = content.value;
content.addChild(SkiaDomApi.RectNode({ width: 100, height: 100, x: 0, y: 0 }));
<SkiaScrollView list={state} style={{ flex: 1 }} />
SkiaScrollViewElementProps
list?: SkiaScrollViewState
Manage the list state yourself using useSkiaScrollView
:
const list = useSkiaScrollView({ height: 1000 });
// do something with the list, e.g. skew the list content:
list.matrix.value[1] = 0.1;
<SkiaScrollView list={list} />
style?: ViewStyle
The view style of the canvas style.
You should specify flex: 1
to ensure the canvas fills the screen.
<SkiaScrollView style={{ flex: 1 }} />
children?: ReactNode
Use children to render skia elements.
You must manually specify the height
prop of ScrollGestureProps to make the list scrollable.
<SkiaScrollView height={100} style={{ flex: 1 }}>
<Circle cx={50} cy={50 + 100} r={50} color="blue" />
</SkiaScrollView>
If you are updating the children very fequently, you should consider using useSkiaScrollView
to improve performance by imperatively updating the list content and therefore avoiding the React reconciler.
const state = useSkiaScrollView({ height: 1000 });
const content = content.value;
let previous = null;
// do something with the list content, e.g. add a new rect every 10ms
setInterval(() => {
if (previous) content.removeChild(previous);
previous = SkiaDomApi.RectNode({
width: 100, height: 100,
x: Math.random() * 1000,
y: Math.random() * 1000
});
content.addChild(previous);
}, 10);
fixedChildren?: ReactNode
Use fixedChildren
to render skia elements that are displayed fixed on top of the list content and are not scrollable.
<SkiaScrollView fixedChildren={<Fill color="blue" />} />
debug?: boolean
Enable debug mode to show the FPS count and render time.
keyboardDismissMode?: "none" | "interactive" | "on-drag"
Determines whether the keyboard gets dismissed in response to a drag.
none
(the default) drags do not dismiss the keyboard.onDrag
the keyboard is dismissed when a drag begins.interactive
the keyboard is dismissed interactively with the drag and moves in synchrony with the touch; dragging upwards cancels the dismissal.
keyboardShouldPersistTaps?: boolean | "always" | "never"
Determines when the keyboard should stay visible after a tap.
never
(the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won`t receive the tap.always
, the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.false
, deprecated, usenever
insteadtrue
, deprecated, usealways
instead
ScrollGestureInitalState
layout: SharedValue<object>
Contains the width and height of the scroll view. Will automatically update when the layout changes.
width: number
height: number
scrollY: SharedValue<number>
The current scroll Y position.
startY: SharedValue<number>
Used internally to keep track of the start Y position when dragging.
offsetY: SharedValue<number>
Used to indepentently control the scroll position without affecting the scrollY value (which is used by the gesture handler) e.g. used for keyboard handling.
maxHeight: SharedValue<number>
The maximum height the scroll view can scroll to. Automatically updated when the layout/data changes.
scrolling: SharedValue<boolean>
Shared value that indicates if the view is currently being scrolled.
scrollingDisabled: SharedValue<boolean>
Shared value to disable scrolling.
bounces: boolean
If the scroll view should bounce when reaching the top or bottom.
decelerationRate: number
The deceleration rate for momentum scrolling. Default is 0.998.
ScrollGestureProps
height?: number
Sets the initial value for maxHeight
while taking the layout height into consideration.
You need to set this value for SkiaScrollView to be able to scroll.
inverted?: boolean
Inverts the scroll direction of the Gesture Handler. Used in conjunction with inverted
of SkiaScrollViewProps.
onScroll?: (value) => void
Callback that is invoked every time the scroll position changes. Needs to be a worklet function. You need to implement throttling yourself:
function onScroll(event: ScrollEvent) {
"worklet";
const { scrollY } = event;
// do something with scrollY, e.g. update the position of a sticky header
}
<SkiaScrollView onScroll={onScroll} />
onScrollBeginDrag?: () => void
Callback that is invoked when the user starts dragging the scroll view. Needs to be a worklet function.
onScrollEndDrag?: () => void
Callback that is invoked when the user stops dragging the scroll view. Needs to be a worklet function.
onMomentumScrollEnd?: () => void
Callback that is invoked when the momentum scroll begins. Needs to be a worklet function.
onMomentumScrollBegin?: () => void
Callback that is invoked when the momentum scroll ends. Needs to be a worklet function.
startedAnimation?: () => void
Call this function when you start animating an SharedValue to enable continous rendering mode.
finishedAnimation?: () => void
Call this function when you finish animating an SharedValue to return to default rendering mode.
Ensure that you have the equal amount of startedAnimation
and finishedAnimation
calls to avoid unecessary re-renders when idling.
ScrollGestureState
gesture: GestureType
scrollTo: (opts) => void
Scroll to a certain Y position.
scrollToEnd: (opts) => void
Scroll to the end of the scroll view (inferred by maxHeight).
scrollToStart: (opts) => void
Scroll to start of the scroll view.
startMomentumScroll: (velocityY) => void
If you manually handle scrolling, e.g. with a custom ScrollBar, you can call this function to start momentum scrolling.
useSkiaScrollView(SkiaScrollViewProps
): SkiaScrollViewState
Use this hook to manage and access the state of SkiaScrollView.
const state = useSkiaScrollView({ height: 1000 });
state.content.value.addChild(SkiaDomApi.RectNode({ width: 100, height: 100, x: 0, y: 0 }));
<SkiaScrollView list={state} style={{ flex: 1 }} />
InitialScrollViewState
root: SharedValue<RenderNode<GroupProps>>
The root group node of the skia list view with a fixed position that contains the content
group node
You can transform the entire list by setting the matrix
property.
root.value.setProp("matrix", Skia.Matrix().skew(1, 0.5).get());
content: SharedValue<RenderNode<GroupProps>>
The content group node of the skia list view that contains the list items which are translated based on the scroll position.
matrix: SharedValue<SkMatrix>
The Skia Matrix that translates the content node.
matrix.value[0]
is the X scale.matrix.value[4]
is the Y scale.matrix.value[5]
is the Y translation. UsescrollY
insteadmatrix.value[2]
is the X translation. UsesafeArea.left
insteadmatrix.value[1]
is the X skew.matrix.value[3]
is the Y skew.
redraw: () => void
Call redraw()
to request a redraw of the skia canvas, e.g. when adding a fixed element to the root node.
When using FlatList use redrawItems()
instead to redraw the list items.
When animating a property use startedAnimation()
and finishedAnimation()
to efficiently rerender the list.
pressing: SharedValue<boolean>
Shared value that indicates if the view is currently being pressed.
SkiaScrollViewProps
customScrollGesture?: typeof getScrollGesture
Specify a custom scroll gesture.
customGesture?: (props) => ComposedGesture
Specify a custom gesture handler. E.g. to implement scrolling and swiping list items horizontally.
safeArea?: EdgeInsets
Specify offsets for the content of the scroll view.
e.g. { top: 30, bottom: 20, left: 15, right: 15 }
automaticallyAdjustKeyboardInsets?: boolean
Set to false
to disable the automatic keyboard adjustment.
keyboard?: ReanimatedContext
Specify a custom keyboard handler
inverted?: boolean
SkiaScrollView so that the elements start rendering from the bottom screen to the top.
mode?: "default" | "continuous"
manually overwrite the rendering mode. The default value is undefined meaning it only rerenders when needed