Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,46 @@ Use `{ type: 'none' }` to apply values immediately without animation. Useful for

`onTransitionEnd` fires immediately with `{ finished: true }`.

### Per-Property Transitions

Pass a map instead of a single config to use different animation types per property category.

```tsx
<EaseView
animate={{ opacity: visible ? 1 : 0, translateY: visible ? 0 : 30 }}
transition={{
opacity: { type: 'timing', duration: 150, easing: 'easeOut' },
transform: { type: 'spring', damping: 12, stiffness: 200 },
}}
/>
```

Available category keys:

| Key | Properties |
| ----------------- | ---------------------------------------------------------------- |
| `default` | Fallback for categories not explicitly listed |
| `transform` | translateX, translateY, scaleX, scaleY, rotate, rotateX, rotateY |
| `opacity` | opacity |
| `borderRadius` | borderRadius |
| `backgroundColor` | backgroundColor |

Use `default` as a fallback for categories not explicitly listed:

```tsx
<EaseView
animate={{ opacity: 1, scale: 1.2, translateY: -20 }}
transition={{
default: { type: 'spring', damping: 15, stiffness: 120 },
opacity: { type: 'timing', duration: 200, easing: 'easeOut' },
}}
/>
```

When no `default` key is provided, the library default (timing 300ms easeInOut) applies to all categories.

> **Android note:** Android animates `backgroundColor` with `ValueAnimator` (timing only). If a per-property map specifies `type: 'spring'` for `backgroundColor`, it silently falls back to timing 300ms.

### Border Radius

`borderRadius` can be animated just like other properties. It uses hardware-accelerated platform APIs — `ViewOutlineProvider` + `clipToOutline` on Android and `layer.cornerRadius` + `layer.masksToBounds` on iOS. Unlike RN's style-based `borderRadius` (which uses a Canvas drawable on Android), this clips children properly and is GPU-accelerated.
Expand Down
343 changes: 261 additions & 82 deletions android/src/main/java/com/ease/EaseView.kt

Large diffs are not rendered by default.

54 changes: 5 additions & 49 deletions android/src/main/java/com/ease/EaseViewManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.ease
import android.graphics.Color
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.WritableMap
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.PixelUtil
Expand Down Expand Up @@ -126,56 +127,11 @@ class EaseViewManager : ReactViewManager() {
view.initialAnimateBorderRadius = PixelUtil.toPixelFromDIP(value)
}

// --- Transition config setters ---
// --- Transitions config (single ReadableMap) ---

@ReactProp(name = "transitionType")
fun setTransitionType(view: EaseView, value: String?) {
view.transitionType = value ?: "timing"
}

@ReactProp(name = "transitionDuration", defaultInt = 300)
fun setTransitionDuration(view: EaseView, value: Int) {
view.transitionDuration = value
}

@ReactProp(name = "transitionEasingBezier")
fun setTransitionEasingBezier(view: EaseView, value: ReadableArray?) {
if (value != null && value.size() == 4) {
view.transitionEasingBezier = floatArrayOf(
value.getDouble(0).toFloat(),
value.getDouble(1).toFloat(),
value.getDouble(2).toFloat(),
value.getDouble(3).toFloat()
)
} else {
// Fallback: easeInOut
view.transitionEasingBezier = floatArrayOf(0.42f, 0f, 0.58f, 1.0f)
}
}

@ReactProp(name = "transitionDamping", defaultFloat = 15f)
fun setTransitionDamping(view: EaseView, value: Float) {
view.transitionDamping = value
}

@ReactProp(name = "transitionStiffness", defaultFloat = 120f)
fun setTransitionStiffness(view: EaseView, value: Float) {
view.transitionStiffness = value
}

@ReactProp(name = "transitionMass", defaultFloat = 1f)
fun setTransitionMass(view: EaseView, value: Float) {
view.transitionMass = value
}

@ReactProp(name = "transitionLoop")
fun setTransitionLoop(view: EaseView, value: String?) {
view.transitionLoop = value ?: "none"
}

@ReactProp(name = "transitionDelay", defaultInt = 0)
fun setTransitionDelay(view: EaseView, value: Int) {
view.transitionDelay = value.toLong()
@ReactProp(name = "transitions")
fun setTransitions(view: EaseView, value: ReadableMap?) {
view.setTransitionsFromMap(value)
}

// --- Border radius ---
Expand Down
51 changes: 51 additions & 0 deletions example/src/demos/PerPropertyDemo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useState } from 'react';
import { Text, StyleSheet } from 'react-native';
import { EaseView } from 'react-native-ease';

import { Section } from '../components/Section';
import { Button } from '../components/Button';

export function PerPropertyDemo() {
const [active, setActive] = useState(false);
return (
<Section title="Per-Property Transitions">
<Text style={styles.hint}>
Opacity fades slowly (1.5s timing), transforms slide with a bouncy
spring — each category animates independently
</Text>
<EaseView
animate={{
opacity: active ? 1 : 0.2,
translateX: active ? 200 : 0,
}}
transition={{
opacity: { type: 'timing', duration: 1500, easing: 'easeInOut' },
transform: { type: 'spring', damping: 8, stiffness: 120, mass: 1 },
}}
style={styles.box}
/>
<Button
label={active ? 'Reset' : 'Animate'}
onPress={() => setActive((v) => !v)}
/>
</Section>
);
}

const styles = StyleSheet.create({
box: {
width: 80,
height: 80,
backgroundColor: '#4a90d9',
borderRadius: 12,
borderWidth: 2,
borderColor: '#7ab8ff',
alignItems: 'center',
justifyContent: 'center',
},
hint: {
fontSize: 13,
color: '#8888aa',
marginBottom: 12,
},
});
6 changes: 6 additions & 0 deletions example/src/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { SlideDemo } from './SlideDemo';
import { StyleReRenderDemo } from './StyleReRenderDemo';
import { StyledCardDemo } from './StyledCardDemo';
import { TransformOriginDemo } from './TransformOriginDemo';
import { PerPropertyDemo } from './PerPropertyDemo';

interface DemoEntry {
component: ComponentType;
Expand Down Expand Up @@ -80,6 +81,11 @@ export const demos: Record<string, DemoEntry> = {
title: 'Comparison',
section: 'Advanced',
},
'per-property': {
component: PerPropertyDemo,
title: 'Per-Property',
section: 'Advanced',
},
...(Platform.OS !== 'web'
? {
benchmark: {
Expand Down
Loading
Loading