@unif/react-native-camera 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/lib/commonjs/camera/Camera.js +39 -8
  2. package/lib/commonjs/camera/Camera.js.map +1 -1
  3. package/lib/commonjs/camera/footer/Footer.js +3 -2
  4. package/lib/commonjs/camera/footer/Footer.js.map +1 -1
  5. package/lib/commonjs/camera/preview/PreView.js +6 -2
  6. package/lib/commonjs/camera/preview/PreView.js.map +1 -1
  7. package/lib/commonjs/camera/preview/PreViewContainer.js +3 -2
  8. package/lib/commonjs/camera/preview/PreViewContainer.js.map +1 -1
  9. package/lib/commonjs/camera/preview/PreviewFooter.js +1 -1
  10. package/lib/commonjs/camera/preview/SinglePre.js +7 -7
  11. package/lib/commonjs/camera/preview/SinglePre.js.map +1 -1
  12. package/lib/commonjs/camera/watermark/Render.js +110 -0
  13. package/lib/commonjs/camera/watermark/Render.js.map +1 -0
  14. package/lib/commonjs/camera/watermark/ViewShotWatermark.js +88 -0
  15. package/lib/commonjs/camera/watermark/ViewShotWatermark.js.map +1 -0
  16. package/lib/commonjs/camera/watermark/index.js +21 -0
  17. package/lib/commonjs/camera/watermark/index.js.map +1 -0
  18. package/lib/commonjs/utils/common.js +3 -2
  19. package/lib/commonjs/utils/common.js.map +1 -1
  20. package/lib/commonjs/utils/index.js +22 -0
  21. package/lib/commonjs/utils/index.js.map +1 -1
  22. package/lib/commonjs/utils/interface.js +1 -1
  23. package/lib/commonjs/utils/render-item.js +3 -2
  24. package/lib/commonjs/utils/render-item.js.map +1 -1
  25. package/lib/commonjs/utils/util.js +23 -0
  26. package/lib/commonjs/utils/util.js.map +1 -0
  27. package/lib/commonjs/utils/watermark.js +43 -0
  28. package/lib/commonjs/utils/watermark.js.map +1 -0
  29. package/lib/module/camera/Camera.js +40 -8
  30. package/lib/module/camera/Camera.js.map +1 -1
  31. package/lib/module/camera/footer/Footer.js +4 -3
  32. package/lib/module/camera/footer/Footer.js.map +1 -1
  33. package/lib/module/camera/index.js +1 -1
  34. package/lib/module/camera/preview/PreView.js +6 -2
  35. package/lib/module/camera/preview/PreView.js.map +1 -1
  36. package/lib/module/camera/preview/PreViewContainer.js +3 -2
  37. package/lib/module/camera/preview/PreViewContainer.js.map +1 -1
  38. package/lib/module/camera/preview/PreviewFooter.js +1 -1
  39. package/lib/module/camera/preview/SinglePre.js +7 -7
  40. package/lib/module/camera/preview/SinglePre.js.map +1 -1
  41. package/lib/module/camera/watermark/Render.js +102 -0
  42. package/lib/module/camera/watermark/Render.js.map +1 -0
  43. package/lib/module/camera/watermark/ViewShotWatermark.js +78 -0
  44. package/lib/module/camera/watermark/ViewShotWatermark.js.map +1 -0
  45. package/lib/module/camera/watermark/index.js +13 -0
  46. package/lib/module/camera/watermark/index.js.map +1 -0
  47. package/lib/module/utils/common.js +2 -1
  48. package/lib/module/utils/common.js.map +1 -1
  49. package/lib/module/utils/index.js +3 -1
  50. package/lib/module/utils/index.js.map +1 -1
  51. package/lib/module/utils/interface.js +1 -1
  52. package/lib/module/utils/render-item.js +3 -2
  53. package/lib/module/utils/render-item.js.map +1 -1
  54. package/lib/module/utils/util.js +16 -0
  55. package/lib/module/utils/util.js.map +1 -0
  56. package/lib/module/utils/watermark.js +33 -0
  57. package/lib/module/utils/watermark.js.map +1 -0
  58. package/lib/typescript/src/camera/Camera.d.ts.map +1 -1
  59. package/lib/typescript/src/camera/footer/Footer.d.ts.map +1 -1
  60. package/lib/typescript/src/camera/preview/PreView.d.ts.map +1 -1
  61. package/lib/typescript/src/camera/preview/SinglePre.d.ts +2 -0
  62. package/lib/typescript/src/camera/preview/SinglePre.d.ts.map +1 -1
  63. package/lib/typescript/src/camera/watermark/Render.d.ts +26 -0
  64. package/lib/typescript/src/camera/watermark/Render.d.ts.map +1 -0
  65. package/lib/typescript/src/camera/watermark/ViewShotWatermark.d.ts +33 -0
  66. package/lib/typescript/src/camera/watermark/ViewShotWatermark.d.ts.map +1 -0
  67. package/lib/typescript/src/camera/watermark/index.d.ts +4 -0
  68. package/lib/typescript/src/camera/watermark/index.d.ts.map +1 -0
  69. package/lib/typescript/src/utils/common.d.ts +1 -0
  70. package/lib/typescript/src/utils/common.d.ts.map +1 -1
  71. package/lib/typescript/src/utils/index.d.ts +2 -0
  72. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  73. package/lib/typescript/src/utils/interface.d.ts +10 -0
  74. package/lib/typescript/src/utils/interface.d.ts.map +1 -1
  75. package/lib/typescript/src/utils/render-item.d.ts.map +1 -1
  76. package/lib/typescript/src/utils/util.d.ts +2 -0
  77. package/lib/typescript/src/utils/util.d.ts.map +1 -0
  78. package/lib/typescript/src/utils/watermark.d.ts +11 -0
  79. package/lib/typescript/src/utils/watermark.d.ts.map +1 -0
  80. package/package.json +12 -5
  81. package/src/camera/Camera.tsx +144 -105
  82. package/src/camera/footer/Footer.tsx +3 -1
  83. package/src/camera/index.tsx +1 -1
  84. package/src/camera/preview/PreView.tsx +4 -2
  85. package/src/camera/preview/PreViewContainer.tsx +2 -1
  86. package/src/camera/preview/PreviewFooter.tsx +1 -1
  87. package/src/camera/preview/SinglePre.tsx +5 -10
  88. package/src/camera/watermark/Render.tsx +128 -0
  89. package/src/camera/watermark/ViewShotWatermark.tsx +81 -0
  90. package/src/camera/watermark/index.tsx +12 -0
  91. package/src/utils/common.ts +3 -1
  92. package/src/utils/index.ts +3 -1
  93. package/src/utils/interface.ts +12 -1
  94. package/src/utils/render-item.tsx +3 -2
  95. package/src/utils/util.ts +15 -0
  96. package/src/utils/watermark.ts +45 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unif/react-native-camera",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "camera",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -60,6 +60,7 @@
