-
Notifications
You must be signed in to change notification settings - Fork 460
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
--------- Co-authored-by: William Candillon <[email protected]>
- Loading branch information
1 parent
1af462c
commit ac40c2b
Showing
91 changed files
with
2,531 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
--- | ||
id: paragraph | ||
title: Paragraph | ||
sidebar_label: Paragraph | ||
slug: /text/paragraph | ||
--- | ||
|
||
React Native Skia offers an API to perform text layouts. | ||
Behind the scene, this API is the Skia Paragraph API. | ||
|
||
|
||
## Hello World | ||
|
||
In the example below, we create a simple paragraph based on custom fonts. | ||
The emojis will be renderer using the emoji font available on the platform. | ||
Other system fonts will are available as well. | ||
|
||
```tsx twoslash | ||
import { useMemo } from "react"; | ||
import { Paragraph, Skia, useFonts } from "@shopify/react-native-skia"; | ||
|
||
const MyParagraph = () => { | ||
const customFontMgr = useFonts({ | ||
Roboto: [ | ||
require("path/to/Roboto-Regular.ttf"), | ||
require("path/to/Roboto-Medium.ttf") | ||
] | ||
}); | ||
|
||
const paragraph = useMemo(() => { | ||
// Are the font loaded already? | ||
if (!customFontMgr) { | ||
return null; | ||
} | ||
const textStyle = { | ||
color: Skia.Color("black"), | ||
fontFamilies: ["Roboto"], | ||
fontSize: 50, | ||
}; | ||
return Skia.ParagraphBuilder.Make({}, customFontMgr) | ||
.pushStyle(textStyle) | ||
.addText("Say Hello to ") | ||
.pushStyle({ ...textStyle, fontStyle: { weight: 500 } }) | ||
.addText("Skia 🎨") | ||
.pop() | ||
.build(); | ||
}, [customFontMgr]); | ||
|
||
// Render the paragraph | ||
return <Paragraph paragraph={paragraph} x={0} y={0} width={300} />; | ||
}; | ||
``` | ||
|
||
Below is the result on Android (left) and iOS (right). | ||
<img src={require("/static/img/paragraph/hello-world-android.png").default} width="256" height="256" /> | ||
<img src={require("/static/img/paragraph/hello-world-ios.png").default} width="256" height="256" /> | ||
|
||
On Web, you will need to provide you own emoji font ([NotoColorEmoji](https://fonts.google.com/noto/specimen/Noto+Color+Emoji) for instance) and add it to the list of font families. | ||
|
||
```tsx twoslash | ||
import { useFonts, Skia } from "@shopify/react-native-skia"; | ||
|
||
const customFontMgr = useFonts({ | ||
Roboto: [ | ||
require("path/to/Roboto-Regular.ttf"), | ||
require("path/to/Roboto-Medium.ttf") | ||
], | ||
// Only load the emoji font on Web | ||
Noto: [ | ||
require("path/to/NotoColorEmoji.ttf") | ||
] | ||
}); | ||
|
||
// We add Noto to the list of font families | ||
const textStyle = { | ||
color: Skia.Color("black"), | ||
fontFamilies: ["Roboto", "Noto"], | ||
fontSize: 50, | ||
}; | ||
``` | ||
|
||
## Styling Paragraphs | ||
|
||
These properties define the overall layout and behavior of a paragraph. | ||
|
||
| Property | Description | | ||
|-------------------------|---------------------------------------------------------------------------------------| | ||
| `disableHinting` | Controls whether text hinting is disabled. | | ||
| `ellipsis` | Specifies the text to use for ellipsis when text overflows. | | ||
| `heightMultiplier` | Sets the line height as a multiplier of the font size. | | ||
| `maxLines` | Maximum number of lines for the paragraph. | | ||
| `replaceTabCharacters` | Determines whether tab characters should be replaced with spaces. | | ||
| `strutStyle` | Defines the strut style, which affects the minimum height of a line. | | ||
| `textAlign` | Sets the alignment of text (left, right, center, justify, start, end). | | ||
| `textDirection` | Determines the text direction (RTL or LTR). | | ||
| `textHeightBehavior` | Controls the behavior of text ascent and descent in the first and last lines. | | ||
| `textStyle` | Default text style for the paragraph (can be overridden by individual text styles). | | ||
|
||
### Text Style Properties | ||
|
||
These properties are used to style specific segments of text within a paragraph. | ||
|
||
| Property | Description | | ||
|-----------------------|-------------------------------------------------------------------------------------| | ||
| `backgroundColor` | Background color of the text. | | ||
| `color` | Color of the text. | | ||
| `decoration` | Type of text decoration (underline, overline, line-through). | | ||
| `decorationColor` | Color of the text decoration. | | ||
| `decorationThickness` | Thickness of the text decoration. | | ||
| `decorationStyle` | Style of the text decoration (solid, double, dotted, dashed, wavy). | | ||
| `fontFamilies` | List of font families for the text. | | ||
| `fontFeatures` | List of font features. | | ||
| `fontSize` | Font size of the text. | | ||
| `fontStyle` | Font style (weight, width, slant). | | ||
| `fontVariations` | Font variations. | | ||
| `foregroundColor` | Foreground color (for effects like gradients). | | ||
| `heightMultiplier` | Multiplier for line height. | | ||
| `halfLeading` | Controls half-leading value. | | ||
| `letterSpacing` | Space between characters. | | ||
| `locale` | Locale for the text (affects things like sorting). | | ||
| `shadows` | List of text shadows. | | ||
| `textBaseline` | Baseline for the text (alphabetic, ideographic). | | ||
| `wordSpacing` | Space between words. | | ||
|
||
These tables offer a quick reference to differentiate between paragraph and text styles in React Native Skia. You can use them to guide developers on how to apply various styles to create visually appealing and functional text layouts. | ||
Below is an example using different font styling: | ||
|
||
```tsx twoslash | ||
import { useMemo } from "react"; | ||
import { Paragraph, Skia, useFonts, FontStyle } from "@shopify/react-native-skia"; | ||
|
||
const MyParagraph = () => { | ||
const customFontMgr = useFonts({ | ||
Roboto: [ | ||
require("path/to/Roboto-Italic.ttf"), | ||
require("path/to/Roboto-Regular.ttf"), | ||
require("path/to/Roboto-Bold.ttf") | ||
], | ||
}); | ||
|
||
const paragraph = useMemo(() => { | ||
// Are the custom fonts loaded? | ||
if (!customFontMgr) { | ||
return null; | ||
} | ||
const textStyle = { | ||
fontSize: 24, | ||
fontFamilies: ["Roboto"], | ||
color: Skia.Color("#000"), | ||
}; | ||
|
||
const paragraphBuilder = Skia.ParagraphBuilder.Make({}, customFontMgr); | ||
paragraphBuilder | ||
.pushStyle({ ...textStyle, fontStyle: FontStyle.Bold }) | ||
.addText("This text is bold\n") | ||
.pop() | ||
.pushStyle({ ...textStyle, fontStyle: FontStyle.Normal }) | ||
.addText("This text is regular\n") | ||
.pop() | ||
.pushStyle({ ...textStyle, fontStyle: FontStyle.Italic }) | ||
.addText("This text is italic") | ||
.pop() | ||
.build(); | ||
return paragraphBuilder.build(); | ||
}, [customFontMgr]); | ||
|
||
return <Paragraph paragraph={paragraph} x={0} y={0} width={300} />; | ||
}; | ||
``` | ||
|
||
#### Result | ||
|
||
<img src={require("/static/img/paragraph/font-style-node.png").default} width="256" height="256" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import React, { useEffect, useMemo } from "react"; | ||
import { Platform, ScrollView, useWindowDimensions } from "react-native"; | ||
import type { DataModule, SkTextStyle } from "@shopify/react-native-skia"; | ||
import { | ||
Canvas, | ||
FontSlant, | ||
FontWeight, | ||
Group, | ||
PaintStyle, | ||
Paragraph, | ||
Rect, | ||
Skia, | ||
TextDecoration, | ||
mix, | ||
useFonts, | ||
} from "@shopify/react-native-skia"; | ||
import { | ||
useSharedValue, | ||
useDerivedValue, | ||
withRepeat, | ||
withTiming, | ||
} from "react-native-reanimated"; | ||
|
||
const fonts: Record<string, DataModule[]> = { | ||
Roboto: [ | ||
require("../../Tests/assets/Roboto-Medium.ttf"), | ||
require("../../Tests/assets/Roboto-Regular.ttf"), | ||
], | ||
}; | ||
// On Web, we need provide a font for emojis | ||
if (Platform.OS === "web") { | ||
fonts.NotoColorEmoji = [require("../../Tests/assets/NotoColorEmoji.ttf")]; | ||
} | ||
|
||
export const Paragraphs = () => { | ||
const { height, width } = useWindowDimensions(); | ||
const progress = useSharedValue(1); | ||
|
||
useEffect(() => { | ||
progress.value = withRepeat(withTiming(0, { duration: 3000 }), -1, true); | ||
}, [progress]); | ||
|
||
const loopedWidth = useDerivedValue( | ||
() => mix(progress.value, width * 0.2, width * 0.8), | ||
[progress] | ||
); | ||
|
||
const customFontMgr = useFonts(fonts); | ||
|
||
const paragraph = useMemo(() => { | ||
if (customFontMgr === null) { | ||
return null; | ||
} | ||
const fontSize = 20; | ||
const paragraphBuilder = Skia.ParagraphBuilder.Make({}, customFontMgr); | ||
const strokePaint = Skia.Paint(); | ||
strokePaint.setStyle(PaintStyle.Stroke); | ||
strokePaint.setStrokeWidth(1); | ||
|
||
const textStyle = { | ||
fontSize, | ||
fontFamilies: ["Roboto", "NotoColorEmoji"], | ||
color: Skia.Color("#000"), | ||
}; | ||
|
||
const coloredTextStyle = { | ||
fontSize: fontSize * 1.3, | ||
fontFamilies: ["Roboto"], | ||
fontStyle: { | ||
weight: FontWeight.Medium, | ||
}, | ||
color: Skia.Color("#61bea2"), | ||
}; | ||
|
||
const crazyStyle: SkTextStyle = { | ||
color: Skia.Color("#000"), | ||
backgroundColor: Skia.Color("#CECECE"), | ||
fontSize: fontSize * 1.3, | ||
fontFamilies: ["Roboto"], | ||
letterSpacing: -1, | ||
wordSpacing: 20, | ||
fontStyle: { | ||
slant: FontSlant.Italic, | ||
weight: FontWeight.ExtraBlack, | ||
}, | ||
shadows: [ | ||
{ | ||
color: Skia.Color("#00000044"), | ||
blurRadius: 4, | ||
offset: { x: 4, y: 4 }, | ||
}, | ||
], | ||
decorationColor: Skia.Color("#00223A"), | ||
decorationThickness: 2, | ||
decoration: 1, | ||
decorationStyle: TextDecoration.Overline, | ||
}; | ||
|
||
paragraphBuilder | ||
.pushStyle(textStyle) | ||
.addText("Hello ") | ||
.pushStyle({ | ||
...textStyle, | ||
fontStyle: { | ||
weight: FontWeight.Medium, | ||
}, | ||
}) | ||
.addText("Skia") | ||
.pop() | ||
.addText(" 🙋🏼♂️\n\nThis text rendered using the ") | ||
.pushStyle(coloredTextStyle) | ||
.addText("SkParagraph ") | ||
.pop() | ||
.addText("module with "); | ||
|
||
const altColoredTextStyle = { | ||
...coloredTextStyle, | ||
color: Skia.Color("#f5a623"), | ||
}; | ||
|
||
const retVal = paragraphBuilder | ||
.pushStyle(altColoredTextStyle) | ||
.addText("libgrapheme ") | ||
.pop() | ||
.addText("on iOS.") | ||
.pushStyle(textStyle) | ||
.addText( | ||
"\n\nOn Android we use built-in ICU while on web we use CanvasKit's." | ||
) | ||
.pop() | ||
.pushStyle(crazyStyle, strokePaint) | ||
.addText("\n\nWow - this is cool.") | ||
.pop() | ||
.build(); | ||
|
||
return retVal; | ||
}, [customFontMgr]); | ||
|
||
return ( | ||
<ScrollView> | ||
<Canvas | ||
style={{ | ||
width, | ||
height, | ||
}} | ||
> | ||
<Group transform={[{ translateX: 30 }, { translateY: 30 }]}> | ||
<Paragraph paragraph={paragraph} x={0} y={0} width={loopedWidth} /> | ||
<Rect x={loopedWidth} y={0} width={1} height={300} /> | ||
</Group> | ||
</Canvas> | ||
</ScrollView> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.