Skip to content

Commit b212886

Browse files
committed
feat(ImageHeader):add ImageHeader
1 parent 618ab6c commit b212886

File tree

11 files changed

+530
-2
lines changed

11 files changed

+530
-2
lines changed

example/examples/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"react-native-image-viewing": "~0.2.2",
2424
"react-native-safe-area-context": "~4.3.1",
2525
"react-native-screens": "~3.15.0",
26-
"react-native-svg": "13.0.0"
26+
"react-native-svg": "13.0.0",
27+
"react-native-reanimated":"3.1.0"
2728
},
2829
"devDependencies": {
2930
"@babel/core": "~7.20.7",

example/examples/src/routes.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,4 +522,12 @@ export const stackPageData: Routes[] = [
522522
description: '登录页组件',
523523
},
524524
},
525+
{
526+
name: 'ImageHeader',
527+
component: require('./routes/ImageHeader').default,
528+
params: {
529+
title: 'ImageHeader 图片头部组件',
530+
description: '图片头部组件',
531+
},
532+
},
525533
];
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import {ImageHeader, Icon, Flex, Text} from '@uiw/react-native';
3+
import {ComProps} from '../../routes';
4+
import Layout, {Container} from '../../Layout';
5+
6+
export interface ImageHeaderProps extends ComProps {}
7+
8+
export default class ImageHeaderView extends React.Component<ImageHeaderProps> {
9+
render() {
10+
return (
11+
<ImageHeader
12+
headerBackgroundImg={{uri: 'https://c-ssl.duitang.com/uploads/blog/201411/18/20141118232436_zkQVV.jpeg'}}
13+
headerHeight={161}
14+
headerLeftColor="#FFF"
15+
headerLeft="返回"
16+
headerRight={<Icon name="delete" size={20} color={'##FFF'} />}
17+
statusBarColor="blue"
18+
statusBarStyle="dark-content">
19+
<Flex justify="center" style={{backgroundColor: 'white', height: 100, marginHorizontal: 20}}>
20+
<Text>111</Text>
21+
</Flex>
22+
</ImageHeader>
23+
);
24+
}
25+
}