60
60
  "@types/jest": "^28.1.2",
61
61
  "@types/react": "~17.0.21",
62
62
  "@types/react-native": "0.70.0",
63
+ "@types/react-native-canvas": "^0.1.14",
63
64
  "commitlint": "^17.0.2",
64
65
  "del-cli": "^5.0.0",
65
66
  "eslint": "^8.4.1",
@@ -71,11 +72,14 @@
71
72
  "react": "18.2.0",
72
73
  "react-native": "0.74.6",
73
74
  "react-native-builder-bob": "^0.20.0",
75
+ "react-native-canvas": "0.1.40",
74
76
  "react-native-gesture-handler": "^2.21.2",
75
77
  "react-native-reanimated": "^3.16.5",
76
78
  "react-native-reanimated-carousel": "^4.0.0-canary.22",
77
79
  "react-native-safe-area-context": "5.0.0",
80
+ "react-native-view-shot": "^4.0.3",
78
81
  "react-native-vision-camera": "^4.6.3",
82
+ "react-native-webview": "^13.0.0",
79
83
  "release-it": "^15.0.0",
80
84
  "turbo": "^1.10.7",
81
85
  "typescript": "^5.0.2"
@@ -84,13 +88,16 @@
84
88
  "@types/react": "17.0.21"
85
89
  },
