CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-redash

Utility library for React Native Reanimated and Gesture Handler providing mathematical functions, animations, transformations, and helper utilities for building complex gesture-driven animations.

Pending
Overview
Eval results
Files

transforms.mddocs/

Transform Utilities

Transform utilities for applying transformations with custom origins and creating animated styles with translation effects.

import type { TransformsStyle } from "react-native";

Capabilities

Transform Types

/**
 * React Native transform array type
 */
type RNTransform = Exclude<TransformsStyle["transform"], undefined>;

Transform Origin

Apply transformations around a custom pivot point instead of the default center.

/**
 * Apply transformations with custom origin point
 * @param origin - Origin point for transformations
 * @param transformations - Array of React Native transform objects
 * @returns Transform array with origin translations
 */
function transformOrigin(
  origin: Vector,
  transformations: RNTransform
): RNTransform;

/**
 * Apply 2D transformations with custom origin point
 * @param origin - Origin point for transformations
 * @param transformations - Array of 2D transform objects
 * @returns Transform array with origin translations
 */
function transformOrigin2d(
  origin: Vector,
  transformations: Transforms2d
): Transforms2d;

Usage Example:

import { transformOrigin, vec2 } from "react-native-redash";
import { useAnimatedStyle, useSharedValue } from "react-native-reanimated";

export const CustomOriginRotation = () => {
  const rotation = useSharedValue(0);
  
  const animatedStyle = useAnimatedStyle(() => {
    // Rotate around top-left corner instead of center
    const topLeft = vec2(0, 0);
    
    const transform = transformOrigin(topLeft, [
      { rotate: `${rotation.value}deg` },
      { scale: 1.2 }
    ]);
    
    return { transform };
  });
  
  return (
    <Animated.View 
      style={[
        { width: 100, height: 100, backgroundColor: 'blue' },
        animatedStyle
      ]} 
    />
  );
};

Translation Hook

Convenient hook for creating translation-based animated styles.

/**
 * Hook for translation animations using vector of SharedValues
 * @param vector - Vector with SharedValue components for x and y translation
 * @returns Animated style object with translation transform
 */
function useTranslation(vector: Vector<Animated.SharedValue<number>>): {
  transform: { translateX: number; translateY: number }[];
};

Usage Example:

import { useTranslation, useVector } from "react-native-redash";
import { useAnimatedGestureHandler } from "react-native-reanimated";
import { PanGestureHandler } from "react-native-gesture-handler";

export const DraggableElement = () => {
  const position = useVector(0, 0);
  const offset = useVector(0, 0);
  
  // Use the translation hook for convenient styling
  const translationStyle = useTranslation(position);
  
  const gestureHandler = useAnimatedGestureHandler({
    onStart: () => {
      offset.x.value = position.x.value;
      offset.y.value = position.y.value;
    },
    onActive: (event) => {
      position.x.value = offset.x.value + event.translationX;
      position.y.value = offset.y.value + event.translationY;
    }
  });
  
  return (
    <PanGestureHandler onGestureEvent={gestureHandler}>
      <Animated.View 
        style={[
          { width: 100, height: 100, backgroundColor: 'red' },
          translationStyle
        ]} 
      />
    </PanGestureHandler>
  );
};

Advanced Transform Origin Examples

import { transformOrigin, transformOrigin2d, vec2 } from "react-native-redash";
import { useAnimatedStyle, useSharedValue } from "react-native-reanimated";

export const AdvancedTransforms = () => {
  const rotation = useSharedValue(0);
  const scale = useSharedValue(1);
  
  // Rotate around bottom-right corner
  const bottomRightRotation = useAnimatedStyle(() => {
    const bottomRight = vec2(100, 100); // Assuming 100x100 element
    
    return {
      transform: transformOrigin(bottomRight, [
        { rotate: `${rotation.value}deg` }
      ])
    };
  });
  
  // Scale from top-center
  const topCenterScale = useAnimatedStyle(() => {
    const topCenter = vec2(50, 0); // Center horizontally, top vertically
    
    return {
      transform: transformOrigin(topCenter, [
        { scale: scale.value }
      ])
    };
  });
  
  // Combined transformations with custom origin
  const combinedTransform = useAnimatedStyle(() => {
    const customOrigin = vec2(75, 25); // 3/4 right, 1/4 down
    
    return {
      transform: transformOrigin(customOrigin, [
        { rotate: `${rotation.value}deg` },
        { scale: scale.value },
        { skewX: `${rotation.value * 0.1}deg` }
      ])
    };
  });
  
  // Using 2D transform types
  const matrix2dTransform = useAnimatedStyle(() => {
    const origin = vec2(50, 50);
    
    const transforms = transformOrigin2d(origin, [
      { rotateZ: `${rotation.value}deg` },
      { scaleX: scale.value },
      { scaleY: scale.value * 0.8 }
    ]);
    
    return { transform: transforms };
  });
  
  return (
    <View style={{ padding: 20 }}>
      <Animated.View style={[styles.box, bottomRightRotation]} />
      <Animated.View style={[styles.box, topCenterScale]} />
      <Animated.View style={[styles.box, combinedTransform]} />
      <Animated.View style={[styles.box, matrix2dTransform]} />
    </View>
  );
};

