feat: improve animating colors (#2471)

# Summary

Currently on Fabric, you cannot animate color properties like `fill` or
`stroke` using Animated with `useNativeDriver: true` or Reanimated.
That's because we have custom structure that needs to be parsed on `JS`
and looks like:

```ts
type ColorStruct = Readonly<{
  type?: WithDefault<Int32, -1>;
  payload?: ColorValue;
  brushRef?: string;
}>;
```

However, special values like `currentColor` / `context-fill` /
`context-stroke` / `/^url\(#(.+)\)$/` can not be animated anyway.
Instead, we should allow passing `ColorValue` directly and detect on the
native side if its ColorValue or ColorStruct.

## Test Plan

Example app -> TestX

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| iOS     |          |
| MacOS   |          |
| Android |          |

## Checklist

- [x] I have tested this on a device and a simulator
- [ ] I added documentation in `README.md`
This commit is contained in:
Jakub Grzywacz
2024-10-04 14:20:54 +02:00
committed by GitHub
parent 74c3bf9e6e
commit 690cddd2f7
56 changed files with 233 additions and 171 deletions

View File

@@ -55,38 +55,6 @@ static id RNSVGConvertFollyDynamicToId(const folly::dynamic &dyn)
}
}
template <typename T>
RNSVGBrush *brushFromColorStruct(const T &fillObject)
{
int type = fillObject.type;
switch (type) {
case -1: // empty struct
return nil;
case 0: // solid color
{
// These are probably expensive allocations since it's often the same value.
// We should memoize colors but look ups may be just as expensive.
RNSVGColor *color = RCTUIColorFromSharedColor(fillObject.payload) ?: [RNSVGColor clearColor];
return [[RNSVGSolidColorBrush alloc] initWithColor:color];
}
case 1: // brush
{
NSArray *arr = @[ @(type), RCTNSStringFromString(fillObject.brushRef) ];
return [[RNSVGPainterBrush alloc] initWithArray:arr];
}
case 2: // currentColor
return [[RNSVGBrush alloc] initWithArray:nil];
case 3: // context-fill
return [[RNSVGContextBrush alloc] initFill];
case 4: // context-stroke
return [[RNSVGContextBrush alloc] initStroke];
default:
RCTLogError(@"Unknown brush type: %d", type);
return nil;
}
}
template <typename T>
void setCommonNodeProps(const T &nodeProps, RNSVGNode *node)
{
@@ -143,10 +111,16 @@ void setCommonRenderableProps(const T &renderableProps, RNSVGRenderable *rendera
if (RCTUIColorFromSharedColor(renderableProps.color)) {
[renderableNode setColor:RCTUIColorFromSharedColor(renderableProps.color)];
}
renderableNode.fill = brushFromColorStruct(renderableProps.fill);
id fill = RNSVGConvertFollyDynamicToId(renderableProps.fill);
if (fill != nil) {
renderableNode.fill = [RCTConvert RNSVGBrush:fill];
}
renderableNode.fillOpacity = renderableProps.fillOpacity;
renderableNode.fillRule = renderableProps.fillRule == 0 ? kRNSVGCGFCRuleEvenodd : kRNSVGCGFCRuleNonzero;
renderableNode.stroke = brushFromColorStruct(renderableProps.stroke);
id stroke = RNSVGConvertFollyDynamicToId(renderableProps.stroke);
if (stroke != nil) {
renderableNode.stroke = [RCTConvert RNSVGBrush:stroke];
}
renderableNode.strokeOpacity = renderableProps.strokeOpacity;
id strokeWidth = RNSVGConvertFollyDynamicToId(renderableProps.strokeWidth);
if (strokeWidth != nil) {