-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
444 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
:: BASE_DOC :: | ||
|
||
## API | ||
### Base Props | ||
|
||
临时md,后期会自动生成 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import React, { useEffect, useMemo } from 'react'; | ||
import { GestureDetector, Gesture, Directions } from 'react-native-gesture-handler'; | ||
import Animated, { | ||
useSharedValue, | ||
useAnimatedStyle, | ||
withDelay, | ||
withTiming, | ||
runOnJS, | ||
Easing, | ||
} from 'react-native-reanimated'; | ||
import { wrapStyleTransforms, flattenStyle, TRANSFORM_STYLE_PROPERTIES } from './utils'; | ||
import { AnimationProps, DIRECTION, AnimationDirections } from './types'; | ||
|
||
export const AnimationView = React.forwardRef<unknown, AnimationProps>((props) => { | ||
const { | ||
style, | ||
duration = 300, | ||
delay = 0, | ||
easing = Easing.bezierFn(0.42, 0, 0.58, 1), | ||
animation, | ||
placement = 'center', | ||
gesture, | ||
gestureCallback, | ||
gestureDirection, | ||
...rest | ||
} = props; | ||
const offset = useSharedValue({ x: 0, y: 0 }); | ||
const timestamp = useSharedValue(0); | ||
|
||
const direction: AnimationDirections = gestureDirection ?? DIRECTION[placement] ?? 0; | ||
const timeConfig = useMemo(() => ({ duration, easing }), [duration, easing]); | ||
|
||
const fromStyle = wrapStyleTransforms(animation?.from); | ||
const fromFlatten = flattenStyle(fromStyle); | ||
const toStyle = wrapStyleTransforms(animation?.to); | ||
const toFlatten = flattenStyle(toStyle); | ||
|
||
const animationSharedValue = useSharedValue(fromFlatten); | ||
|
||
const animatedStyles = useAnimatedStyle(() => { | ||
const result: any = {}; | ||
Object.keys(animationSharedValue.value).forEach((key) => { | ||
if (TRANSFORM_STYLE_PROPERTIES.indexOf(key) !== -1) { | ||
if (!result?.transform) { | ||
result.transform = []; | ||
} | ||
result.transform.push({ [key]: animationSharedValue.value[key] }); | ||
} else { | ||
result[key] = animationSharedValue.value[key]; | ||
} | ||
}); | ||
return result; | ||
}); | ||
|
||
useEffect(() => { | ||
const result: any = {}; | ||
Object.keys(toFlatten).forEach((key) => { | ||
result[key] = withDelay(delay, withTiming(toFlatten[key], timeConfig)); | ||
}); | ||
animationSharedValue.value = result; | ||
}, [animation, animationSharedValue, delay, timeConfig, toFlatten]); | ||
|
||
const gesturePan = Gesture.Pan() | ||
.onBegin(() => { | ||
'worklet'; | ||
|
||
offset.value = { | ||
x: toFlatten?.translateX ?? 0, | ||
y: toFlatten?.translateY ?? 0, | ||
}; | ||
}) | ||
.onChange((e) => { | ||
'worklet'; | ||
|
||
console.log(e.changeY, offset.value.y, toFlatten?.translateY); | ||
switch (direction) { | ||
case Directions.DOWN: | ||
if (e.changeY + offset.value.y < toFlatten?.translateY ?? 0) { | ||
return; | ||
} | ||
break; | ||
case Directions.UP: | ||
if (e.changeY + offset.value.y > toFlatten?.translateY ?? 0) { | ||
return; | ||
} | ||
break; | ||
case Directions.LEFT: | ||
if (e.changeX + offset.value.x > toFlatten?.translateX ?? 0) { | ||
return; | ||
} | ||
break; | ||
case Directions.RIGHT: | ||
if (e.changeX + offset.value.x < toFlatten?.translateX ?? 0) { | ||
return; | ||
} | ||
break; | ||
} | ||
|
||
offset.value = { | ||
x: e.changeX + offset.value.x, | ||
y: e.changeY + offset.value.y, | ||
}; | ||
// 更新最新触摸时间 | ||
timestamp.value = new Date().valueOf(); | ||
|
||
switch (direction) { | ||
case Directions.UP: | ||
case Directions.DOWN: | ||
// case 'none': | ||
animationSharedValue.value = { | ||
...animationSharedValue.value, | ||
translateY: offset.value.y, | ||
}; | ||
break; | ||
case Directions.LEFT: | ||
case Directions.RIGHT: | ||
animationSharedValue.value = { | ||
...animationSharedValue.value, | ||
translateX: offset.value.x, | ||
}; | ||
break; | ||
} | ||
}) | ||
.onFinalize((e) => { | ||
'worklet'; | ||
|
||
const mainAxis = ([Directions.UP, Directions.DOWN] as number[]).includes(direction) ? 'Y' : 'X'; | ||
|
||
// 如果滑动方向和定义不一致 返回原位 | ||
if ( | ||
(([Directions.UP, Directions.LEFT] as number[]).includes(direction) && e[`translation${mainAxis}`] > 0) || | ||
(([Directions.DOWN, Directions.RIGHT] as number[]).includes(direction) && e[`translation${mainAxis}`] < 0) | ||
) { | ||
animationSharedValue.value = { | ||
...animationSharedValue.value, | ||
[`translate${mainAxis}`]: toFlatten[`translate${mainAxis}`], | ||
}; | ||
return; | ||
} | ||
|
||
// 计算滑动时间,如果很快 速度很大 判断为投掷 | ||
if (Math.abs(e[`translation${mainAxis}`] / (new Date().valueOf() - timestamp.value)) > 5) { | ||
if (gestureCallback) { | ||
runOnJS(gestureCallback)(); | ||
} | ||
return; | ||
} | ||
|
||
// 不超过三分之一 | ||
const distance = Math.abs( | ||
(toFlatten?.[`translate${mainAxis}`] ?? 0) - (fromFlatten?.[`translate${mainAxis}`] ?? 0), | ||
); | ||
if (distance > 0 && Math.abs(e[`translation${mainAxis}`]) < distance / 3) { | ||
// 返回原位 | ||
animationSharedValue.value = { | ||
...animationSharedValue.value, | ||
[`translate${mainAxis}`]: toFlatten[`translate${mainAxis}`], | ||
}; | ||
} else { | ||
// 关闭 | ||
if (gestureCallback) { | ||
runOnJS(gestureCallback)(); | ||
} | ||
} | ||
}); | ||
|
||
return gesture ? ( | ||
<GestureDetector gesture={gesturePan}> | ||
<Animated.View style={[style, animatedStyles]} {...rest}> | ||
{props.children} | ||
</Animated.View> | ||
</GestureDetector> | ||
) : ( | ||
<Animated.View style={[style, animatedStyles]} {...rest}> | ||
{props.children} | ||
</Animated.View> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/** | ||
* title: AnimationView 动画容器 | ||
* description: 执行特定的动画 | ||
* spline: base | ||
* isComponent: true | ||
* toc: false | ||
*/ | ||
import { View, Button, AnimationView } from 'tdesign-react-native/components'; | ||
import { Section, CodeSpace, H3, H5, P } from '@src/../example/src/components'; | ||
import { useState } from 'react'; | ||
import { Easing } from 'react-native-reanimated'; | ||
import { Directions } from 'react-native-gesture-handler'; | ||
|
||
const NormalDemo = () => { | ||
const [toggle, setToggle] = useState(false); | ||
const fromStyle = { width: 20, height: 20, backgroundColor: 'red', opacity: 1, transform: [{ translateX: 0 }] }; | ||
const toStyle = { width: 50, height: 50, backgroundColor: 'blue', opacity: 0.5, transform: [{ translateX: 100 }] }; | ||
return ( | ||
<View style={{ height: 50 }} className="flexRow flexBetween gapX40 px16"> | ||
<AnimationView animation={!toggle ? { from: fromStyle, to: toStyle } : { from: toStyle, to: fromStyle }} /> | ||
<Button onPress={() => setToggle(!toggle)} size="small" theme="primary" content={'重复动画'} /> | ||
</View> | ||
); | ||
}; | ||
|
||
const DurationDemo = () => { | ||
const [toggle, setToggle] = useState(false); | ||
const fromStyle = { width: 20, height: 20, backgroundColor: 'red', opacity: 1, transform: [{ translateX: 0 }] }; | ||
const toStyle = { width: 50, height: 50, backgroundColor: 'blue', opacity: 0.5, transform: [{ translateX: 100 }] }; | ||
return ( | ||
<View style={{ height: 50 }} className="flexRow flexBetween gapX40 px16"> | ||
<AnimationView | ||
duration={2000} | ||
animation={!toggle ? { from: fromStyle, to: toStyle } : { from: toStyle, to: fromStyle }} | ||
/> | ||
<Button onPress={() => setToggle(!toggle)} size="small" theme="primary" content={'重复动画'} /> | ||
</View> | ||
); | ||
}; | ||
|
||
const DelayDemo = () => { | ||
const [toggle, setToggle] = useState(false); | ||
const fromStyle = { width: 20, height: 20, backgroundColor: 'red', opacity: 1, transform: [{ translateX: 0 }] }; | ||
const toStyle = { width: 50, height: 50, backgroundColor: 'blue', opacity: 0.5, transform: [{ translateX: 100 }] }; | ||
return ( | ||
<View style={{ height: 50 }} className="flexRow flexBetween gapX40 px16"> | ||
<AnimationView | ||
delay={2000} | ||
animation={!toggle ? { from: fromStyle, to: toStyle } : { from: toStyle, to: fromStyle }} | ||
/> | ||
<Button onPress={() => setToggle(!toggle)} size="small" theme="primary" content={'重复动画'} /> | ||
</View> | ||
); | ||
}; | ||
|
||
const EasingDemo = () => { | ||
const [toggle, setToggle] = useState(false); | ||
const fromStyle = { width: 20, height: 20, backgroundColor: 'red', opacity: 1, transform: [{ translateX: 0 }] }; | ||
const toStyle = { width: 50, height: 50, backgroundColor: 'blue', opacity: 0.5, transform: [{ translateX: 100 }] }; | ||
return ( | ||
<View style={{ height: 50 }} className="flexRow flexBetween gapX40 px16"> | ||
<AnimationView | ||
easing={Easing.bounce} | ||
animation={!toggle ? { from: fromStyle, to: toStyle } : { from: toStyle, to: fromStyle }} | ||
/> | ||
<Button onPress={() => setToggle(!toggle)} size="small" theme="primary" content={'重复动画'} /> | ||
</View> | ||
); | ||
}; | ||
|
||
const GestureDemo = () => { | ||
const [toggle, setToggle] = useState(false); | ||
const fromStyle = { width: 20, height: 20, backgroundColor: 'red', opacity: 1, transform: [{ translateX: 0 }] }; | ||
const toStyle = { width: 50, height: 50, backgroundColor: 'blue', opacity: 0.5, transform: [{ translateX: 100 }] }; | ||
return ( | ||
<View> | ||
<View style={{ height: 50 }} className="flexRow flexBetween gapX40 px16"> | ||
<AnimationView | ||
gestureDirection={!toggle ? Directions.LEFT : Directions.RIGHT} | ||
gesture | ||
gestureCallback={() => setToggle(!toggle)} | ||
animation={!toggle ? { from: fromStyle, to: toStyle } : { from: toStyle, to: fromStyle }} | ||
/> | ||
<Button onPress={() => setToggle(!toggle)} size="small" theme="primary" content={'重复动画'} /> | ||
</View> | ||
<P> | ||
动画左右滑动试试,当滑动距离小于三分之一时会重置动画,大于三分之一时会触发gestureCallback定义的事件,用于某些动画组件的交互场景 | ||
</P> | ||
</View> | ||
); | ||
}; | ||
|
||
const Demo = () => { | ||
return ( | ||
<> | ||
<Section> | ||
<H3>1.基础</H3> | ||
<H5>普通动画</H5> | ||
<CodeSpace> | ||
<NormalDemo /> | ||
</CodeSpace> | ||
<H5>动画时间</H5> | ||
<CodeSpace> | ||
<DurationDemo /> | ||
</CodeSpace> | ||
<H5>延迟动画</H5> | ||
<CodeSpace> | ||
<DelayDemo /> | ||
</CodeSpace> | ||
<H5>动画函数</H5> | ||
<CodeSpace> | ||
<EasingDemo /> | ||
</CodeSpace> | ||
</Section> | ||
<Section> | ||
<H3>2.手势</H3> | ||
<H5>动画配合手势</H5> | ||
<CodeSpace> | ||
<GestureDemo /> | ||
</CodeSpace> | ||
</Section> | ||
</> | ||
); | ||
}; | ||
export default Demo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './AnimationView'; | ||
export * from './types'; |
Oops, something went wrong.