React Native font outline / textShadow
There is at least one way to make it look like this on both ios and android:
Idea:
The idea is to use multiple shadows on the Text object. We can do it by wrapping Text component with View and clone the same Text object multiple times with different shadows to make them using different directions.
Implementation:
Here is the code for the wrapper component:
import * as React from "react";
import { StyleSheet, View } from "react-native";
import { Children, cloneElement, isValidElement } from "react";
type Props = {
children: any,
color: string,
stroke: number
}
const styles = StyleSheet.create({
outline: {
position: 'absolute'
},
});
export class TextStroke extends React.Component<Props> {
createClones = (w: number, h: number, color?: string) => {
const { children } = this.props;
return Children.map(children, child => {
if (isValidElement(child)) {
const currentProps = child.props as any;
const currentStyle = currentProps ? (currentProps.style || {}) : {};
const newProps = {
...currentProps,
style: {
...currentStyle,
textShadowOffset: {
width: w,
height: h
},
textShadowColor: color,
textShadowRadius: 1
}
}
return cloneElement(child, newProps)
}
return child;
});
}
render() {
const {color, stroke, children} = this.props;
const strokeW = stroke;
const top = this.createClones(0, -strokeW * 1.2, color);
const topLeft = this.createClones(-strokeW, -strokeW, color);
const topRight = this.createClones(strokeW, -strokeW, color);
const right = this.createClones(strokeW, 0, color);
const bottom = this.createClones(0, strokeW, color);
const bottomLeft = this.createClones(-strokeW, strokeW, color);
const bottomRight = this.createClones(strokeW, strokeW, color);
const left = this.createClones(-strokeW * 1.2, 0, color);
return (
<View>
<View style={ styles.outline }>{ left }</View>
<View style={ styles.outline }>{ right }</View>
<View style={ styles.outline }>{ bottom }</View>
<View style={ styles.outline }>{ top }</View>
<View style={ styles.outline }>{ topLeft }</View>
<View style={ styles.outline }>{ topRight }</View>
<View style={ styles.outline }>{ bottomLeft }</View>
{ bottomRight }
</View>
);
}
}
If the text is not big, you can also use only 4 directions instead of 8 to improve performance:
<View>
<View style={ styles.outline }>{ topLeft }</View>
<View style={ styles.outline }>{ topRight }</View>
<View style={ styles.outline }>{ bottomLeft }</View>
{ bottomRight }
</View>
The usage:
<TextStroke stroke={ 2 } color={ '#000000' }>
<Text style={ {
fontSize: 100,
color: '#FFFFFF'
} }> Sample </Text>
</TextStroke>
You can also use multiple Text objects inside since the wrapper copies all of them.
Performance:
I haven't checked the performance for this solution yet. Since we're copying text so many times it maybe not great.
Issues:
Need to be careful with the stroke
value. With higher values, the edges of the shadows will be visible. If you really need a wider stroke, you can fix this by adding more layers to cover different shadow directions.
Yes it is possible through the following properties:
textShadowColor color
textShadowOffset ReactPropTypes.shape( {width: ReactPropTypes.number, height: ReactPropTypes.number} )
textShadowRadius ReactPropTypes.number
https://facebook.github.io/react-native/docs/text.html
Actual completed pull request: https://github.com/facebook/react-native/pull/4975