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.
@@ -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
- [self showImage: image];
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
- [self showImage: image];
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 setImage: image];
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 == load) {
804
- weakSelf.image = image;
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: _defaultSource
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.21.0",
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.js",
23
- "module": "dist/index.js",
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 && cp src/index.js.flow dist/index.js.flow && cp src/index.js.flow dist/index.cjs.js.flow",
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",