{"id":27553032,"url":"https://github.com/udevbe/react-canvaskit","last_synced_at":"2025-04-19T11:43:12.260Z","repository":{"id":45079702,"uuid":"254612447","full_name":"udevbe/react-canvaskit","owner":"udevbe","description":"Experiment in creating a custom react renderer using an offscreen webgl canvas on top of Skia CanvasKit","archived":false,"fork":false,"pushed_at":"2022-01-10T12:35:50.000Z","size":15272,"stargazers_count":121,"open_issues_count":1,"forks_count":13,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-18T13:04:25.289Z","etag":null,"topics":["accelerated","canvas","canvaskit","hardware-acceleration","html5","html5-canvas","react","react-reconciler","reactjs","skia","skia-library","webgl"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/udevbe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-10T10:56:22.000Z","updated_at":"2025-03-07T00:15:51.000Z","dependencies_parsed_at":"2022-08-26T09:41:28.217Z","dependency_job_id":null,"html_url":"https://github.com/udevbe/react-canvaskit","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udevbe%2Freact-canvaskit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udevbe%2Freact-canvaskit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udevbe%2Freact-canvaskit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udevbe%2Freact-canvaskit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/udevbe","download_url":"https://codeload.github.com/udevbe/react-canvaskit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249685436,"owners_count":21310602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accelerated","canvas","canvaskit","hardware-acceleration","html5","html5-canvas","react","react-reconciler","reactjs","skia","skia-library","webgl"],"created_at":"2025-04-19T11:43:11.791Z","updated_at":"2025-04-19T11:43:12.252Z","avatar_url":"https://github.com/udevbe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React-CanvasKit\n\n![npm](https://img.shields.io/npm/v/react-canvaskit)\n\nExperimental implementation of [Skia CanvasKit](https://skia.org/user/modules/canvaskit) using [ReactJS](https://reactjs.org/).\n\nThis implementation allows you to use all familiar React concepts like hooks and contexts, in conjunction with JXS elements that closely match the existing Skia CanvasKit API. Everything is drawn to a hardware accelerated WebGL canvas.\n\n# Examples\n#### Paragraph with dynamic font loading\n![Alt text](/demos/paragraph-demo/paragraph-demo.gif?raw=true \"Paragraph Demo\")\n\n```typescript jsx\nimport type { FunctionComponent } from 'react'\nimport React from 'react'\nimport { FontManagerProvider } from 'react-canvaskit'\nimport ParagraphDemo from './ParagraphDemo'\n\nconst robotoPromise = fetch('https://storage.googleapis.com/skia-cdn/google-web-fonts/Roboto-Regular.ttf')\n  .then((resp) =\u003e resp.arrayBuffer())\nconst notoColorEmojiPromise = fetch('https://storage.googleapis.com/skia-cdn/misc/NotoColorEmoji.ttf')\n  .then((resp) =\u003e resp.arrayBuffer())\n\nconst fontsPromise = Promise.all([robotoPromise, notoColorEmojiPromise])\n\nexport const App: FunctionComponent = () =\u003e {\n  const [fonts, setFonts] = React.useState\u003cArrayBuffer[] | undefined\u003e(undefined)\n  fontsPromise.then(fetchedFonts =\u003e setFonts(fetchedFonts))\n\n  return (\n    \u003cFontManagerProvider fontData={fonts}\u003e\n      \u003cParagraphDemo/\u003e\n    \u003c/FontManagerProvider\u003e\n  )\n}\n```\n\n```typescript jsx\nimport type { SkParagraph } from 'canvaskit-oc'\nimport React from 'react'\nimport type { SkObjectRef } from 'react-canvaskit'\nimport { PaintStyle, TextAlignEnum, useFontManager } from 'react-canvaskit'\nimport useAnimationFrame from './useAnimationFrame'\n\nconst fontPaint = { style: PaintStyle.Fill, antiAlias: true }\n\nconst X = 250\nconst Y = 250\nconst paragraphText = 'The quick brown fox 🦊 ate a zesty hamburgerfonts 🍔.\\nThe 👩‍👩‍👧‍👧 laughed.'\n\nexport default () =\u003e {\n  const skParagraphRef = React.useRef\u003cSkObjectRef\u003cSkParagraph\u003e\u003e(null)\n  const fontManager = useFontManager()\n\n  const calcWrapTo = (time: number): number =\u003e 350 + 150 * Math.sin(time / 2000)\n  const [wrapTo, setWrapTo] = React.useState(calcWrapTo(performance.now()))\n\n  useAnimationFrame(time =\u003e setWrapTo(calcWrapTo(time)))\n\n  return (\n    \u003cck-canvas clear='#FFFFFF'\u003e\n      \u003cck-paragraph\n        fontManager={fontManager}\n        ref={skParagraphRef}\n        textStyle={{\n          color: '#000000',\n          // Noto Mono is the default canvaskit font, we use it as a fallback\n          fontFamilies: ['Noto Mono', 'Roboto', 'Noto Color Emoji'],\n          fontSize: 50\n        }}\n        textAlign={TextAlignEnum.Left}\n        maxLines={7}\n        ellipsis='...'\n        layout={wrapTo}\n      \u003e\n        {paragraphText}\n      \u003c/ck-paragraph\u003e\n      \u003cck-line x1={wrapTo} y1={0} x2={wrapTo} y2={400} paint={fontPaint}/\u003e\n      \u003cck-text x={5} y={450}\n               paint={fontPaint}\u003e{`At (${X.toFixed(2)}, ${Y.toFixed(2)}) glyph is '${glyph}'`}\u003c/ck-text\u003e\n    \u003c/ck-canvas\u003e\n  )\n}\n```\n\n#### Simple Paint \n![Alt text](/demos/simple-paint/hello-react-canvaskit.png?raw=true \"Hello React-CanvasKit!\")\n\n```typescript jsx\nconst App: FunctionComponent = () =\u003e {\n  return (\n    \u003cck-canvas clear={{ red: 255, green: 165, blue: 0 }}\u003e\n      \u003cck-text x={5} y={50} paint={{ color: '#00FFFF', antiAlias: true }} font={{ size: 24 }}\u003e\n        Hello React-CanvasKit!\n      \u003c/ck-text\u003e\n      \u003cck-surface width={100} height={100} dx={100} dy={100}\u003e\n        \u003cck-canvas clear='#FF00FF' rotate={{ degree: 45 }}\u003e\n          \u003cck-text\u003e React-CanvasKit.\u003c/ck-text\u003e\n          \u003cck-line x1={0} y1={10} x2={142} y2={10} \n            paint={{ antiAlias: true, color: '#FFFFFF', strokeWidth: 10 }}/\u003e\n        \u003c/ck-canvas\u003e\n      \u003c/ck-surface\u003e\n    \u003c/ck-canvas\u003e\n  )\n}\n\nconst htmlCanvasElement = document.createElement('canvas')\nconst rootElement = document.getElementById('root')\nif (rootElement === null) {\n  throw new Error('No root element defined.')\n}\nrootElement.appendChild(htmlCanvasElement)\ndocument.body.appendChild(htmlCanvasElement)\nhtmlCanvasElement.width = 400\nhtmlCanvasElement.height = 300\n\ninit().then(() =\u003e render(\u003cApp/\u003e, htmlCanvasElement))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fudevbe%2Freact-canvaskit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fudevbe%2Freact-canvaskit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fudevbe%2Freact-canvaskit/lists"}