-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Proposal: Drop Shadow support for View, Text, Image, and more
Implement support for ViewStyle.shadow* and TextStyle.textShadow* for View, Text, and Image.
Summary
Tasks
- A C++/winrt port of
DropShadowPanel, to be calledShadowedContentControlwill be used for shadows cast from all XAML-based components. - Implement TextStyle.textShadow* support
- Implement ViewStyle.shadow* support for ViewStyle.border* and ViewStyle.backgroundColor props
- Implement ViewStyle.shadow* support for RN
, ImageViewManager
- Optionally, extend and share
ShadowedContentControlfor reuse by third parties
Motivation
React Native supports shadows for View, Text, and Image using the ViewStyle.shadow* and TextStyle.textShadow* props. Android and iOS both support these properties (though with some notable differences), but support hasn’t been implemented in react-native-windows vNext yet. The WPF RN project has support but a straight port to UWP isn’t possible due to differences in shadow APIs available between WPF and UWP + XAML + Composition.
Shadows will improve application visuals and usability. In addition to aiding visualization of the application's information hierarchy and drawing attention to UX of interest, drop shadows can also be used to enhance readability of text against low-contrast or "busy" background content such as a photograph. A shadow without vertical or horizontal offsets can also be used to create a "glow" effect, used for similar design and usability goals.
Basic example
<View>
<Text style={{shadowColor: "red", shadowRadius: 2, shadowOpacity: 1, shadowOffset: {width: 1, height: 1}}}>This is text using ViewStyle.shadow* props</Text>
<Text style={{textShadowColor: 'red', textShadowRadius: 2, textShadowOffset: {width: 1, height: 1}}}>This is text using TextStyle.textShadow* props</Text>
<Image source={{uri: 'https://cdn.freebiesupply.com/logos/large/2x/disney-1-logo-png-transparent.png'}} style={{width: 200, height: 200, shadowColor: "red", shadowRadius: 2, shadowOpacity: 1, shadowOffset: {width: 1, height: 1}}} />
<View style={{width: 300, height: 50, backgroundColor: "blue", shadowColor: "red", shadowRadius: 2, shadowOpacity: 1, shadowOffset: {width: 1, height: 1}, marginBottom: 20}} />
<View style={{width: 300, height: 250, borderColor: "blue", borderWidth: 5, borderRadius: 20, borderStyle: 'dotted', shadowColor: "red", shadowRadius: 2, shadowOpacity: 1, shadowOffset: {width: 1, height: 1}}}>
<Text>Shadow on a view with dotted border. Child elements (text and image) inherit ViewStyle.shadow* props</Text>
<Image source={{uri: 'https://cdn.freebiesupply.com/logos/large/2x/disney-1-logo-png-transparent.png'}} style={{width: 200, height: 200}} />
</View>
</View>
Open Questions
View shadows, iOS, Android, and staging work
iOS and Android's support for shadows on elements differs in one important way: on iOS all the children of the view also get the same drop shadow visuals applied to them. On android, the view's shadow only affects the view's border and background visuals, not all the children too. This can be seen in the screenshot above.
Android's model seems ideal because it allows for the most flexibility. On iOS, if you want a View's border to have shadow visuals, but you don't want the children to, then you have to put the children in another superimposed view. On Android, you simply set the shadow props on all the elements you want to have shadows. Similarly, in the event where child and a parent should both have shadows, on Android you're able to use the specialized textShadow props on the Text, which visually look better than iOS's inherited ViewStyle.shadow* visuals (see first two Text shadows in screenshot above).
However, I can guess how iOS' behavior came about. If we were to implement ShadowedContentControl using the "SoftwareBitmap -> CompositionBrush -> AlphaMask" technique, the shadow visuals would apply not only to the View itself, but also all children.
With that background information out of the way, we can discuss work staging. Rewriting ViewViewManager to use Composition border rendering instead of a XAML control would be a substantial task. The fastest path to supporting View shadows is to use the "SoftwareBitmap -> CompositionBrush -> AlphaMask" technique. However, this would mean children inherit shadows, like iOS. It also would be inferior performance-wise. Luckily, shadows are opt-in so it's pay-for-play.
Long-term there seems to be agreement on adopting Composition for a plethora of benefits (smaller XAML tree, improved performance, support for ViewStyle.borderStyle "dotted" and "dashed", etc.), but ShadowedContentControl + SoftwareBitmap -> CompositionBrush -> AlphaMask could help us get to basic drop shadow support faster. The short-term work also wouldn't go wasted, because it accrues towards making ShadowedContentControl work with arbitrary XAML types, useful for third parties wanting shadows for their own custom NativeComponents written wrapping XAML components.
RN Glyph / NativeComponent "PLYIcon"
IconViewManager is currently implemented using Glyph, not a XAML . We would like shadow support for these elements too, but I'm not sure which implementation would be preferred. Some possibilities:
a. If ShadowedContentContainer has support for arbitrary XAML content via the SoftwareBitmap -> CompositionBrush -> AlphaMask approach, Glyph could be wrapped in a ShadowedContentContainer.
b. IconViewManager could be rewritten to use instead of Glyph. The TextBlock could then be wrapped using ShadowedContentContainer.
c. IconViewMangager could be rewritten to be drawn using Composition with DropShadow.
d. Alternatively, our client could migrate from Glyph-based icons to using or components.
Popup/Flyout and Context Menu
Shadows are also of interest for these components but I haven't investigated enough to include anything along with this proposal.
Further reading, dev plan notes
2020/10/28: Attaching investigation notes I put together back in July 2019 when I was formulating a plan of attack. These notes were only intended for personal purposes and haven't been cleaned up for broad publication, so grant me a little leeway regarding rough edges. I hope it helps anyone that might pick up this task in the future.
Drop Shadows in react-native-windows.pdf