86
90
  "peerDependencies": {
87
- "react": "*",
88
- "react-native": "*",
91
+ "react": ">=18.2.0",
92
+ "react-native": ">=0.74.0",
93
+ "react-native-canvas": ">0.1.40",
89
94
  "react-native-gesture-handler": ">=2.21.0",
90
95
  "react-native-reanimated": ">=3.9.0",
91
96
  "react-native-reanimated-carousel": ">=4.0.0",
92
- "react-native-safe-area-context": ">=5.0.0",
93
- "react-native-vision-camera": ">=4.0.0"
97
+ "react-native-safe-area-context": "*",
98
+ "react-native-view-shot": "^4.0.3",
99
+ "react-native-vision-camera": ">=4.0.0",
100
+ "react-native-webview": ">=13.0.0"
94
101
  },
95
102
  "workspaces": [
96
103
  "example"
@@ -1,11 +1,12 @@
1
1
  /*
2
2
  * @Author: 刘利军
3
- * @Date: 2024-01-09 16:27:07
3
+ * @Date: 2024-12-02 13:48:40
4
4
  * @LastEditors: 刘利军
5
- * @LastEditTime: 2024-12-23 11:26:31
5
+ * @LastEditTime: 2024-12-27 10:44:31
6
6
  * @Description:
7
7
  * @PageName:
8
8
  */
9
+
9
10
  import { StyleSheet } from 'react-native';
10
11
  import React, { useEffect, useRef, useState } from 'react';
11
12
  import {
@@ -13,6 +14,7 @@ import {
13
14
  useCameraDevice,
14
15
  useCameraFormat,
15
16
  } from 'react-native-vision-camera';
17
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
16
18
  import NoCamera from './NoCamera';
17
19
 
18
20
  import {
@@ -28,6 +30,7 @@ import { SetUp } from './setup';
28
30
  import { PreView, PreViewContainer, SinglePre } from './preview';
29
31
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
30
32
  import { useConfirm } from '../hooks';
33
+ import { ViewShotWatermark, WaterMarkRender } from './watermark';
31
34
 
32
35
  export const PhotoAspectRatio: { [key: number]: number } = {
33
36
  4: 4 / 3,
@@ -48,6 +51,7 @@ const Camera: React.FC<{
48
51
  const [isActive, setIsActive] = useState(false);
49
52
  const [preVis, setPreVis] = useState(false);
50
53
 
54
+ const [watermarkUriVis, setWatermarkUriVis] = useState(false);
51
55
  const device = useCameraDevice(position, {
52
56
  physicalDevices: [
53
57
  'ultra-wide-angle-camera',
@@ -101,124 +105,159 @@ const Camera: React.FC<{
101
105
  onChange({
102
106
  code: 200,
103
107
  data: photos.map((item) => ({
104
- ...item,
108
+ cameraMode: item.mode,
105
109
  id: new Date().getTime().toString(),
106
110
  cameraType: position,
111
+ path: item.path,
107
112
  })),
108
113
  message: '保存成功',
109
114
  });
110
115
  };
116
+ const openViewShotWatermark = () => {
117
+ setIsActive((val) => !val);
118
+ setWatermarkUriVis((val) => !val);
119
+ };
111
120
  return (
112
121
  <GestureHandlerRootView>
113
- <VisionCamera
114
- zoom={zoom}
115
- format={format}
116
- ref={camera}
117
- style={styles.camera}
118
- device={device}
119
- isActive={isActive}
120
- enableZoomGesture={true}
121
- photoQualityBalance="balanced"
122
- photo={cameraMode.some(
123
- (item) => item.mode === 'single' || item.mode === 'continuous'
124
- )}
125
- video={showVideo()}
126
- audio={showVideo()}
127
- resizeMode={resizeMode}
128
- />
129
-
130
- <SetUp
131
- isMultiCam={device?.isMultiCam}
132
- onChange={(val) => (flash = val)}
133
- onAspectRationChange={setAspectRatio}
134
- onScreenChange={setResizeMode}
135
- onVolumeChange={(val) => (enableShutterSound = val)}
136
- onFishEyeOpen={(val) => {
137
- if (val === 'fishEye') {
138
- setZoom(device.minZoom);
139
- }
140
- if (val === 'default') {
141
- setZoom(device.neutralZoom);
142
- }
143
- }}
144
- />
145
- <Footer
146
- cameraMode={cameraMode}
147
- photos={photos}
148
- backPress={async () => {
149
- if (photos.length > 0) {
150
- const isBack = await confirmApi.open({
151
- text: '返回会清空已拍摄数据,请确认!',
152
- });
153
- if (isBack) {
154
- back();
122
+ <SafeAreaProvider>
123
+ <VisionCamera
124
+ zoom={zoom}
125
+ format={format}
126
+ ref={camera}
127
+ style={styles.camera}
128
+ device={device}
129
+ isActive={isActive}
130
+ enableZoomGesture={true}
131
+ photoQualityBalance="balanced"
132
+ photo={cameraMode.some(
133
+ (item) => item.mode === 'single' || item.mode === 'continuous'
134
+ )}
135
+ video={showVideo()}
136
+ audio={showVideo()}
137
+ resizeMode={resizeMode}
138
+ />
139
+
140
+ <SetUp
141
+ isMultiCam={device?.isMultiCam}
142
+ onChange={(val) => (flash = val)}
143
+ onAspectRationChange={setAspectRatio}
144
+ onScreenChange={setResizeMode}
145
+ onVolumeChange={(val) => (enableShutterSound = val)}
146
+ onFishEyeOpen={(val) => {
147
+ if (val === 'fishEye') {
148
+ setZoom(device.minZoom);
155
149
  }
156
- } else {
157
- back();
158
- }
159
- }}
160
- thumbnailPress={showPreview}
161
- onChange={(val) => {
162
- if (dataRetainedMode === 'clear') {
163
- setPhotos([]);
164
- }
165
- setMode(val);
166
- }}
167
- takePress={async () => {
168
- const photo = await camera.current?.takePhoto({
169
- flash,
170
- enableShutterSound: enableShutterSound,
171
- });
172
- if (photo) {
173
- setPhotos((p) => [...p, { ...photo, mode }]);
174
- }
175
- if (isSingle()) {
176
- showPreview();
177
- }
178
- }}
179
- savePress={() => {
180
- confirm();
181
- }}
182
- rotatePress={() => {
183
- setPosition((val) => {
184
- if (val === 'back') {
185
- return 'front';
150
+ if (val === 'default') {
151
+ setZoom(device.neutralZoom);
186
152
  }
187
- return 'back';
188
- });
189
- }}
190
- />
191
-
192
- <PreViewContainer visible={preVis}>
193
- {isSingle() ? (
194
- <SinglePre
195
- uri={photos[0]?.path as string}
196
- onBack={() => signleBack()}
197
- onSave={() => confirm()}
198
- />
199
- ) : (
200
- <PreView
201
- cameraMode={cameraMode}
202
- data={photos}
203
- onBack={() => commonBack()}
204
- onDel={async (snapItem) => {
205
- const isConfirm = await confirmApi.open({
206
- text: '图片删除后无法恢复,请确认!',
153
+ }}
154
+ />
155
+ <Footer
156
+ cameraMode={cameraMode}
157
+ photos={photos}
158
+ backPress={async () => {
159
+ if (photos.length > 0) {
160
+ const isBack = await confirmApi.open({
161
+ text: '返回会清空已拍摄数据,请确认!',
207
162
  });
208
- if (isConfirm) {
209
- setPhotos((photoList) => {
210
- return photoList.filter(
211
- (photo) => photo.path !== snapItem?.path
212
- );
213
- });
214
- return isConfirm;
163
+ if (isBack) {
164
+ back();
165
+ }
166
+ } else {
167
+ back();
168
+ }
169
+ }}
170
+ thumbnailPress={showPreview}
171
+ onChange={(val) => {
172
+ if (dataRetainedMode === 'clear') {
173
+ setPhotos([]);
174
+ }
175
+ setMode(val);
176
+ }}
177
+ takePress={async () => {
178
+ const photo = await camera.current?.takePhoto({
179
+ flash,
180
+ enableShutterSound: enableShutterSound,
181
+ });
182
+ if (photo) {
183
+ setPhotos((p) => [
184
+ ...p,
185
+ { ...photo, frontTime: new Date().getTime(), mode },
186
+ ]);
187
+ }
188
+ // 拍照后进行水印
189
+ if (data.watermark) {
190
+ openViewShotWatermark();
191
+ } else {
192
+ if (isSingle()) {
193
+ showPreview();
215
194
  }
216
- return isConfirm;
195
+ }
196
+ }}
197
+ savePress={() => {
198
+ confirm();
199
+ }}
200
+ rotatePress={() => {
201
+ setPosition((val) => {
202
+ if (val === 'back') {
203
+ return 'front';
204
+ }
205
+ return 'back';
206
+ });
207
+ }}
208
+ />
209
+
210
+ {watermarkUriVis && (
211
+ <ViewShotWatermark
212
+ data={photos.at(-1)}
213
+ watermark={data.watermark}
214
+ onChange={(value) => {
215
+ console.log(value);
216
+ setPhotos((photosList) => {
217
+ return photosList.map((item) => {
218
+ if (item.frontTime === value.frontTime) {
219
+ return value;
220
+ }
221
+ return item;
222
+ });
223
+ });
224
+ openViewShotWatermark();
217
225
  }}
218
226
  />
219
227
  )}
220
- </PreViewContainer>
221
- {contextHolder}
228
+
229
+ <PreViewContainer visible={preVis}>
230
+ {isSingle() ? (
231
+ <SinglePre
232
+ uri={photos[0]?.path as string}
233
+ onBack={() => signleBack()}
234
+ onSave={() => confirm()}
235
+ />
236
+ ) : (
237
+ <PreView
238
+ cameraMode={cameraMode}
239
+ data={photos}
240
+ onBack={() => commonBack()}
241
+ onDel={async (snapItem) => {
242
+ const isConfirm = await confirmApi.open({
243
+ text: '图片删除后无法恢复,请确认!',
244
+ });
245
+ if (isConfirm) {
246
+ setPhotos((photoList) => {
247
+ return photoList.filter(
248
+ (photo) => photo.path !== snapItem?.path
249
+ );
250
+ });
251
+ return isConfirm;
252
+ }
253
+ return isConfirm;
254
+ }}
255
+ />
256
+ )}
257
+ </PreViewContainer>
258
+ {data.watermark && <WaterMarkRender data={data.watermark} />}
259
+ {contextHolder}
260
+ </SafeAreaProvider>
222
261
  </GestureHandlerRootView>
223
262
  );
224
263
  };
@@ -9,6 +9,7 @@ import {
9
9
  import React, { useState } from 'react';
10
10
  import {
11
11
  CameraModeEnum,
12
+ formatUri,
12
13
  type CameraMode,
13
14
  type CameraModeType,
14
15
  } from '../../utils';
@@ -90,7 +91,7 @@ const Footer: React.FC<{
90
91
  <View style={styles.footerHorizontal}>
91
92
  {isPhotos() ? (
92
93
  <PreviewThumbnail
93
- uri={`file://${photos[photos.length - 1]?.path}`}
94
+ uri={formatUri(photos[photos.length - 1]?.path || '')}
94
95
  onPress={thumbnailPress}
95
96
  />
96
97
  ) : (
@@ -145,6 +146,7 @@ const styles = StyleSheet.create({
145
146
  borderTopLeftRadius: 10,
146
147
  borderTopRightRadius: 10,
147
148
  paddingHorizontal: 16,
149
+ zIndex: 9,
148
150
  },
149
151
  footerBottom: {
150
152
  flexDirection: 'row',
@@ -2,7 +2,7 @@
2
2
  * @Author: 刘利军
3
3
  * @Date: 2024-01-12 13:44:47
4
4
  * @LastEditors: 刘利军
5
- * @LastEditTime: 2024-12-16 13:08:44
5
+ * @LastEditTime: 2024-12-25 11:04:23
6
6
  * @Description:
7
7
  * @PageName:
8
8
  */
@@ -2,7 +2,7 @@
2
2
  * @Author: 刘利军
3
3
  * @Date: 2024-12-02 13:37:36
4
4
  * @LastEditors: 刘利军
5
- * @LastEditTime: 2024-12-20 13:03:12
5
+ * @LastEditTime: 2024-12-25 17:10:52
6
6
  * @Description:
7
7
  * @PageName:
8
8
  */
@@ -15,12 +15,14 @@ import { pxToDpWidth, type CustomPhotoFile } from '../../utils';
15
15
  import PreviewFooter from './PreviewFooter';
16
16
 
17
17
  import { styles as singleStyles, type CommonProps } from './SinglePre';
18
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
18
19
  const PreView: React.FC<
19
20
  {
20
21
  cameraMode: CameraModeType[];
21
22
  data: CustomPhotoFile[];
22
23
  } & CommonProps
23
24
  > = ({ cameraMode, data, onBack, onDel }) => {
25
+ const inset = useSafeAreaInsets();
24
26
  const [mode, setMode] = useState(data[0]?.mode);
25
27
  const showData = data.filter((item) => item.mode === mode);
26
28
  const [snapToIndex, setSnapToIndex] = useState<number>(0);
@@ -55,7 +57,7 @@ const PreView: React.FC<
55
57
  <View style={{ paddingHorizontal: pxToDpWidth(34) }}>
56
58
  <MyCarousel data={showData} onSnapToItem={setSnapToIndex} />
57
59
  </View>
58
- <View style={singleStyles.footer}>
60
+ <View style={[singleStyles.footer, { bottom: inset.bottom + 16 }]}>
59
61
  <PreviewFooter
60
62
  actions={[
61
63
  <Back
@@ -2,7 +2,7 @@
2
2
  * @Author: 刘利军
3
3
  * @Date: 2024-12-02 13:37:36
4
4
  * @LastEditors: 刘利军
5
- * @LastEditTime: 2024-12-20 17:30:09
5
+ * @LastEditTime: 2024-12-25 17:08:19
6
6
  * @Description:
7
7
  * @PageName:
8
8
  */
@@ -29,5 +29,6 @@ const styles = StyleSheet.create({
29
29
  right: 0,
30
30
  left: 0,
31
31
  backgroundColor: '#fff',
32
+ zIndex: 19,
32
33
  },
33
34
  });
@@ -2,7 +2,7 @@
2
2
  * @Author: 刘利军
3
3
  * @Date: 2024-12-16 09:21:56
4
4
  * @LastEditors: 刘利军
5
- * @LastEditTime: 2024-12-16 10:27:40
5
+ * @LastEditTime: 2024-12-25 17:12:01
6
6
  * @Description:
7
7
  * @PageName:
8
8
  */
@@ -2,7 +2,7 @@
2
2
  * @Author: 刘利军
3
3
  * @Date: 2024-12-16 09:54:00
4
4
  * @LastEditors: 刘利军
5
- * @LastEditTime: 2024-12-20 17:13:39
5
+ * @LastEditTime: 2024-12-27 10:55:55
6
6
  * @Description:
7
7
  * @PageName:
8
8
  */
@@ -10,7 +10,7 @@ import { Image, StyleSheet, View } from 'react-native';
10
10
  import React from 'react';
11
11
  import PreviewFooter from './PreviewFooter';
12
12
  import { Back, Save } from '../../components';
13
- import { type CustomPhotoFile } from '../../utils';
13
+ import { formatUri, type CustomPhotoFile } from '../../utils';
14
14
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
15
15
 
16
16
  export interface SingleProps extends CommonProps {
@@ -25,13 +25,7 @@ const SinglePre: React.FC<SingleProps> = ({ uri, onBack, onSave }) => {
25
25
  const inset = useSafeAreaInsets();
26
26
  return (
27
27
  <View style={styles.container}>
28
- <Image
29
- style={styles.container}
30
- source={{ uri: `file://${uri}` }}
31
- onLoadEnd={() => {
32
- // 预留水印逻辑
33
- }}
34
- />
28
+ <Image style={styles.container} source={{ uri: formatUri(uri) }} />
35
29
  <View style={[styles.footer, { bottom: inset.bottom + 16 }]}>
36
30
  <PreviewFooter
37
31
  actions={[
@@ -57,11 +51,12 @@ const SinglePre: React.FC<SingleProps> = ({ uri, onBack, onSave }) => {
57
51
  export default SinglePre;
58
52
 
59
53
  export const styles = StyleSheet.create({
60
- container: { flex: 1 },
54
+ container: { flex: 1, backgroundColor: '#fff' },
61
55
  footer: {
62
56
  position: 'absolute',
63
57
  left: 0,
64
58
  right: 0,
59
+ bottom: 0,
65
60
  },
66
61
  image: { width: 60, height: 60 },
67
62
  });
@@ -0,0 +1,128 @@
1
+ import { Dimensions, Image, StyleSheet, View } from 'react-native';
2
+ import React, { useState } from 'react';
3
+ import {
4
+ getContentMeasure,
5
+ getMeasure,
6
+ lastPx,
7
+ type WatermarkType,
8
+ } from '../../utils';
9
+ import Canvas, { Image as CanvasImage } from 'react-native-canvas';
10
+
11
+ const Render: React.FC<{
12
+ data: WatermarkType;
13
+ zIndex?: number;
14
+ }> = ({ data, zIndex }) => {
15
+ const [waterBase64, setWaterBase64] = useState<string>();
16
+
17
+ const getMarkSize = async (width: number, height: number) => {
18
+ let defaultWidth = lastPx(120);
19
+ let defaultHeight = lastPx(64);
20
+ if (!data?.image) {
21
+ defaultWidth = width;
22
+ defaultHeight = height;
23
+ }
24
+ return [data?.width ?? defaultWidth, data?.height ?? defaultHeight];
25
+ };
26
+
27
+ const handleImageRect = async (canvas: Canvas) => {
28
+ const ctx = canvas.getContext('2d');
29
+ const {
30
+ image,
31
+ content = [],
32
+ rotate = data.image ? 0 : -22,
33
+ gap = [100, 100],
34
+ } = data;
35
+
36
+ const measure = await getContentMeasure(ctx, content);
37
+
38
+ const [markWidth, markHeight] = await getMarkSize(
39
+ measure.width,
40
+ measure.height
41
+ );
42
+ const x = lastPx(gap[0] as number);
43
+ const y = lastPx(gap[1] as number);
44
+
45
+ const width = lastPx(markWidth as number);
46
+ const height = lastPx(markHeight as number);
47
+ canvas.width = width + x;
48
+ canvas.height = height + y;
49
+
50
+ const angle = (Math.PI * rotate) / 180;
51
+ ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
52
+ const setDataURL = async () => {
53
+ const imgBase64 = await canvas.toDataURL();
54
+ const base = imgBase64.replace(/^"|"$/g, '');
55
+ setWaterBase64(base.split(',')[1]);
56
+ };
57
+ if (image) {
58
+ const canvasImage = new CanvasImage(canvas);
59
+ canvasImage.crossOrigin = 'anonymous';
60
+ canvasImage.src = image;
61
+ canvasImage.width = width;
62
+ canvasImage.height = height;
63
+
64
+ ctx.rotate(angle);
65
+
66
+ canvasImage.addEventListener('load', async () => {
67
+ ctx.drawImage(
68
+ canvasImage,
69
+ -canvas.height / 2,
70
+ -canvas.width / 2,
71
+ width,
72
+ height
73
+ );
74
+ setDataURL();
75
+ });
76
+ } else {
77
+ if (content.length > 0) {
78
+ ctx.font = `${lastPx(22)}px Arial`;
79
+ ctx.textAlign = 'center';
80
+ ctx.rotate(angle);
81
+ for (let index = 0; index < content.length; index++) {
82
+ const element = content[index];
83
+ const metrics = await getMeasure(ctx, element as string);
84
+ ctx.fillText(element as string, 0, metrics.height * (index + 1));
85
+ }
86
+ setDataURL();
87
+ }
88
+ }
89
+ };
90
+ return (
91
+ <View style={[styles.container, { zIndex }]}>
92
+ <View style={styles.watermark}>
93
+ {waterBase64 ? (
94
+ <Image
95
+ resizeMode="repeat"
96
+ source={{ uri: `data:image/png;base64,${waterBase64}` }}
97
+ style={{
98
+ width: Dimensions.get('window').width,
99
+ height: Dimensions.get('window').height,
100
+ }}
101
+ />
102
+ ) : (
103
+ <Canvas
104
+ ref={(canvas: Canvas) => canvas && handleImageRect(canvas)}
105
+ style={styles.canvas}
106
+ />
107
+ )}
108
+ </View>
109
+ </View>
110
+ );
111
+ };
112
+
113
+ export default Render;
114
+
115
+ export const styles = StyleSheet.create({
116
+ container: {
117
+ ...StyleSheet.absoluteFillObject,
118
+ },
119
+ watermark: {
120
+ display: 'flex',
121
+ flexWrap: 'wrap',
122
+ flex: 1,
123
+ flexDirection: 'row',
124
+ },
125
+ canvas: {
126
+ display: 'none',
127
+ },
128
+ });