UNPKG

@shopify/react-native-skia

Version:

High-performance React Native Graphics using Skia

308 lines (257 loc) 7.75 kB
import type { SharedValue } from "react-native-reanimated"; import { NodeType } from "../../dom/types"; import type { BlurMaskFilterProps, CircleProps, CTMProps, ImageProps, PaintProps, PointsProps, PathProps, RectProps, RoundedRectProps, OvalProps, LineProps, PatchProps, VerticesProps, DiffRectProps, TextProps, TextPathProps, TextBlobProps, GlyphsProps, PictureProps, ImageSVGProps, ParagraphProps, AtlasProps, BoxProps, BoxShadowProps, SkottieProps, DrawingNodeProps, } from "../../dom/types"; import type { AnimatedProps } from "../../renderer"; import { isSharedValue } from "../utils"; import { isColorFilter, isImageFilter, isPathEffect, isShader } from "../Node"; import type { SkPaint, BaseRecorder } from "../../skia/types"; import { CommandType } from "./Core"; import type { Command } from "./Core"; export interface Recording { commands: Command[]; paintPool: SkPaint[]; } interface AnimationValues { animationValues: Set<SharedValue<unknown>>; } export class Recorder implements BaseRecorder { commands: Command[] = []; cursors: Command[][] = []; animationValues: Set<SharedValue<unknown>> = new Set(); constructor() { this.cursors.push(this.commands); } getRecording(): Recording & AnimationValues { return { commands: this.commands, paintPool: [], animationValues: this.animationValues, }; } private processProps(props: Record<string, unknown>) { const animatedProps: Record<string, SharedValue<unknown>> = {}; let hasAnimatedProps = false; for (const key in props) { const prop = props[key]; if (isSharedValue(prop)) { this.animationValues.add(prop); animatedProps[key] = prop; hasAnimatedProps = true; } } return { props, animatedProps: hasAnimatedProps ? animatedProps : undefined, }; } private add(command: Command) { if (command.props) { const { animatedProps } = this.processProps( command.props as Record<string, unknown> ); if (animatedProps) { command.animatedProps = animatedProps; } } this.cursors[this.cursors.length - 1].push(command); } saveGroup(props?: AnimatedProps<Pick<DrawingNodeProps, "zIndex">>) { const children: Command[] = []; const group: Command = { type: CommandType.Group, children }; if (props) { group.props = props; } this.add(group); this.cursors.push(children); } restoreGroup() { this.cursors.pop(); } savePaint(props: AnimatedProps<PaintProps>, standalone: boolean) { this.add({ type: CommandType.SavePaint, props, standalone }); } restorePaint() { this.add({ type: CommandType.RestorePaint }); } restorePaintDeclaration() { this.add({ type: CommandType.RestorePaintDeclaration }); } materializePaint() { this.add({ type: CommandType.MaterializePaint }); } pushPathEffect(pathEffectType: NodeType, props: AnimatedProps<unknown>) { if (!isPathEffect(pathEffectType)) { throw new Error("Invalid color filter type: " + pathEffectType); } this.add({ type: CommandType.PushPathEffect, pathEffectType, props, }); } pushImageFilter(imageFilterType: NodeType, props: AnimatedProps<unknown>) { if (!isImageFilter(imageFilterType)) { throw new Error("Invalid color filter type: " + imageFilterType); } this.add({ type: CommandType.PushImageFilter, imageFilterType, props, }); } pushColorFilter(colorFilterType: NodeType, props: AnimatedProps<unknown>) { if (!isColorFilter(colorFilterType)) { throw new Error("Invalid color filter type: " + colorFilterType); } this.add({ type: CommandType.PushColorFilter, colorFilterType, props, }); } pushShader( shaderType: NodeType, props: AnimatedProps<unknown>, children: number ) { if (!isShader(shaderType) && !(shaderType === NodeType.Blend)) { throw new Error("Invalid color filter type: " + shaderType); } this.add({ type: CommandType.PushShader, shaderType, props, children }); } pushBlurMaskFilter(props: AnimatedProps<BlurMaskFilterProps>) { this.add({ type: CommandType.PushBlurMaskFilter, props }); } composePathEffect() { this.add({ type: CommandType.ComposePathEffect }); } composeColorFilter() { this.add({ type: CommandType.ComposeColorFilter }); } composeImageFilter() { this.add({ type: CommandType.ComposeImageFilter }); } saveCTM(props: AnimatedProps<CTMProps>) { this.add({ type: CommandType.SaveCTM, props }); } restoreCTM() { this.add({ type: CommandType.RestoreCTM }); } drawPaint() { this.add({ type: CommandType.DrawPaint }); } saveLayer() { this.add({ type: CommandType.SaveLayer }); } saveBackdropFilter() { this.add({ type: CommandType.SaveBackdropFilter }); } drawBox( boxProps: AnimatedProps<BoxProps>, shadows: { props: BoxShadowProps; animatedProps?: Record<string, SharedValue<unknown>>; }[] ) { shadows.forEach((shadow) => { if (shadow.props) { if (shadow.props) { const { animatedProps } = this.processProps( shadow.props as unknown as Record<string, unknown> ); if (animatedProps) { shadow.animatedProps = animatedProps; } } } }); this.add({ type: CommandType.DrawBox, props: boxProps, shadows }); } drawImage(props: AnimatedProps<ImageProps>) { this.add({ type: CommandType.DrawImage, props }); } drawCircle(props: AnimatedProps<CircleProps>) { this.add({ type: CommandType.DrawCircle, props }); } drawPoints(props: AnimatedProps<PointsProps>) { this.add({ type: CommandType.DrawPoints, props }); } drawPath(props: AnimatedProps<PathProps>) { this.add({ type: CommandType.DrawPath, props }); } drawRect(props: AnimatedProps<RectProps>) { this.add({ type: CommandType.DrawRect, props }); } drawRRect(props: AnimatedProps<RoundedRectProps>) { this.add({ type: CommandType.DrawRRect, props }); } drawOval(props: AnimatedProps<OvalProps>) { this.add({ type: CommandType.DrawOval, props }); } drawLine(props: AnimatedProps<LineProps>) { this.add({ type: CommandType.DrawLine, props }); } drawPatch(props: AnimatedProps<PatchProps>) { this.add({ type: CommandType.DrawPatch, props }); } drawVertices(props: AnimatedProps<VerticesProps>) { this.add({ type: CommandType.DrawVertices, props }); } drawDiffRect(props: AnimatedProps<DiffRectProps>) { this.add({ type: CommandType.DrawDiffRect, props }); } drawText(props: AnimatedProps<TextProps>) { this.add({ type: CommandType.DrawText, props }); } drawTextPath(props: AnimatedProps<TextPathProps>) { this.add({ type: CommandType.DrawTextPath, props }); } drawTextBlob(props: AnimatedProps<TextBlobProps>) { this.add({ type: CommandType.DrawTextBlob, props }); } drawGlyphs(props: AnimatedProps<GlyphsProps>) { this.add({ type: CommandType.DrawGlyphs, props }); } drawPicture(props: AnimatedProps<PictureProps>) { this.add({ type: CommandType.DrawPicture, props }); } drawImageSVG(props: AnimatedProps<ImageSVGProps>) { this.add({ type: CommandType.DrawImageSVG, props }); } drawParagraph(props: AnimatedProps<ParagraphProps>) { this.add({ type: CommandType.DrawParagraph, props }); } drawAtlas(props: AnimatedProps<AtlasProps>) { this.add({ type: CommandType.DrawAtlas, props }); } drawSkottie(props: AnimatedProps<SkottieProps>) { this.add({ type: CommandType.DrawSkottie, props }); } }