react-native-fast-image 8.21.0 → 8.23.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.
- package/README.md +31 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +15 -0
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +199 -3
- package/dist/constants.d.ts +16 -0
- package/dist/index.cjs.js +18 -2
- package/dist/index.cjs.js.flow +7 -0
- package/dist/index.cjs.web.js +193 -0
- package/dist/index.cjs.web.js.flow +154 -0
- package/dist/index.d.ts +37 -16
- package/dist/index.js +18 -2
- package/dist/index.js.flow +7 -0
- package/dist/index.web.d.ts +6 -0
- package/dist/index.web.js +165 -0
- package/dist/index.web.js.flow +154 -0
- package/ios/FastImage/FFFastImageView.h +6 -0
- package/ios/FastImage/FFFastImageView.m +107 -7
- package/ios/FastImage/FFFastImageViewManager.m +3 -0
- package/package.json +6 -4
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
@property(nonatomic, assign) CGSize blurredForSize;
|
|
27
27
|
// The blurred image showing (untinted), so a tint change can re-show it.
|
|
28
28
|
@property(nonatomic, strong) UIImage* blurredImage;
|
|
29
|
+
// The image being blurred fades in once it's blurred (transition).
|
|
30
|
+
@property(nonatomic, assign) BOOL fadesBlurredImage;
|
|
31
|
+
// The image being blurred is the first loaded one the view shows: until it's
|
|
32
|
+
// blurred, the view shows no loaded image (nothing, or defaultSource).
|
|
33
|
+
@property(nonatomic, assign) BOOL blursOverNothing;
|
|
29
34
|
// Whether the current load was already restarted after the app came back
|
|
30
35
|
// from the background (see downloadImage:).
|
|
31
36
|
@property(nonatomic, assign) BOOL retriedAfterBackground;
|
|
@@ -117,6 +122,77 @@ static UIImage* FFFBlurredImage(UIImage* image, CGFloat scale, CGFloat radius, B
|
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
|
|
125
|
+
// Whether a loaded image fades in (the `transition` prop). Over a loaded
|
|
126
|
+
// image (a new source) only with betweenImages: otherwise it replaces it at
|
|
127
|
+
// once, as Android's image libraries fade an image in from nothing and never
|
|
128
|
+
// between images. Then as SDWebImage's transitions and those libraries
|
|
129
|
+
// decide: from the memory cache it shows at once (skipOnCacheHit 'memory',
|
|
130
|
+
// the default), and from the disk cache too with 'all'. Downloads, local
|
|
131
|
+
// files and bundled images fade.
|
|
132
|
+
- (BOOL) fadesImageFromCache: (SDImageCacheType)cacheType {
|
|
133
|
+
if (self.transitionDuration <= 0 || ([self displaysLoadedImage] && !self.transitionBetweenImages)) {
|
|
134
|
+
return NO;
|
|
135
|
+
}
|
|
136
|
+
NSString* skip = self.transitionSkipOnCacheHit;
|
|
137
|
+
if ([skip isEqualToString: @"none"]) {
|
|
138
|
+
return YES;
|
|
139
|
+
}
|
|
140
|
+
if (cacheType == SDImageCacheTypeMemory) {
|
|
141
|
+
return NO;
|
|
142
|
+
}
|
|
143
|
+
// SDWebImage also keeps local files and bundled images in its disk cache,
|
|
144
|
+
// so with 'all' they usually only fade the first time.
|
|
145
|
+
return !(cacheType == SDImageCacheTypeDisk && [skip isEqualToString: @"all"]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Whether the view shows a loaded image now: a loaded image that's still
|
|
149
|
+
// being blurred isn't showing yet.
|
|
150
|
+
- (BOOL) displaysLoadedImage {
|
|
151
|
+
return self.showsLoadedImage && !self.blursOverNothing;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Shows a loaded image, fading it in over what the view shows if `fade`. A
|
|
155
|
+
// blurred image shows, and fades in, once it's blurred.
|
|
156
|
+
- (void) showLoadedImage: (UIImage*)image fade: (BOOL)fade {
|
|
157
|
+
if ([self blurs: image]) {
|
|
158
|
+
self.blursOverNothing = ![self displaysLoadedImage];
|
|
159
|
+
self.fadesBlurredImage = fade;
|
|
160
|
+
self.image = image;
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
self.blursOverNothing = NO;
|
|
164
|
+
self.fadesBlurredImage = NO;
|
|
165
|
+
if (fade) {
|
|
166
|
+
[self fadeIn: ^{
|
|
167
|
+
self.image = image;
|
|
168
|
+
}];
|
|
169
|
+
} else {
|
|
170
|
+
self.image = image;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Makes the change (showing another image), fading it in over what the view
|
|
175
|
+
// showed.
|
|
176
|
+
- (void) fadeIn: (void (^)(void))change {
|
|
177
|
+
if (!super.image || !self.layer.presentationLayer) {
|
|
178
|
+
// Nothing to cross-dissolve from: the view shows no image, or isn't
|
|
179
|
+
// on screen yet (e.g. a new view whose image loads as it mounts),
|
|
180
|
+
// where a cross-dissolve has nothing drawn to start from and shows
|
|
181
|
+
// the image at once. Fade the view in instead.
|
|
182
|
+
change();
|
|
183
|
+
CABasicAnimation* fadeIn = [CABasicAnimation animationWithKeyPath: @"opacity"];
|
|
184
|
+
fadeIn.fromValue = @0;
|
|
185
|
+
fadeIn.duration = self.transitionDuration / 1000;
|
|
186
|
+
[self.layer addAnimation: fadeIn forKey: @"FFFFadeIn"];
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
[UIView transitionWithView: self
|
|
190
|
+
duration: self.transitionDuration / 1000
|
|
191
|
+
options: UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowUserInteraction
|
|
192
|
+
animations: change
|
|
193
|
+
completion: nil];
|
|
194
|
+
}
|
|
195
|
+
|
|
120
196
|
- (void) setPaused: (BOOL)paused {
|
|
121
197
|
if (_paused == paused) {
|
|
122
198
|
return;
|
|
@@ -246,7 +322,17 @@ static UIImage* FFFBlurredImage(UIImage* image, CGFloat scale, CGFloat radius, B
|
|
|
246
322
|
}
|
|
247
323
|
self.originalImage = image && self.imageColor ? image : nil;
|
|
248
324
|
self.blurredImage = nil;
|
|
249
|
-
|
|
325
|
+
self.blursOverNothing = NO;
|
|
326
|
+
// The blur was removed before it was ready: the fade it was waiting for
|
|
327
|
+
// happens now.
|
|
328
|
+
if (self.fadesBlurredImage) {
|
|
329
|
+
self.fadesBlurredImage = NO;
|
|
330
|
+
[self fadeIn: ^{
|
|
331
|
+
[self showImage: image];
|
|
332
|
+
}];
|
|
333
|
+
} else {
|
|
334
|
+
[self showImage: image];
|
|
335
|
+
}
|
|
250
336
|
}
|
|
251
337
|
|
|
252
338
|
// Shows the image, tinted as React Native's Image does it: UIKit draws a
|
|
@@ -336,7 +422,15 @@ static UIImage* FFFBlurredImage(UIImage* image, CGFloat scale, CGFloat radius, B
|
|
|
336
422
|
- (void) showBlurredImage: (UIImage*)image count: (NSUInteger)count {
|
|
337
423
|
if (self.imageCount == count) {
|
|
338
424
|
self.blurredImage = image;
|
|
339
|
-
|
|
425
|
+
self.blursOverNothing = NO;
|
|
426
|
+
if (self.fadesBlurredImage) {
|
|
427
|
+
self.fadesBlurredImage = NO;
|
|
428
|
+
[self fadeIn: ^{
|
|
429
|
+
[self showImage: image];
|
|
430
|
+
}];
|
|
431
|
+
} else {
|
|
432
|
+
[self showImage: image];
|
|
433
|
+
}
|
|
340
434
|
}
|
|
341
435
|
}
|
|
342
436
|
|
|
@@ -513,8 +607,9 @@ NSString *FFFErrorMessage(NSError *error)
|
|
|
513
607
|
if (changed) {
|
|
514
608
|
// The view shows other content now: don't keep the current image
|
|
515
609
|
// while the next one loads (reloadImage clears it), even if the
|
|
516
|
-
// source is the same.
|
|
610
|
+
// source is the same, nor a fade from it.
|
|
517
611
|
self.showsLoadedImage = NO;
|
|
612
|
+
[self.layer removeAllAnimations];
|
|
518
613
|
_needsReload = YES;
|
|
519
614
|
}
|
|
520
615
|
}
|
|
@@ -646,7 +741,7 @@ NSString *FFFErrorMessage(NSError *error)
|
|
|
646
741
|
}
|
|
647
742
|
return;
|
|
648
743
|
}
|
|
649
|
-
[self
|
|
744
|
+
[self showLoadedImage: image fade: [self fadesImageFromCache: SDImageCacheTypeNone]];
|
|
650
745
|
if (self.onFastImageProgress) {
|
|
651
746
|
self.onFastImageProgress(@{
|
|
652
747
|
@"loaded": @(1),
|
|
@@ -799,13 +894,18 @@ NSString *FFFErrorMessage(NSError *error)
|
|
|
799
894
|
// class to SDAnimatedImage (loadContext sets it). Only while this is the
|
|
800
895
|
// current load: SDWebImage sets the placeholder when a load it cancelled
|
|
801
896
|
// completes, which could replace a newer image.
|
|
897
|
+
UIImage* placeholder = _defaultSource;
|
|
802
898
|
SDSetImageBlock setImage = ^(UIImage* _Nullable image, NSData* _Nullable data, SDImageCacheType cacheType, NSURL* _Nullable imageURL) {
|
|
803
|
-
if (weakSelf.loadCount
|
|
804
|
-
|
|
899
|
+
if (weakSelf.loadCount != load) {
|
|
900
|
+
return;
|
|
805
901
|
}
|
|
902
|
+
// The loaded image, not defaultSource (shown while it loads, or if it
|
|
903
|
+
// fails).
|
|
904
|
+
BOOL loaded = image && image != placeholder;
|
|
905
|
+
[weakSelf showLoadedImage: image fade: loaded && [weakSelf fadesImageFromCache: cacheType]];
|
|
806
906
|
};
|
|
807
907
|
[self sd_internalSetImageWithURL: url
|
|
808
|
-
placeholderImage:
|
|
908
|
+
placeholderImage: placeholder
|
|
809
909
|
options: options
|
|
810
910
|
context: context
|
|
811
911
|
setImageBlock: setImage
|
|
@@ -107,6 +107,9 @@ RCT_EXPORT_VIEW_PROPERTY(recyclingKey, NSString)
|
|
|
107
107
|
RCT_EXPORT_VIEW_PROPERTY(loopCount, NSInteger)
|
|
108
108
|
RCT_EXPORT_VIEW_PROPERTY(imageRendering, NSString)
|
|
109
109
|
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL)
|
|
110
|
+
RCT_EXPORT_VIEW_PROPERTY(transitionDuration, double)
|
|
111
|
+
RCT_EXPORT_VIEW_PROPERTY(transitionBetweenImages, BOOL)
|
|
112
|
+
RCT_EXPORT_VIEW_PROPERTY(transitionSkipOnCacheHit, NSString)
|
|
110
113
|
RCT_EXPORT_VIEW_PROPERTY(downsample, BOOL)
|
|
111
114
|
RCT_EXPORT_VIEW_PROPERTY(blurRadius, CGFloat)
|
|
112
115
|
RCT_EXPORT_VIEW_PROPERTY(onFastImageLoadStart, RCTDirectEventBlock)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-fast-image",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.23.0",
|
|
4
4
|
"description": "🚩 FastImage, performant React Native image component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
},
|
|
20
20
|
"license": "(MIT AND Apache-2.0)",
|
|
21
21
|
"author": "Dylan Vann <dylan@dylanvann.com> (https://dylanvann.com)",
|
|
22
|
-
"main": "dist/index.cjs
|
|
23
|
-
"module": "dist/index
|
|
22
|
+
"main": "dist/index.cjs",
|
|
23
|
+
"module": "dist/index",
|
|
24
|
+
"react-native": "dist/index.cjs",
|
|
25
|
+
"browser": "dist/index.web.js",
|
|
24
26
|
"typings": "dist/index.d.ts",
|
|
25
27
|
"files": [
|
|
26
28
|
"android/build.gradle",
|
|
@@ -34,7 +36,7 @@
|
|
|
34
36
|
"RNFastImage.podspec"
|
|
35
37
|
],
|
|
36
38
|
"scripts": {
|
|
37
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json && rolldown -c &&
|
|
39
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && rolldown -c && for f in index index.cjs index.web index.cjs.web; do cp src/index.js.flow dist/$f.js.flow; done",
|
|
38
40
|
"lint": "oxlint",
|
|
39
41
|
"release": "semantic-release",
|
|
40
42
|
"test": "bun test",
|