@sdcx/image-crop 0.2.0 → 1.0.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 (34) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +27 -14
  3. package/RNImageCrop.podspec +3 -3
  4. package/android/build.gradle +48 -26
  5. package/android/src/main/java/com/reactnative/imagecrop/ImageCropPackage.java +1 -1
  6. package/android/src/main/java/com/reactnative/imagecrop/{RNImageCropView.java → ImageCropView.java} +12 -12
  7. package/android/src/main/java/com/reactnative/imagecrop/ImageCropViewManager.java +76 -0
  8. package/android/src/main/java/com/reactnative/imagecrop/OnCropEvent.java +34 -0
  9. package/dist/ImageCropNativeComponent.d.ts +22 -0
  10. package/dist/ImageCropNativeComponent.js +5 -0
  11. package/dist/index.d.ts +11 -0
  12. package/dist/index.js +14 -0
  13. package/ios/ImageCrop/RNImageCropView.h +10 -0
  14. package/ios/ImageCrop/RNImageCropView.mm +186 -0
  15. package/package.json +50 -64
  16. package/src/ImageCropNativeComponent.ts +34 -0
  17. package/src/index.tsx +27 -0
  18. package/android/src/main/java/com/reactnative/imagecrop/RNImageCropViewManager.java +0 -77
  19. package/ios/ImageCrop/RNImageCrop.h +0 -30
  20. package/ios/ImageCrop/RNImageCrop.m +0 -129
  21. package/ios/ImageCrop/RNImageCropManager.h +0 -18
  22. package/ios/ImageCrop/RNImageCropManager.m +0 -37
  23. package/lib/ImageCropView.d.ts +0 -15
  24. package/lib/ImageCropView.js +0 -24
  25. package/lib/ImageCropViewRef.d.ts +0 -3
  26. package/lib/ImageCropViewRef.js +0 -1
  27. package/lib/index.d.ts +0 -5
  28. package/lib/index.js +0 -2
  29. package/lib/typings.d.ts +0 -6
  30. package/lib/typings.js +0 -1
  31. package/src/ImageCropView.tsx +0 -69
  32. package/src/ImageCropViewRef.ts +0 -3
  33. package/src/index.ts +0 -6
  34. package/src/typings.ts +0 -6
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 http://www.sdcx.tech
3
+ Copyright (c) 2025 listenzz@163.com
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,40 +2,53 @@
2
2
 
3
3
  `ImageCropView` 是一个 React Native 原生 UI 组件,用于头像裁剪以及图片裁剪,同时支持图像主体识别后设置需要裁剪的主体区域。
4
4
 
5
+ ## 版本兼容
6
+
7
+ | 版本 | RN 版本 | RN 架构 |
8
+ | ---- | ------- | ------- |
9
+ | 0.x | < 0.82 | 旧架构 |
10
+ | 1.x | >= 0.82 | 新架构 |
11
+
5
12
  ## Installation
6
13
 
7
14
  ```bash
8
15
  yarn add @sdcx/image-crop
9
16
  # &
10
- pod install
17
+ cd ios && bundle exec pod install
11
18
  ```
12
19
 
13
20
  ## Usage
14
21
 
22
+ ```tsx
23
+ <ImageCropView
24
+ ref={imageCropViewRef}
25
+ style={'your style'}
26
+ fileUri={'your file uri'}
27
+ cropStyle={'circular' | 'default'}
28
+ onCrop={(uri: string) => {}}
29
+ objectRect={objectRect}
30
+ />
15
31
  ```
16
- <ImageCropView
17
- ref={imageCropViewRef}
18
- style={'your style'}
19
- fileUri={'your file uri'}
20
- cropStyle={'circular' | 'default'}
21
- onCropped={(uri: string) => {}}
22
- objectRect={objectRect}
23
- />
24
- ```
32
+
25
33
  #### cropStyle
26
- 默认default为裁剪矩形,若需要裁剪圆形头像,则设为circular;
34
+
35
+ 默认 default 为裁剪矩形,若需要裁剪圆形头像,则设为 circular;
27
36
 
28
37
  #### objectRect
29
- objectRect可设置默认的图片裁剪区域,且当cropStyle为default时有效。
30
38
 
31
- objectRect的四个属性分别是(单位都是像素px):
39
+ objectRect 可设置默认的图片裁剪区域,且当 cropStyle 为 default 时有效。
40
+
41
+ objectRect 的四个属性分别是(单位都是像素 px):
42
+
32
43
  1. left: 裁剪区域离图片左边的距离;
33
44
  2. top: 裁剪区域离图片上边的距离;
34
45
  3. width: 裁剪区域的宽度;
35
46
  4. height: 裁剪区域的高度;
36
47
 
37
48
  #### 裁剪
49
+
38
50
  ```
39
51
  imageCropViewRef.crop()
