React-CanvasKit
Experiment in creating a custom react renderer using an offscreen webgl canvas on top of Skia CanvasKit
Examples
Paragraph with dynamic font loading
import type { FunctionComponent } from 'react'
import React from 'react'
import { FontManagerProvider } from 'react-canvaskit'
import ParagraphDemo from './ParagraphDemo'
const robotoPromise = fetch('https://storage.googleapis.com/skia-cdn/google-web-fonts/Roboto-Regular.ttf')
.then((resp) => resp.arrayBuffer())
const notoColorEmojiPromise = fetch('https://storage.googleapis.com/skia-cdn/misc/NotoColorEmoji.ttf')
.then((resp) => resp.arrayBuffer())
const fontsPromise = Promise.all([robotoPromise, notoColorEmojiPromise])
export const App: FunctionComponent = () => {
const [fonts, setFonts] = React.useState<ArrayBuffer[] | undefined>(undefined)
fontsPromise.then(fetchedFonts => setFonts(fetchedFonts))
return (
<FontManagerProvider fontData={fonts}>
<ParagraphDemo/>
</FontManagerProvider>
)
}
import type { SkParagraph } from 'canvaskit-oc'
import React from 'react'
import type { SkObjectRef } from 'react-canvaskit'
import { PaintStyle, TextAlignEnum, useFontManager } from 'react-canvaskit'
import useAnimationFrame from './useAnimationFrame'
const fontPaint = { style: PaintStyle.Fill, antiAlias: true }
const X = 250
const Y = 250
const paragraphText = 'The quick brown fox ? ate a zesty hamburgerfonts ?.\nThe ???? laughed.'
export default () => {
const skParagraphRef = React.useRef<SkObjectRef<SkParagraph>>(null)
const fontManager = useFontManager()
const calcWrapTo = (time: number): number => 350 + 150 * Math.sin(time / 2000)
const [wrapTo, setWrapTo] = React.useState(calcWrapTo(performance.now()))
useAnimationFrame(time => setWrapTo(calcWrapTo(time)))
return (
<ck-canvas clear='#FFFFFF'>
<ck-paragraph
fontManager={fontManager}
ref={skParagraphRef}
textStyle={{
color: '#000000',
// Noto Mono is the default canvaskit font, we use it as a fallback
fontFamilies: ['Noto Mono', 'Roboto', 'Noto Color Emoji'],
fontSize: 50
}}
textAlign={TextAlignEnum.Left}
maxLines={7}
ellipsis='...'
layout={wrapTo}
>
{paragraphText}
</ck-paragraph>
<ck-line x1={wrapTo} y1={0} x2={wrapTo} y2={400} paint={fontPaint}/>
<ck-text x={5} y={450}
paint={fontPaint}>{`At (${X.toFixed(2)}, ${Y.toFixed(2)}) glyph is '${glyph}'`}</ck-text>
</ck-canvas>
)
}
Simple Paint
const App: FunctionComponent = () => {
return (
<ck-canvas clear={{ red: 255, green: 165, blue: 0 }}>
<ck-text x={5} y={50} paint={{ color: '#00FFFF', antiAlias: true }} font={{ size: 24 }}>
Hello React-CanvasKit!
</ck-text>
<ck-surface width={100} height={100} dx={100} dy={100}>
<ck-canvas clear='#FF00FF' rotate={{ degree: 45 }}>
<ck-text> React-CanvasKit.</ck-text>
<ck-line x1={0} y1={10} x2={142} y2={10}
paint={{ antiAlias: true, color: '#FFFFFF', strokeWidth: 10 }}/>
</ck-canvas>
</ck-surface>
</ck-canvas>
)
}
const htmlCanvasElement = document.createElement('canvas')
const rootElement = document.getElementById('root')
if (rootElement === null) {
throw new Error('No root element defined.')
}
rootElement.appendChild(htmlCanvasElement)
document.body.appendChild(htmlCanvasElement)
htmlCanvasElement.width = 400
htmlCanvasElement.height = 300
init().then(() => render(<App/>, htmlCanvasElement))