packages/core/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@
6565
"prop-types": "15.7.2",
6666
"react-native-gesture-handler": "2.8.0",
6767
"react-native-root-siblings": "4.1.1",
68-
"react-native-svg": "13.0.0"
68+
"react-native-svg": "13.0.0",
69+
"react-native-safe-area-context":"4.5.1",
70+
"react-native-reanimated":"3.1.0"
6971
},
7072
"peerDependencies": {
7173
"react": ">=16.9.0",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React, { ReactNode } from 'react';
2+
import { TextStyle, TouchableOpacity, Text } from 'react-native';
3+
import Animated, { Extrapolate, interpolate, interpolateColor, useAnimatedStyle } from 'react-native-reanimated';
4+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
5+
import { Theme } from '../theme';
6+
import { useTheme } from '@shopify/restyle';
7+
8+
import Box from '../Typography/Box';
9+
import Flex from '../Flex';
10+
import helpers from './helpers';
11+
import Icon from '../Icon';
12+
13+
const { px, ONE_PIXEL, deviceWidth } = helpers;
14+
const HEADER_HEIGHT = px(44);
15+
export interface AnimateHeaderProps {
16+
/** 头部文字 */
17+
headerTitle: string;
18+
/** 头部文字样式 */
19+
headerTitleStyle?: TextStyle;
20+
/** 滚动距离 */
21+
scrollY: Animated.SharedValue<number>;
22+
/** 纵向滚动到哪个值时显示ImageHeader */
23+
scrollHeight?: number;
24+
/** 头部右侧内容 */
25+
headerRight?: ReactNode;
26+
/** 左侧返回键和字体颜色 */
27+
headerLeftColor?: string;
28+
/** 头部左侧内容 */
29+
headerLeft?: ReactNode;
30+
/** 头部底色,默认为透明 */
31+
headerBackgroundColor?: string;
32+
/** 左侧点击事件 */
33+
onPress?: () => void;
34+
/** 是否显示左侧图标 */
35+
showLeft?: boolean;
36+
}
37+
38+
const AnimateHeader: React.FC<AnimateHeaderProps> = (props) => {
39+
const theme = useTheme<Theme>();
40+
const insets = useSafeAreaInsets();
41+
42+
const {
43+
scrollY,
44+
headerTitle,
45+
headerTitleStyle,
46+
scrollHeight = 300,
47+
onPress,
48+
showLeft = true,
49+
headerRight,
50+
headerLeftColor = theme.colors.gray500,
51+
headerLeft,
52+
headerBackgroundColor = theme.colors.background,
53+
} = props;
54+
55+
const inputRange = [0, scrollHeight];
56+
const style = useAnimatedStyle(() => {
57+
const opacity = interpolate(scrollY.value, inputRange, [0, 1], Extrapolate.CLAMP);
58+
const borderBottomWidth = interpolate(scrollY.value, inputRange, [0, ONE_PIXEL], Extrapolate.CLAMP);
59+
const backgroundColor = interpolateColor(scrollY.value, inputRange, ['transparent', headerBackgroundColor]);
60+
61+
return {
62+
borderBottomWidth,
63+
backgroundColor,
64+
opacity,
65+
};
66+
});
67+
68+
return (
69+
<Animated.View
70+
style={[
71+
{
72+
width: deviceWidth,
73+
position: 'absolute',
74+
top: 0,
75+
zIndex: 99,
76+
justifyContent: 'center',
77+
alignItems: 'center',
78+
borderBottomColor: theme.colors.border,
79+
paddingTop: insets.top,
80+
height: HEADER_HEIGHT + insets.top,
81+
},
82+
style,
83+
]}
84+
>
85+
<Flex style={{ flex: 1 }}>
86+
{showLeft ? (
87+
<TouchableOpacity activeOpacity={0.5} onPress={onPress} style={{ flex: 1 }}>
88+
<Flex>
89+
<Icon name="left" size={px(24)} color={headerLeftColor} />
90+
{typeof headerLeft === 'string' ? (
91+
<Text style={{ color: headerLeftColor }}>{headerLeft}</Text>
92+
) : (
93+
headerLeft
94+
)}
95+
</Flex>
96+
</TouchableOpacity>
97+
) : (
98+
<Box flex={1} />
99+
)}
100+
<Animated.View style={{ flex: 5, alignItems: 'center' }}>
101+
<Text numberOfLines={1} style={[{ color: '#333333' }, headerTitleStyle]}>
102+
{headerTitle}
103+
</Text>
104+
</Animated.View>
105+
<Box flex={1} alignItems="flex-end">
106+
{headerRight}
107+
</Box>
108+
</Flex>
109+
</Animated.View>
110+
);
111+
};
112+
AnimateHeader.displayName = 'AnimateHeader';
113+
114+
export default AnimateHeader;
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
title: ImageHeader - 图片头部组件
3+
nav:
4+
title: RN组件
5+
path: /react-native
6+
group:
7+
title: Display
8+
path: /display
9+
---
10+
11+
# ImageHeader 图片头部组件
12+
13+
## 效果演示
14+
15+
### 1. 普通 ImageHeader
16+
17+
```tsx | pure
18+
<ImageHeader headerBackgroundImg={require('../../assets/images/bg_rank.png')} headerHeight={px(161)} {...props}>
19+
<Flex justifyContent="center" backgroundColor="white" height={100}>
20+
<Text>111</Text>
21+
</Flex>
22+
</ImageHeader>
23+
```
24+
25+
<center>
26+
<figure>
27+
<img
28+
alt="header-ios1.png"
29+
src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1609999430064140139.png"
30+
style="width: 375px; margin-right: 10px; border: 1px solid #ddd;"
31+
/>
32+
</figure>
33+
</center>
34+
35+
### 2. ImageHeader 配置 left、right 和 headerLeftColor
36+
37+
```tsx | pure
38+
<ImageHeader
39+
headerBackgroundImg={require('../../assets/images/bg_rank.png')}
40+
headerHeight={px(161)}
41+
headerLeftColor={theme.colors.white}
42+
headerLeft="返回"
43+
headerRight={<Icon name="delete" size={px(20)} color={theme.colors.white} />}
44+
{...props}
45+
>
46+
<Flex justifyContent="center" backgroundColor="white" height={100}>
47+
<Text>111</Text>
48+
</Flex>
49+
</ImageHeader>
50+
```
51+
52+
<center>
53+
<figure>
54+
<img
55+
alt="header-ios2.png"
56+
src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1609999550703021067.png"
57+
style="width: 375px; margin-right: 10px; border: 1px solid #ddd;"
58+
/>
59+
</figure>
60+
</center>
61+
62+
### 3. ImageHeader 配置 headerBackgroundColor
63+
64+
```tsx | pure
65+
<ImageHeader
66+
headerBackgroundImg={require('../../assets/images/bg_rank.png')}
67+
headerHeight={px(161)}
68+
headerBackgroundColor={theme.colors.white}
69+
headerLeft="返回"
70+
headerRight={<Icon name="delete" size={px(20)} color={theme.colors.white} />}
71+
{...props}
72+
>
73+
<Flex justifyContent="center" height={100}>
74+
<Text>111</Text>
75+
</Flex>
76+
</ImageHeader>
77+
```
78+
79+
<center>
80+
<figure>
81+
<img
82+
alt="header-ios3.png"
83+
src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1610000705310241428.png"
84+
style="width: 375px; margin-right: 10px; border: 1px solid #ddd;"
85+
/>
86+
</figure>
87+
</center>
88+
89+
### 4. AnimatedHeader
90+
91+
```tsx | pure
92+
import { useScrollHandler } from 'react-native-redash';
93+
import Animated from 'react-native-reanimated';
94+
95+
export default () => {
96+
const { scrollHandler, y } = useScrollHandler();
97+
98+
return (
99+
<AnimateHeader
100+
scrollY={y}
101+
scrollHeight={200}
102+
headerTitle="测试啊啊啊啊啊"
103+
headerLeft="返回"
104+
headerBackgroundColor={theme.colors.white}
105+
{...props}
106+
headerRight={
107+
<TouchableOpacity activeOpacity={0.5} onPress={() => props.navigation.goBack()}>
108+
<Icon name="delete" size={px(20)} color={theme.colors.primaryColor} />
109+
</TouchableOpacity>
110+
}
111+
/>
112+
<Animated.ScrollView {...scrollHandler}>
113+
<ImageHeader
114+
headerBackgroundImg={require('../../assets/images/bg_rank.png')}
115+
headerHeight={px(161)}
116+
headerLeftColor={theme.colors.white}
117+
headerRight={
118+
<TouchableOpacity activeOpacity={0.5} onPress={() => props.navigation.goBack()}>
119+
<Icon name="delete" size={px(20)} color={theme.colors.primaryColor} />
120+
</TouchableOpacity>
121+
}
122+
{...props}
123+
>
124+
<Flex justifyContent="center" height={100}>
125+
<Text>111</Text>
126+
</Flex>
127+
</ImageHeader>
128+
<Box width={200} height={900} />
129+
</Animated.ScrollView>
130+
)
131+
}
132+
```
133+
134+
<center>
135+
<figure>
136+
<img
137+
alt="header-ios4.gif"
138+
src="https://td-dev-public.oss-cn-hangzhou.aliyuncs.com/maoyes-app/1608877076955547998.gif"
139+
style="width: 375px; margin-right: 10px; border: 1px solid #ddd;"
140+
/>
141+
</figure>
142+
</center>
143+
144+
## ImageHeader 组件 API
145+
146+
| 属性 | 必填 | 说明 | 类型 | 默认值 |
147+
| --------------------- | ------- | -------------------- | --------------------- | --------------------------- |
148+
| headerTitle | `false` | 头部文字 | `ReactNode` | |
149+
| headerRight | `false` | 头部右侧内容 | `ReactNode` | |
150+
| headerLeft | `false` | 头部左侧内容 | `ReactNode` | |
151+
| headerLeftColor | `false` | 左侧返回键和字体颜色 | `string` | `theme.colors.primaryColor` |
152+
| headerBackgroundColor | `false` | 头部背景颜色 | `string` | `transparent` |
153+
| headerBackgroundImg | `true` | 头部背景图片 | `ImageSourcePropType` | |
154+
| headerHeight | `false` | 头部高度 | `number` | |
155+
| onPress | `false` | 左边图标点击事件 | `() => void` | |
156+
| showLeft | `false` | 是否显示左边图标 | `boolean` | `true` |
157+
158+
## AnimateHeader 组件 API
159+
160+
| 属性 | 必填 | 说明 | 类型 | 默认值 |
161+
| --- | --- | --- | --- | --- |
162+
| headerTitle | `true` | 头部文字 | `string` | |
163+
| headerTitleStyle | `false` | 头部文字样式 | `TextStyle` | |
164+
| scrollY | `false` | 滚动距离 | `Animated.SharedValue<number>` | `0` |
165+
| scrollHeight | `false` | 纵向滚动到哪个值时显示 `ImageHeader` | `number` | `300` |
166+
| headerHeight | `true` | 头部高度 | `number` | |
167+
| headerRight | `false` | 头部右侧内容 | `ReactNode` | |
168+
| headerLeft | `false` | 头部左侧内容 | `ReactNode` | |
169+
| headerLeftColor | `false` | 左侧返回键和字体颜色 | `string` | `theme.colors.primaryColor` |
170+
| headerBackgroundColor | `false` | 头部背景颜色 | `string` | `transparent` |
171+
| onPress | `false` | 左边按钮点击事件 | `() => void` | |
172+
| showLeft | `false` | 是否显示左边图标 | `boolean` | `true` |
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Platform, StyleProp } from 'react-native';
2+
3+
import { deviceHeight, deviceWidth, ONE_PIXEL, px } from './normalize';
4+
// import rddNode from './rddNode';
5+
6+
/**
7+
* 判断是否是IOS系统
8+
*/
9+
const isIOS = Platform.OS === 'ios';
10+
11+
/**
12+
* 根据条件决定样式是否生效
13+
* @param condition
14+
* @param style
15+
*/
16+
const conditionalStyle = (condition: boolean, style: StyleProp<any>) => (condition ? style : {});
17+
18+
function hexToRgba(hex: string, alpha = 1) {
19+
let c: any;
20+
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
21+
let arr = hex.substring(1).split('');
22+
if (arr.length == 3) {
23+
arr = [arr[0], arr[0], arr[1], arr[1], arr[2], arr[2]];
24+
}
25+
c = '0x' + arr.join('');
26+
return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + `,${alpha})`;
27+
}
28+
throw new Error('Bad Hex');
29+
}
30+
31+
export default {
32+
// rddNode,
33+
px,
34+
deviceWidth,
35+
deviceHeight,
36+
ONE_PIXEL,
37+
isIOS,
38+
conditionalStyle,
39+
hexToRgba,
40+
};

0 commit comments

Comments
 (0)