40
52
  ```
41
- 然后裁剪成功后会在onCropped属性中回调裁剪后图片的uri;
53
+
54
+ 然后裁剪成功后会在 onCropped 属性中回调裁剪后图片的 uri;
@@ -6,14 +6,14 @@ Pod::Spec.new do |s|
6
6
  s.name = "RNImageCrop"
7
7
  s.version = package["version"]
8
8
  s.summary = package["description"]
9
-
9
+
10
10
  s.homepage = package["homepage"]
11
11
  s.license = package["license"]
12
12
  s.authors = package["author"]
13
- s.platforms = { :ios => "10.0" }
13
+ s.platforms = { :ios => min_ios_version_supported }
14
14
  s.source = { :git => "https://github.com/github-account/image-crop.git", :tag => "#{s.version}" }
15
15
 
16
16
  s.source_files = "ios/ImageCrop/**/*.{h,m,mm}"
17
- s.dependency "React-Core"
18
17
  s.dependency "TOCropViewController"
18
+ install_modules_dependencies(s)
19
19
  end
@@ -1,36 +1,58 @@
1
- // android/build.gradle
1
+ buildscript {
2
+ repositories {
3
+ mavenCentral()
4
+ google()
5
+ }
2
6
 
3
- def safeExtGet(prop, fallback) {
4
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:8.7.2"
9
+ }
5
10
  }
6
11
 
7
12
  apply plugin: 'com.android.library'
13
+ apply plugin: 'com.facebook.react'
14
+
15
+ def safeExtGet(prop, fallback) {
16
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
17
+ }
8
18
 
9
19
  android {
10
- compileOptions {
11
- sourceCompatibility JavaVersion.VERSION_1_8
12
- targetCompatibility JavaVersion.VERSION_1_8
13
- }
14
-
15
- compileSdkVersion safeExtGet('compileSdkVersion', 30)
16
- buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
17
-
18
- defaultConfig {
19
- minSdkVersion safeExtGet('minSdkVersion', 21)
20
- targetSdkVersion safeExtGet('targetSdkVersion', 30)
21
- versionCode 1
22
- versionName "1.0.0"
23
- }
24
-
25
- buildTypes {
26
- release {
27
- consumerProguardFiles 'proguard-rules.pro'
28
- }
29
- }
20
+
21
+ def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
22
+ // Check AGP version for backward compatibility w/react-native versions still on gradle plugin 6
23
+ def major = agpVersion[0].toInteger()
24
+ def minor = agpVersion[1].toInteger()
25
+ if ((major == 7 && minor >= 3) || major >= 8) {
26
+ namespace "com.reactnative.imagecrop"
27
+ buildFeatures {
28
+ buildConfig true
29
+ }
30
+ }
31
+
32
+ compileSdkVersion safeExtGet('compileSdkVersion', 35)
33
+ buildToolsVersion safeExtGet('buildToolsVersion', '35.0.0')
34
+
35
+ defaultConfig {
36
+ minSdkVersion safeExtGet('minSdkVersion', 24)
37
+ targetSdkVersion safeExtGet('targetSdkVersion', 35)
38
+ }
39
+
40
+ sourceSets {
41
+ main {
42
+ java.srcDirs += [
43
+ "generated/java",
44
+ "generated/jni"
45
+ ]
46
+ }
47
+ }
48
+ }
49
+
50
+ repositories {
51
+ google()
52
+ mavenCentral()
30
53
  }
31
54
 
32
55
  dependencies {
33
- implementation fileTree(dir: 'libs', include: ['*.jar'])
34
- implementation 'com.facebook.react:react-native:+'
35
- implementation 'com.github.yalantis:ucrop:2.2.6'
56
+ implementation 'com.github.yalantis:ucrop:2.2.6'
57
+ api 'com.facebook.react:react-native:+'
36
58
  }
@@ -21,6 +21,6 @@ public class ImageCropPackage implements ReactPackage {
21
21
  @NonNull
22
22
  @Override
23
23
  public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
24
- return Arrays.asList(new RNImageCropViewManager());
24
+ return Arrays.asList(new ImageCropViewManager());
25
25
  }
26
26
  }
@@ -13,11 +13,10 @@ import android.widget.FrameLayout;
13
13
  import androidx.annotation.NonNull;
14
14
 
15
15
  import com.facebook.common.logging.FLog;
16
- import com.facebook.react.bridge.Arguments;
17
16
  import com.facebook.react.bridge.ReactContext;
18
17
  import com.facebook.react.bridge.ReadableMap;
19
- import com.facebook.react.bridge.WritableMap;
20
- import com.facebook.react.uimanager.events.RCTEventEmitter;
18
+ import com.facebook.react.uimanager.UIManagerHelper;
19
+ import com.facebook.react.uimanager.events.EventDispatcher;
21
20
  import com.yalantis.ucrop.callback.BitmapCropCallback;
22
21
  import com.yalantis.ucrop.callback.OverlayViewChangeListener;
23
22
  import com.yalantis.ucrop.view.GestureCropImageView;
@@ -29,7 +28,7 @@ import java.lang.reflect.Field;
29
28
  import java.lang.reflect.Method;
30
29
  import java.util.UUID;
31
30
 
32
- public class RNImageCropView extends FrameLayout {
31
+ public class ImageCropView extends FrameLayout {
33
32
  private static final int DEFAULT_COMPRESS_QUALITY = 90;
34
33
  private static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
35
34
 
@@ -148,7 +147,7 @@ public class RNImageCropView extends FrameLayout {
148
147
  private GestureCropImageView mGestureCropImageView;
149
148
  private OverlayView mOverlayView;
150
149
 
151
- public RNImageCropView(Context context) {
150
+ public ImageCropView(Context context) {
152
151
  super(context);
153
152
  init(context);
154
153
  }
@@ -179,13 +178,14 @@ public class RNImageCropView extends FrameLayout {
179
178
  }
180
179
 
181
180
  private void onCropped(String uri) {
182
- WritableMap data = Arguments.createMap();
183
- data.putString("uri", uri);
184
- ReactContext reactContext = (ReactContext) getContext();
185
- reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
186
- getId(),
187
- "onCropped",
188
- data);
181
+ FLog.i(TAG, "onCropped:" +uri);
182
+ int surfaceId = UIManagerHelper.getSurfaceId(getContext());
183
+ int viewId = getId();
184
+ EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag((ReactContext) getContext(), viewId);
185
+ if (eventDispatcher != null) {
186
+ OnCropEvent event = new OnCropEvent(surfaceId, viewId, uri);
187
+ eventDispatcher.dispatchEvent(event);
188
+ }
189
189
  }
190
190
 
191
191
  private int getBitmapDegree(File file) {
@@ -0,0 +1,76 @@
1
+ package com.reactnative.imagecrop;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+
6
+ import com.facebook.react.bridge.ReadableMap;
7
+ import com.facebook.react.common.MapBuilder;
8
+ import com.facebook.react.uimanager.ThemedReactContext;
9
+ import com.facebook.react.uimanager.ViewGroupManager;
10
+ import com.facebook.react.uimanager.ViewManagerDelegate;
11
+ import com.facebook.react.viewmanagers.ImageCropViewManagerDelegate;
12
+ import com.facebook.react.viewmanagers.ImageCropViewManagerInterface;
13
+
14
+ import java.util.Map;
15
+
16
+ public class ImageCropViewManager extends ViewGroupManager<ImageCropView>
17
+ implements ImageCropViewManagerInterface<ImageCropView> {
18
+ private final ImageCropViewManagerDelegate<ImageCropView, ImageCropViewManager> mDelegagte = new ImageCropViewManagerDelegate<>(this);
19
+ private static final String REACT_CLASS = "ImageCropView";
20
+
21
+ @Override
22
+ protected ViewManagerDelegate<ImageCropView> getDelegate() {
23
+ return mDelegagte;
24
+ }
25
+
26
+ @NonNull
27
+ @Override
28
+ public String getName() {
29
+ return REACT_CLASS;
30
+ }
31
+
32
+ @NonNull
33
+ @Override
34
+ protected ImageCropView createViewInstance(@NonNull ThemedReactContext reactContext) {
35
+ return new ImageCropView(reactContext);
36
+ }
37
+
38
+ @Override
39
+ public void setFileUri(ImageCropView view, @Nullable String uri) {
40
+ view.setFileUri(uri);
41
+ }
42
+
43
+ @Override
44
+ public void setCropStyle(ImageCropView view, @Nullable String style) {
45
+ view.setCropStyle(style);
46
+ }
47
+
48
+ @Override
49
+ public void setObjectRect(ImageCropView view, @Nullable ReadableMap value) {
50
+ view.setObjectRect(value);
51
+ }
52
+
53
+ @Override
54
+ public void crop(ImageCropView view) {
55
+ view.crop();
56
+ }
57
+
58
+ @Override
59
+ protected void onAfterUpdateTransaction(@NonNull ImageCropView view) {
60
+ super.onAfterUpdateTransaction(view);
61
+ view.initProperties();
62
+ }
63
+
64
+ @Nullable
65
+ @Override
66
+ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
67
+ return MapBuilder.of(
68
+ OnCropEvent.Name, MapBuilder.of("registrationName", OnCropEvent.JSEventName)
69
+ );
70
+ }
71
+
72
+ @Override
73
+ protected void addEventEmitters(@NonNull ThemedReactContext reactContext, @NonNull ImageCropView view) {
74
+ super.addEventEmitters(reactContext, view);
75
+ }
76
+ }
@@ -0,0 +1,34 @@
1
+ package com.reactnative.imagecrop;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+
6
+ import com.facebook.react.bridge.Arguments;
7
+ import com.facebook.react.bridge.WritableMap;
8
+ import com.facebook.react.uimanager.events.Event;
9
+
10
+ public class OnCropEvent extends Event<OnCropEvent> {
11
+ public static final String Name = "topCrop";
12
+ public static final String JSEventName = "onCrop";
13
+
14
+ private final String uri;
15
+
16
+ public OnCropEvent(int surfaceId, int viewTag, String uri) {
17
+ super(surfaceId, viewTag);
18
+ this.uri = uri;
19
+ }
20
+
21
+ @NonNull
22
+ @Override
23
+ public String getEventName() {
24
+ return Name;
25
+ }
26
+
27
+ @Nullable
28
+ @Override
29
+ protected WritableMap getEventData() {
30
+ WritableMap event = Arguments.createMap();
31
+ event.putString("uri", uri);
32
+ return event;
33
+ }
34
+ }
@@ -0,0 +1,22 @@
1
+ import type { CodegenTypes, HostComponent, ViewProps } from 'react-native';
2
+ export interface ObjectRect {
3
+ top: CodegenTypes.Float;
4
+ left: CodegenTypes.Float;
5
+ width: CodegenTypes.Float;
6
+ height: CodegenTypes.Float;
7
+ }
8
+ export type OnCropEventPayload = {
9
+ uri: string;
10
+ };
11
+ export interface NativeProps extends ViewProps {
12
+ fileUri?: string;
13
+ cropStyle?: CodegenTypes.WithDefault<'circular' | 'default', 'default'>;
14
+ objectRect?: ObjectRect;
15
+ onCrop?: CodegenTypes.DirectEventHandler<OnCropEventPayload>;
16
+ }
17
+ export interface NativeCommands {
18
+ crop: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
19
+ }
20
+ export declare const Commands: NativeCommands;
21
+ declare const _default: HostComponent<NativeProps>;
22
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { codegenNativeComponent, codegenNativeCommands } from 'react-native';
2
+ export const Commands = codegenNativeCommands({
3
+ supportedCommands: ['crop'],
4
+ });
5
+ export default codegenNativeComponent('ImageCropView');
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { ObjectRect, OnCropEventPayload, NativeProps } from './ImageCropNativeComponent';
3
+ import type { NativeSyntheticEvent } from 'react-native';
4
+ export type OnCropEvent = NativeSyntheticEvent<OnCropEventPayload>;
5
+ export type { ObjectRect };
6
+ export interface ImageCropViewInstance {
7
+ crop: () => void;
8
+ }
9
+ export type ImageCropViewProps = NativeProps;
10
+ declare const ImageCropView: React.ForwardRefExoticComponent<NativeProps & React.RefAttributes<ImageCropViewInstance>>;
11
+ export default ImageCropView;
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import React, { useImperativeHandle, useRef } from 'react';
2
+ import ImageCropNativeComponent, { Commands } from './ImageCropNativeComponent';
3
+ const ImageCropView = React.forwardRef((props, ref) => {
4
+ const viewRef = useRef(null);
5
+ useImperativeHandle(ref, () => ({
6
+ crop: () => {
7
+ if (viewRef.current) {
8
+ Commands.crop(viewRef.current);
9
+ }
10
+ },
11
+ }));
12
+ return <ImageCropNativeComponent {...props} ref={viewRef}/>;
13
+ });
14
+ export default ImageCropView;
@@ -0,0 +1,10 @@
1
+ #import <UIKit/UIKit.h>
2
+ #import <React/RCTViewComponentView.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface RNImageCropView : RCTViewComponentView
7
+
8
+ @end
9
+
10
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,186 @@
1
+ #import "RNImageCropView.h"
2
+ #import "UIImage+CropRotate.h"
3
+ #import "TOCropView.h"
4
+
5
+ #import <react/renderer/components/imagecrop/ComponentDescriptors.h>
6
+ #import <react/renderer/components/imagecrop/EventEmitters.h>
7
+ #import <react/renderer/components/imagecrop/Props.h>
8
+ #import <react/renderer/components/imagecrop/RCTComponentViewHelpers.h>
9
+
10
+ #import <React/RCTConversions.h>
11
+ #import <React/RCTLog.h>
12
+
13
+ using namespace facebook::react;
14
+
15
+ @interface RNImageCropView() <RCTImageCropViewViewProtocol>
16
+
17
+ /**
18
+ RN传递进来的属性值:需要裁剪图片路径
19
+ */
20
+ @property(nonatomic, copy, nonnull) NSString *fileUri;
21
+
22
+ @property(nonatomic, assign) ImageCropViewCropStyle cropStyle;
23
+
24
+ @property(nonatomic, assign) ImageCropViewObjectRectStruct objectRect;
25
+
26
+ /**
27
+ The original, uncropped image that was passed to this controller.
28
+ */
29
+ @property (nonatomic, nonnull) UIImage *image;
30
+
31
+ /**
32
+ The cropping style of this particular crop view controller
33
+ */
34
+ @property (nonatomic, assign) TOCropViewCroppingStyle croppingStyle;
35
+
36
+ /**
37
+ The crop view managed by this view controller.
38
+ */
39
+ @property (nonatomic, strong, nonnull) TOCropView *cropView;
40
+
41
+ @property (nonatomic, assign) BOOL initialSetupPerformed;
42
+
43
+ @end
44
+
45
+ @implementation RNImageCropView
46
+
47
+ // Needed because of this: https://github.com/facebook/react-native/pull/37274
48
+ + (void)load {
49
+ [super load];
50
+ }
51
+
52
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
53
+ return concreteComponentDescriptorProvider<ImageCropViewComponentDescriptor>();
54
+ }
55
+
56
+ + (BOOL)shouldBeRecycled {
57
+ return NO;
58
+ }
59
+
60
+ - (instancetype)initWithFrame:(CGRect)frame {
61
+ if ((self = [super initWithFrame:frame])) {
62
+ static const auto defaultProps = std::make_shared<const ImageCropViewProps>();
63
+ _props = defaultProps;
64
+ _cropStyle = ImageCropViewCropStyle::Default;
65
+ }
66
+ return self;
67
+ }
68
+
69
+ - (void)updateProps:(const facebook::react::Props::Shared &)props oldProps:(const facebook::react::Props::Shared &)oldProps {
70
+ const auto &oldViewProps = static_cast<const ImageCropViewProps &>(*_props);
71
+ const auto &newViewProps = static_cast<const ImageCropViewProps &>(*props);
72
+
73
+ // `fileUri`
74
+ if (newViewProps.fileUri != oldViewProps.fileUri) {
75
+ self.fileUri = RCTNSStringFromStringNilIfEmpty(newViewProps.fileUri);
76
+ }
77
+
78
+ // `cropStyle`
79
+ if (newViewProps.cropStyle != oldViewProps.cropStyle) {
80
+ self.cropStyle = newViewProps.cropStyle;
81
+ }
82
+
83
+ // `objectRect`
84
+ if (newViewProps.objectRect.width != oldViewProps.objectRect.width || newViewProps.objectRect.height != oldViewProps.objectRect.height || newViewProps.objectRect.top != oldViewProps.objectRect.top || newViewProps.objectRect.left != oldViewProps.objectRect.left) {
85
+ self.objectRect = newViewProps.objectRect;
86
+ }
87
+
88
+ [self addCropView:self.croppingStyle image:self.image];
89
+
90
+ [super updateProps:props oldProps:oldProps];
91
+ }
92
+
93
+ - (void)setFileUri:(NSString *)fileUri {
94
+ _fileUri = fileUri;
95
+ _image = [UIImage imageWithContentsOfFile: [[NSURL alloc] initWithString:fileUri].path];
96
+ }
97
+
98
+ - (const ImageCropViewEventEmitter &)eventEmitter {
99
+ return static_cast<const ImageCropViewEventEmitter &>(*_eventEmitter);
100
+ }
101
+
102
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
103
+ RCTImageCropViewHandleCommand(self, commandName, args);
104
+ }
105
+
106
+ - (void)crop {
107
+ CGRect cropFrame = self.cropView.imageCropFrame;
108
+ NSInteger angle = self.cropView.angle;
109
+
110
+ UIImage *image = nil;
111
+ if (angle == 0 && CGRectEqualToRect(cropFrame, (CGRect){CGPointZero, self.image.size})) {
112
+ image = self.image;
113
+ } else {
114
+ image = [self.image croppedImageWithFrame:cropFrame angle:angle circularClip:NO];
115
+ }
116
+
117
+ [self saveImage:image];
118
+ }
119
+
120
+ - (void)layoutSubviews {
121
+ [super layoutSubviews];
122
+ if (self.cropView && [self.subviews containsObject:self.cropView]) {
123
+ if (self.initialSetupPerformed) {
124
+ return;
125
+ }
126
+ self.initialSetupPerformed = YES;
127
+
128
+ //设置图片主体检测参数
129
+ if (self.cropStyle != ImageCropViewCropStyle::Circular
130
+ && self.objectRect.width > 0 && self.objectRect.height > 0) {
131
+ int top = static_cast<int>(self.objectRect.top);
132
+ int left = static_cast<int>(self.objectRect.left);
133
+ int width = static_cast<int>(self.objectRect.width);
134
+ int height = static_cast<int>(self.objectRect.height);
135
+ [self.cropView setImageCropFrame:CGRectMake(left, top, width, height)];
136
+ }
137
+
138
+ //存在极小的概率,初始化时只在左上角展示一小块图片区域,目前没有找到较好的方案,尝试延迟一点时间初始化CropView可以解决问题
139
+ // [self.cropView performInitialSetup];
140
+ [self.cropView performSelector:@selector(performInitialSetup) withObject:nil afterDelay:0.05];
141
+ }
142
+ }
143
+
144
+ - (void)addCropView:(TOCropViewCroppingStyle)style image:(UIImage *)image {
145
+ if (!_cropView) {
146
+ _cropView = [[TOCropView alloc] initWithCroppingStyle:style image:image];
147
+ _cropView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
148
+ [_cropView setBackgroundColor:[UIColor blackColor]];
149
+ }
150
+
151
+ if (![self.subviews containsObject:self.cropView]) {
152
+ [self addSubview:_cropView];
153
+ }
154
+ }
155
+
156
+ - (void)saveImage:(UIImage*)image {
157
+ NSString *fileName = [NSString stringWithFormat:@"%@.png", [self produceUUID]];
158
+ NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
159
+ BOOL result =[UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
160
+
161
+ if(result ==YES) {
162
+ NSLog(@"保存成功: %@", filePath);
163
+ NSString *uri = [[NSURL alloc] initFileURLWithPath:filePath].absoluteString;
164
+ [self eventEmitter].onCrop(ImageCropViewEventEmitter::OnCrop{
165
+ .uri = RCTStringFromNSString(uri)
166
+ });
167
+ }
168
+ }
169
+
170
+ - (TOCropViewCroppingStyle)croppingStyle {
171
+ if (self.cropStyle == ImageCropViewCropStyle::Circular) {
172
+ return TOCropViewCroppingStyleCircular;
173
+ }
174
+ return TOCropViewCroppingStyleDefault;
175
+ }
176
+
177
+ - (NSString *)produceUUID {
178
+ CFUUIDRef uuid_ref = CFUUIDCreate(NULL);
179
+ CFStringRef uuid_string_ref= CFUUIDCreateString(NULL, uuid_ref);
180
+ NSString *uuid = [NSString stringWithString:(__bridge NSString *)uuid_string_ref];
181
+ CFRelease(uuid_ref);
182
+ CFRelease(uuid_string_ref);
183
+ return [uuid uppercaseString];
184
+ }
185
+
186
+ @end
package/package.json CHANGED
@@ -1,66 +1,52 @@
1
1
  {
2
- "name": "@sdcx/image-crop",
3
- "description": "A React Native ui component for image crop.",
4
- "version": "0.2.0",
5
- "main": "./lib/index.js",
6
- "typings": "./lib/index.d.ts",
7
- "react-native": "src/index",
8
- "nativePackage": true,
9
- "files": [
10
- "src",
11
- "lib",
12
- "android",
13
- "ios",
14
- "RNImageCrop.podspec",
15
- "!android/build",
16
- "!ios/build",
17
- "!**/__tests__"
18
- ],
19
- "repository": "https://github.com/sdcxtech/react-native-troika",
20
- "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/image-crop#readme",
21
- "author": "sdcx",
22
- "license": "MIT",
23
- "keywords": [
24
- "react-native",
25
- "image-crop"
26
- ],
27
- "scripts": {
28
- "build": "rm -rf ./lib && tsc -p tsconfig.build.json",
29
- "prepare": "npm run build",
30
- "tsc": "tsc",
31
- "test": "jest",
32
- "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
33
- },
34
- "peerDependencies": {
35
- "react": ">=16.8",
36
- "react-native": ">=0.60"
37
- },
38
- "devDependencies": {
39
- "@babel/core": "^7.13.10",
40
- "@babel/runtime": "^7.13.10",
41
- "@react-native-community/eslint-config": "^3.0.0",
42
- "@types/jest": "^27.0.1",
43
- "@types/react": "^17.0.2",
44
- "@types/react-native": "^0.67.0",
45
- "@types/react-test-renderer": "17.0.2",
46
- "babel-jest": "^27.0.6",
47
- "jest": "^27.0.6",
48
- "metro-react-native-babel-preset": "^0.66.2",
49
- "react": "17.0.2",
50
- "react-native": "^0.67.4",
51
- "react-test-renderer": "17.0.2",
52
- "eslint": "^7.32.0",
53
- "typescript": "^4.6.4"
54
- },
55
- "jest": {
56
- "preset": "react-native",
57
- "moduleFileExtensions": [
58
- "ts",
59
- "tsx",
60
- "js",
61
- "jsx",
62
- "json",
63
- "node"
64
- ]
65
- }
2
+ "name": "@sdcx/image-crop",
3
+ "description": "A React Native ui component for image crop.",
4
+ "version": "1.0.0",
5
+ "main": "./dist/index.js",
6
+ "typings": "./dist/index.d.ts",
7
+ "react-native": "src/index",
8
+ "nativePackage": true,
9
+ "files": [
10
+ "src",
11
+ "dist",
12
+ "android",
13
+ "ios",
14
+ "RNImageCrop.podspec",
15
+ "!android/build",
16
+ "!ios/build",
17
+ "!**/__tests__"
18
+ ],
19
+ "repository": "https://github.com/sdcxtech/react-native-troika",
20
+ "homepage": "https://github.com/sdcxtech/react-native-troika/tree/master/packages/image-crop#readme",
21
+ "author": "sdcx",
22
+ "license": "MIT",
23
+ "keywords": [
24
+ "react-native",
25
+ "image-crop"
26
+ ],
27
+ "scripts": {
28
+ "build": "rm -rf ./dist && tsc -p tsconfig.json",
29
+ "prepare": "npm run build",
30
+ "tsc": "tsc",
31
+ "test": "jest",
32
+ "lint": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
33
+ },
34
+ "codegenConfig": {
35
+ "name": "imagecrop",
36
+ "type": "components",
37
+ "jsSrcsDir": "src",
38
+ "android": {
39
+ "javaPackageName": "com.reactnative.imagecrop"
40
+ },
41
+ "ios": {
42
+ "componentProvider": {
43
+ "ImageCropView": "RNImageCropView"
44
+ }
45
+ }
46
+ },
47
+ "peerDependencies": {
48
+ "react": ">=16.8",
49
+ "react-native": ">=0.60"
50
+ },
51
+ "devDependencies": {}
66
52
  }
@@ -0,0 +1,34 @@
1
+ import type { CodegenTypes, HostComponent, ViewProps } from 'react-native';
2
+ import { codegenNativeComponent, codegenNativeCommands } from 'react-native';
3
+
4
+ export interface ObjectRect {
5
+ top: CodegenTypes.Float;
6
+ left: CodegenTypes.Float;
7
+ width: CodegenTypes.Float;
8
+ height: CodegenTypes.Float;
9
+ }
10
+
11
+ export type OnCropEventPayload = {
12
+ uri: string;
13
+ };
14
+
15
+ export interface NativeProps extends ViewProps {
16
+ fileUri?: string;
17
+ cropStyle?: CodegenTypes.WithDefault<'circular' | 'default', 'default'>;
18
+ objectRect?: ObjectRect;
19
+ onCrop?: CodegenTypes.DirectEventHandler<OnCropEventPayload>;
20
+ }
21
+
22
+ export interface NativeCommands {
23
+ // In TypeScript, the React.ElementRef is deprecated.
24
+ // The correct type to use is actually React.ComponentRef.
25
+ // However, due to a bug in Codegen, using ComponentRef will crash the app.
26
+ // We have the fix already, but we need to release a new version of React Native to apply it.
27
+ crop: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
28
+ }
29
+
30
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
31
+ supportedCommands: ['crop'],
32
+ });
33
+
34
+ export default codegenNativeComponent<NativeProps>('ImageCropView') as HostComponent<NativeProps>;
package/src/index.tsx ADDED
@@ -0,0 +1,27 @@
1
+ import React, { useImperativeHandle, useRef } from 'react';
2
+ import ImageCropNativeComponent, { Commands } from './ImageCropNativeComponent';
3
+ import type { ObjectRect, OnCropEventPayload, NativeProps } from './ImageCropNativeComponent';
4
+ import type { NativeSyntheticEvent } from 'react-native';
5
+
6
+ export type OnCropEvent = NativeSyntheticEvent<OnCropEventPayload>;
7
+ export type { ObjectRect };
8
+
9
+ export interface ImageCropViewInstance {
10
+ crop: () => void;
11
+ }
12
+
13
+ export type ImageCropViewProps = NativeProps;
14
+
15
+ const ImageCropView = React.forwardRef<ImageCropViewInstance, ImageCropViewProps>((props, ref) => {
16
+ const viewRef = useRef<React.ComponentRef<typeof ImageCropNativeComponent>>(null);
17
+ useImperativeHandle(ref, () => ({
18
+ crop: () => {
19
+ if (viewRef.current) {
20
+ Commands.crop(viewRef.current);
21
+ }
22
+ },
23
+ }));
24
+ return <ImageCropNativeComponent {...props} ref={viewRef} />;
25
+ });
26
+
27
+ export default ImageCropView;
@@ -1,77 +0,0 @@
1
- package com.reactnative.imagecrop;
2
-
3
- import androidx.annotation.NonNull;
4
- import androidx.annotation.Nullable;
5
-
6
- import com.facebook.react.bridge.ReadableArray;
7
- import com.facebook.react.bridge.ReadableMap;
8
- import com.facebook.react.common.MapBuilder;
9
- import com.facebook.react.uimanager.ThemedReactContext;
10
- import com.facebook.react.uimanager.ViewGroupManager;
11
- import com.facebook.react.uimanager.annotations.ReactProp;
12
-
13
- import java.util.Map;
14
-
15
- public class RNImageCropViewManager extends ViewGroupManager<RNImageCropView> {
16
- private static final String REACT_CLASS = "RNImageCrop";
17
-
18
- private static final int COMMAND_CROP = 1;
19
-
20
- @NonNull
21
- @Override
22
- public String getName() {
23
- return REACT_CLASS;
24
- }
25
-
26
- @NonNull
27
- @Override
28
- protected RNImageCropView createViewInstance(@NonNull ThemedReactContext reactContext) {
29
- return new RNImageCropView(reactContext);
30
- }
31
-
32
- @ReactProp(name = "fileUri")
33
- public void setFileUri(RNImageCropView RNImageCropView, String fileUri) {
34
- RNImageCropView.setFileUri(fileUri);
35
- }
36
-
37
- @ReactProp(name = "cropStyle")
38
- public void setCropStyle(RNImageCropView RNImageCropView, String cropStyle) {
39
- RNImageCropView.setCropStyle(cropStyle);
40
- }
41
-
42
- @ReactProp(name = "objectRect")
43
- public void setObjectRect(RNImageCropView RNImageCropView, ReadableMap objectRect) {
44
- RNImageCropView.setObjectRect(objectRect);
45
- }
46
-
47
- @Override
48
- protected void onAfterUpdateTransaction(@NonNull RNImageCropView RNImageCropView) {
49
- super.onAfterUpdateTransaction(RNImageCropView);
50
- RNImageCropView.initProperties();
51
- }
52
-
53
- @Nullable
54
- @Override
55
- public Map<String, Integer> getCommandsMap() {
56
- return MapBuilder.of("crop", COMMAND_CROP);
57
- }
58
-
59
- @Override
60
- public void receiveCommand(@NonNull RNImageCropView root, String commandId, @Nullable ReadableArray args) {
61
- super.receiveCommand(root, commandId, args);
62
- int commandIdInt = Integer.parseInt(commandId);
63
- switch (commandIdInt) {
64
- case COMMAND_CROP:
65
- root.crop();
66
- break;
67
- }
68
- }
69
-
70
- public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
71
- String name = "phasedRegistrationNames";
72
- String bubbled = "bubbled";
73
- return MapBuilder.<String, Object>builder()
74
- .put("onCropped", MapBuilder.of(name, MapBuilder.of(bubbled, "onCropped")))
75
- .build();
76
- }
77
- }
@@ -1,30 +0,0 @@
1
- #import <UIKit/UIKit.h>
2
- #import <React/UIView+React.h>
3
-
4
- #import "TOCropView.h"
5
-
6
- @interface RNImageCrop : UIView
7
-
8
- /**
9
- RN传递进来的属性值:需要裁剪图片路径
10
- */
11
- @property(nonatomic, copy, nonnull) NSString *fileUri;
12
-
13
- /**
14
- RN传递进来的属性值:裁剪样式,default | circular
15
- */
16
- @property(nonatomic, copy, nullable) NSString *cropStyle;
17
-
18
- /**
19
- RN传递进来的属性值:图像主体Rect,如{"width":208,"left":43,"top":111,"height":354}
20
- */
21
- @property(nonatomic, copy, nullable) id objectRect;
22
-
23
- /**
24
- 选定图片区域后,确认裁剪操作
25
- */
26
- - (void)crop;
27
-
28
- @property(nonatomic, copy, nullable) RCTBubblingEventBlock onCropped;
29
-
30
- @end
@@ -1,129 +0,0 @@
1
- #import "RNImageCrop.h"
2
- #import "UIImage+CropRotate.h"
3
-
4
- @interface RNImageCrop()
5
-
6
- /**
7
- The original, uncropped image that was passed to this controller.
8
- */
9
- @property (nonatomic, nonnull) UIImage *image;
10
-
11
- /**
12
- The cropping style of this particular crop view controller
13
- */
14
- @property (nonatomic) TOCropViewCroppingStyle croppingStyle;
15
-
16
- /**
17
- The crop view managed by this view controller.
18
- */
19
- @property (nonatomic, strong, nonnull) TOCropView *cropView;
20
-
21
- @property (nonatomic, assign) BOOL initialSetupPerformed;
22
-
23
- @end
24
-
25
- @implementation RNImageCrop
26
-
27
- - (instancetype)initWithFrame:(CGRect)frame {
28
- if ((self = [super initWithFrame:frame])) {
29
- }
30
- return self;
31
- }
32
-
33
- - (void)layoutSubviews {
34
- [super layoutSubviews];
35
- if (self.cropView && [self.subviews containsObject:self.cropView]) {
36
- if (self.initialSetupPerformed) {
37
- return;
38
- }
39
- self.initialSetupPerformed = YES;
40
-
41
- //设置图片主体检测参数
42
- if (![_cropStyle isEqualToString:@"circular"]
43
- && _objectRect != nil
44
- && [_objectRect objectForKey:@"top"]
45
- && [_objectRect objectForKey:@"left"]
46
- && [_objectRect objectForKey:@"width"]
47
- && [_objectRect objectForKey:@"height"]) {
48
- int top = [[_objectRect valueForKey:@"top"] intValue];
49
- int left = [[_objectRect valueForKey:@"left"] intValue];
50
- int width = [[_objectRect valueForKey:@"width"] intValue];
51
- int height = [[_objectRect valueForKey:@"height"] intValue];
52
- [self.cropView setImageCropFrame:CGRectMake(left, top, width, height)];
53
- }
54
-
55
- //存在极小的概率,初始化时只在左上角展示一小块图片区域,目前没有找到较好的方案,尝试延迟一点时间初始化CropView可以解决问题
56
- // [self.cropView performInitialSetup];
57
- [self.cropView performSelector:@selector(performInitialSetup) withObject:nil afterDelay:0.05];
58
- }
59
- }
60
-
61
- - (void)setFileUri:(NSString *)fileUri {
62
- _fileUri = fileUri;
63
- _image = [UIImage imageWithContentsOfFile: [[NSURL alloc] initWithString:fileUri].path];
64
- }
65
-
66
- - (void)didSetProps:(NSArray<NSString *> *)changedProps {
67
- [self addCropView:self.croppingStyle image:self.image];
68
- }
69
-
70
- - (void)addCropView:(TOCropViewCroppingStyle)style image:(UIImage *)image {
71
- if (!_cropView) {
72
- _cropView = [[TOCropView alloc] initWithCroppingStyle:style image:image];
73
- _cropView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
74
- [_cropView setBackgroundColor:[UIColor blackColor]];
75
- }
76
-
77
- if (![self.subviews containsObject:self.cropView]) {
78
- [self addSubview:_cropView];
79
- }
80
- }
81
-
82
- - (void)crop {
83
- CGRect cropFrame = self.cropView.imageCropFrame;
84
- NSInteger angle = self.cropView.angle;
85
-
86
- UIImage *image = nil;
87
- if (angle == 0 && CGRectEqualToRect(cropFrame, (CGRect){CGPointZero, self.image.size})) {
88
- image = self.image;
89
- } else {
90
- image = [self.image croppedImageWithFrame:cropFrame angle:angle circularClip:NO];
91
- }
92
-
93
- [self saveImage:image];
94
- }
95
-
96
- - (void)saveImage:(UIImage*)image {
97
- NSString *fileName = [NSString stringWithFormat:@"%@.png", [self produceUUID]];
98
- NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
99
- BOOL result =[UIImagePNGRepresentation(image)writeToFile:filePath atomically:YES];
100
-
101
- if(result ==YES) {
102
- NSLog(@"保存成功: %@", filePath);
103
-
104
- if (self.onCropped) {
105
- self.onCropped(@{
106
- @"uri": [[NSURL alloc] initFileURLWithPath:filePath].absoluteString
107
- });
108
- }
109
- }
110
- }
111
-
112
- - (TOCropViewCroppingStyle)croppingStyle {
113
- if (_cropStyle && [_cropStyle isEqualToString:@"circular"]) {
114
- return TOCropViewCroppingStyleCircular;
115
- }
116
- return TOCropViewCroppingStyleDefault;
117
- }
118
-
119
-
120
- - (NSString *)produceUUID {
121
- CFUUIDRef uuid_ref = CFUUIDCreate(NULL);
122
- CFStringRef uuid_string_ref= CFUUIDCreateString(NULL, uuid_ref);
123
- NSString *uuid = [NSString stringWithString:(__bridge NSString *)uuid_string_ref];
124
- CFRelease(uuid_ref);
125
- CFRelease(uuid_string_ref);
126
- return [uuid uppercaseString];
127
- }
128
-
129
- @end
@@ -1,18 +0,0 @@
1
- //
2
- // RNImageCropManager.h
3
- // RNImageCrop
4
- //
5
- // Created by vibe on 2023/4/28.
6
- //
7
-
8
- #import <React/RCTViewManager.h>
9
- #import <React/RCTBridgeModule.h>
10
- #import <React/RCTUIManager.h>
11
-
12
- NS_ASSUME_NONNULL_BEGIN
13
-
14
- @interface RNImageCropManager : RCTViewManager
15
-
16
- @end
17
-
18
- NS_ASSUME_NONNULL_END
@@ -1,37 +0,0 @@
1
- //
2
- // RNCropManager.m
3
- // RNCrop
4
- //
5
- // Created by vibe on 2022/2/8.
6
- //
7
-
8
- #import "RNImageCropManager.h"
9
- #import "RNImageCrop.h"
10
-
11
- @implementation RNImageCropManager
12
-
13
- RCT_EXPORT_MODULE(RNImageCrop)
14
- RCT_EXPORT_VIEW_PROPERTY(fileUri, NSString)
15
- RCT_EXPORT_VIEW_PROPERTY(cropStyle, NSString)
16
- RCT_EXPORT_VIEW_PROPERTY(objectRect, id)
17
- RCT_EXPORT_VIEW_PROPERTY(onCropped, RCTBubblingEventBlock)
18
- RCT_EXPORT_METHOD(crop:(nonnull NSNumber *)reactTag) {
19
- [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
20
- RNImageCrop *rnCrop = viewRegistry[reactTag];
21
- if (![rnCrop isKindOfClass:[RNImageCrop class]]) {
22
- RCTLogError(@"Invalid view returned from registry, expecting RNCrop, got: %@", rnCrop);
23
- } else {
24
- dispatch_async(dispatch_get_main_queue(), ^{
25
- RNImageCrop *rnCrop = (RNImageCrop *)viewRegistry[reactTag];
26
- [rnCrop crop];
27
- });
28
- }
29
- }];
30
- }
31
-
32
- - (UIView *)view
33
- {
34
- return [RNImageCrop new];
35
- }
36
-
37
- @end
@@ -1,15 +0,0 @@
1
- import { ViewProps, ViewStyle } from 'react-native';
2
- import React from 'react';
3
- import { ImageCropViewRef } from './ImageCropViewRef';
4
- import { ObjectRect } from './typings';
5
- interface SupperProps extends ViewProps {
6
- fileUri: string;
7
- objectRect?: ObjectRect;
8
- cropStyle?: 'circular' | 'default';
9
- }
10
- interface CropViewProps extends SupperProps {
11
- style?: ViewStyle;
12
- onCropped: (uri: string) => void;
13
- }
14
- declare const ImageCropView: React.ForwardRefExoticComponent<CropViewProps & React.RefAttributes<ImageCropViewRef>>;
15
- export default ImageCropView;
@@ -1,24 +0,0 @@
1
- import { findNodeHandle, Platform, requireNativeComponent, UIManager } from 'react-native';
2
- import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
3
- const CropViewNativeComponent = requireNativeComponent('RNImageCrop');
4
- const ImageCropView = forwardRef(({ fileUri, cropStyle, objectRect, style, onCropped }, ref) => {
5
- const reactTag = useRef(null);
6
- const crop = useCallback(() => {
7
- UIManager.dispatchViewManagerCommand(reactTag.current, Platform.OS === 'ios'
8
- ? UIManager.getViewManagerConfig('RNImageCrop').Commands.crop
9
- : UIManager.getViewManagerConfig('RNImageCrop').Commands.crop.toString(), Platform.OS === 'ios' ? [] : [reactTag.current]);
10
- }, []);
11
- const onNativeCropped = useCallback(({ nativeEvent }) => {
12
- const uri = nativeEvent.uri;
13
- if (onCropped) {
14
- onCropped(uri);
15
- }
16
- }, [onCropped]);
17
- useImperativeHandle(ref, () => ({
18
- crop,
19
- }), [crop]);
20
- return (<CropViewNativeComponent fileUri={fileUri} cropStyle={cropStyle} objectRect={objectRect} onCropped={onNativeCropped} style={style} ref={mRef => {
21
- reactTag.current = findNodeHandle(mRef);
22
- }}/>);
23
- });
24
- export default ImageCropView;
@@ -1,3 +0,0 @@
1
- export interface ImageCropViewRef {
2
- crop: () => void;
3
- }
@@ -1 +0,0 @@
1
- export {};
package/lib/index.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import ImageCropView from './ImageCropView';
2
- import { ImageCropViewRef } from './ImageCropViewRef';
3
- import { ObjectRect } from './typings';
4
- export { ImageCropView };
5
- export type { ImageCropViewRef, ObjectRect };
package/lib/index.js DELETED
@@ -1,2 +0,0 @@
1
- import ImageCropView from './ImageCropView';
2
- export { ImageCropView };
package/lib/typings.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export interface ObjectRect {
2
- top: number;
3
- left: number;
4
- width: number;
5
- height: number;
6
- }
package/lib/typings.js DELETED
@@ -1 +0,0 @@
1
- export {};
@@ -1,69 +0,0 @@
1
- import { findNodeHandle, Platform, requireNativeComponent, UIManager, ViewProps, ViewStyle } from 'react-native'
2
- import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'
3
- import { ImageCropViewRef } from './ImageCropViewRef'
4
- import { ObjectRect } from './typings'
5
-
6
- const CropViewNativeComponent = requireNativeComponent<CropViewNativeComponentProps>('RNImageCrop')
7
-
8
- interface SupperProps extends ViewProps {
9
- fileUri: string
10
- objectRect?: ObjectRect
11
- cropStyle?: 'circular' | 'default'
12
- }
13
-
14
- interface CropViewNativeComponentProps extends SupperProps {
15
- onCropped: (callback: any) => void
16
- }
17
-
18
- interface CropViewProps extends SupperProps {
19
- style?: ViewStyle
20
- onCropped: (uri: string) => void
21
- }
22
-
23
- const ImageCropView = forwardRef<ImageCropViewRef, CropViewProps>(
24
- ({ fileUri, cropStyle, objectRect, style, onCropped }: CropViewProps, ref) => {
25
- const reactTag = useRef<number | null>(null)
26
- const crop = useCallback(() => {
27
- UIManager.dispatchViewManagerCommand(
28
- reactTag.current,
29
- Platform.OS === 'ios'
30
- ? UIManager.getViewManagerConfig('RNImageCrop').Commands.crop
31
- : UIManager.getViewManagerConfig('RNImageCrop').Commands.crop.toString(),
32
- Platform.OS === 'ios' ? [] : [reactTag.current],
33
- )
34
- }, [])
35
-
36
- const onNativeCropped = useCallback(
37
- ({ nativeEvent }: any) => {
38
- const uri = nativeEvent.uri
39
- if (onCropped) {
40
- onCropped(uri)
41
- }
42
- },
43
- [onCropped],
44
- )
45
-
46
- useImperativeHandle(
47
- ref,
48
- () => ({
49
- crop,
50
- }),
51
- [crop],
52
- )
53
-
54
- return (
55
- <CropViewNativeComponent
56
- fileUri={fileUri}
57
- cropStyle={cropStyle}
58
- objectRect={objectRect}
59
- onCropped={onNativeCropped}
60
- style={style}
61
- ref={mRef => {
62
- reactTag.current = findNodeHandle(mRef)
63
- }}
64
- />
65
- )
66
- },
67
- )
68
-
69
- export default ImageCropView
@@ -1,3 +0,0 @@
1
- export interface ImageCropViewRef {
2
- crop: () => void
3
- }
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- import ImageCropView from './ImageCropView'
2
- import { ImageCropViewRef } from './ImageCropViewRef'
3
- import { ObjectRect } from './typings'
4
-
5
- export { ImageCropView }
6
- export type { ImageCropViewRef, ObjectRect }
package/src/typings.ts DELETED
@@ -1,6 +0,0 @@
1
- export interface ObjectRect {
2
- top: number
3
- left: number
4
- width: number
5
- height: number
6
- }