Interactive Transform Origin

import React from "react";
import { View, StyleSheet } from "react-native";
import { transformOrigin, useVector, vec2 } from "react-native-redash";
import { 
  useAnimatedGestureHandler,
  useAnimatedStyle,
  useSharedValue
} from "react-native-reanimated";
import { PanGestureHandler, RotationGestureHandler } from "react-native-gesture-handler";

export const InteractiveTransformOrigin = () => {
  const rotation = useSharedValue(0);
  const originPosition = useVector(50, 50); // Center of 100x100 element
  const elementPosition = useVector(100, 100);
  
  // Drag to change transform origin
  const originGestureHandler = useAnimatedGestureHandler({
    onActive: (event) => {
      originPosition.x.value = event.x;
      originPosition.y.value = event.y;
    }
  });
  
  // Rotate the element
  const rotationGestureHandler = useAnimatedGestureHandler({
    onActive: (event) => {
      rotation.value = event.rotation;
    }
  });
  
  // Element style with custom origin
  const elementStyle = useAnimatedStyle(() => {
    const origin = vec2(originPosition.x.value, originPosition.y.value);
    
    return {
      transform: transformOrigin(origin, [
        { rotate: `${rotation.value}rad` }
      ])
    };
  });
  
  // Origin indicator style
  const originStyle = useAnimatedStyle(() => ({
    transform: [
      { translateX: originPosition.x.value - 5 },
      { translateY: originPosition.y.value - 5 }
    ]
  }));
  
  return (
    <View style={styles.container}>
      {/* Transform origin indicator */}
      <PanGestureHandler onGestureEvent={originGestureHandler}>
        <Animated.View style={[styles.origin, originStyle]} />
      </PanGestureHandler>
      
      {/* Rotatable element */}
      <RotationGestureHandler onGestureEvent={rotationGestureHandler}>
        <Animated.View style={[styles.element, elementStyle]} />
      </RotationGestureHandler>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f0f0'
  },
  element: {
    position: 'absolute',
    left: 100,
    top: 100,
    width: 100,
    height: 100,
    backgroundColor: 'blue',
    borderRadius: 10
  },
  origin: {
    position: 'absolute',
    width: 10,
    height: 10,
    borderRadius: 5,
    backgroundColor: 'red',
    borderWidth: 1,
    borderColor: 'white'
  }
});

Practical Use Cases

import { transformOrigin, vec2 } from "react-native-redash";

// 1. Clock hands rotating from center-bottom
const clockHandStyle = useAnimatedStyle(() => {
  const handOrigin = vec2(2, 100); // 2px from left (hand width/2), 100px from top (hand length)
  
  return {
    transform: transformOrigin(handOrigin, [
      { rotate: `${hourAngle.value}deg` }
    ])
  };
});

// 2. Card flipping from left edge
const cardFlipStyle = useAnimatedStyle(() => {
  const leftEdge = vec2(0, cardHeight / 2);
  
  return {
    transform: transformOrigin(leftEdge, [
      { rotateY: `${flipAngle.value}deg` }
    ])
  };
});

// 3. Door opening from hinges
const doorStyle = useAnimatedStyle(() => {
  const hingePoint = vec2(0, doorHeight / 2);
  
  return {
    transform: transformOrigin(hingePoint, [
      { rotateZ: `${doorAngle.value}deg` }
    ])
  };
});

// 4. Scaling from bottom (like growing plants)
const growthStyle = useAnimatedStyle(() => {
  const bottomCenter = vec2(elementWidth / 2, elementHeight);
  
  return {
    transform: transformOrigin(bottomCenter, [
      { scaleY: growthScale.value }
    ])
  };
});

Important Notes:

  • Transform origin calculations are done by adding translation transforms before and after the main transforms
  • The pattern is: translate to origin → apply transforms → translate back
  • Works with both React Native transforms and custom 2D transform types
  • useTranslation is a convenience hook that's equivalent to manually creating translation transforms
  • All transform origins are relative to the element's coordinate system (0,0 = top-left)

Install with Tessl CLI

npx tessl i tessl/npm-react-native-redash

docs

animations.md

colors.md

components.md

coordinates.md

index.md

math.md

matrices.md

paths.md

transforms.md

transitions.md

utilities.md

vectors.md

tile.json