Skip to content

Commit

Permalink
Add SkyBackground with skia
Browse files Browse the repository at this point in the history
  • Loading branch information
martinezguillaume committed May 5, 2024
1 parent 274747b commit 9fcfa94
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 6 deletions.
4 changes: 4 additions & 0 deletions app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ColorModeFab,
Background,
ListHeader,
SkyBackground,
} from '@/components'
import {data} from '@/data'
import {i18n} from '@/i18n'
Expand All @@ -34,8 +35,11 @@ export default function Home(): ReactElement {
return (
<>
<Background />
<SkyBackground />

<View className="flex-1 max-w-screen-sm web:sm:self-center sm:border-x border-divider">
<Background className="hidden sm:flex" />

<Header scrollY={scrollY} />

<TabView
Expand Down
Binary file modified bun.lockb
Binary file not shown.
5 changes: 1 addition & 4 deletions components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,7 @@ export const Header = memo<HeaderProps>(({scrollY}) => {
})

return (
<Animated.View
pointerEvents="none"
className="absolute left-0 right-0"
style={headerStyle}>
<Animated.View className="absolute left-0 right-0" style={headerStyle}>
<Image
className="flex-1 !w-full"
alt="cover"
Expand Down
89 changes: 89 additions & 0 deletions components/SkyBackground/SkyBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {memo, useEffect} from 'react'
import {Canvas, RoundedRect} from '@shopify/react-native-skia'
import {StyleSheet, useWindowDimensions} from 'react-native'
import {
useSharedValue,
withDelay,
withRepeat,
withTiming,
} from 'react-native-reanimated'

type SkyBackgroundProps = {}

const getRandomInt = (min: number, max: number) =>
Math.random() * (max - min) + min

export const SkyBackground = memo(({}: SkyBackgroundProps) => {
return (
<Canvas style={StyleSheet.absoluteFill}>
<Stars density={400} size={1} />
<Stars density={2000} size={2} />
</Canvas>
)
})

const Stars = memo(
({
size,
density,
animated,
}: {
size: number
density: number
animated?: boolean
}) => {
const {width, height} = useWindowDimensions()
const length = Math.round((width * height) / density)

return Array(length)
.fill(null)
.map((_, i) => (
<Star
key={i}
x={getRandomInt(-5, width + 5)}
y={getRandomInt(-5, height + 5)}
size={size}
animated={animated}
/>
))
},
)

const Star = memo(
({
size,
x,
y,
animated,
}: {
x: number
y: number
size: number
animated?: boolean
}) => {
const opacity = useSharedValue(0)

useEffect(() => {
if (animated) {
opacity.value = withDelay(
getRandomInt(0, 3000),
withRepeat(withTiming(1, {duration: 2000}), -1, true),
)
}
}, [animated, opacity])

return (
<RoundedRect
x={x}
y={y}
width={size}
height={size}
r={size / 2}
opacity={animated ? opacity : getRandomInt(0.1, 1)}
color="white"
/>
)
},
)

export default SkyBackground
1 change: 1 addition & 0 deletions components/SkyBackground/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SkyBackground'
8 changes: 8 additions & 0 deletions components/SkyBackground/index.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {WithSkiaWeb} from '@shopify/react-native-skia/lib/module/web'

export const SkyBackground = () => (
<WithSkiaWeb
// @ts-ignore
getComponent={() => import('./SkyBackground')}
/>
)
1 change: 1 addition & 0 deletions components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './LocaleFab'
export * from './ColorModeFab'
export * from './Background'
export * from './ListHeader'
export * from './SkyBackground'
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
"test": "tsc && bun run lint",
"lint": "eslint . --ext .ts,.tsx --max-warnings 0",
"predeploy": "expo export -p web",
"deploy": "gh-pages -t -d dist"
"deploy": "gh-pages -t -d dist",
"postinstall": "patch-package && setup-skia-web"
},
"dependencies": {
"@expo/vector-icons": "^14.0.0",
"@react-native-async-storage/async-storage": "1.21.0",
"@react-navigation/native": "^6.1.9",
"@shopify/react-native-skia": "0.1.221",
"ajv": "^8.12.0",
"dayjs": "^1.10.8",
"expo": "~50.0.17",
Expand All @@ -25,11 +27,12 @@
"expo-font": "~11.10.3",
"expo-linking": "~6.2.2",
"expo-localization": "~14.8.4",
"expo-router": "~3.4.9",
"expo-router": "~3.4.10",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "~2.9.4",
"i18n-js": "^4.3.0",
"nativewind": "^4.0.1",
"patch-package": "^8.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.73.6",
Expand Down
122 changes: 122 additions & 0 deletions patches/@shopify+react-native-skia+0.1.221.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
diff --git a/node_modules/@shopify/react-native-skia/lib/module/Platform/Platform.web.js b/node_modules/@shopify/react-native-skia/lib/module/Platform/Platform.web.js
index 95b7ee2..be77fcc 100644
--- a/node_modules/@shopify/react-native-skia/lib/module/Platform/Platform.web.js
+++ b/node_modules/@shopify/react-native-skia/lib/module/Platform/Platform.web.js
@@ -123,9 +123,17 @@ const View = _ref => {

export const Platform = {
OS: "web",
- PixelRatio: window.devicePixelRatio,
+ PixelRatio: typeof window !== "undefined" ? window.devicePixelRatio : 1,
resolveAsset: source => {
if (isRNModule(source)) {
+ if (typeof source === "number" && typeof require === "function") {
+ const {
+ getAssetByID,
+ } = require("react-native/Libraries/Image/AssetRegistry");
+ const { httpServerLocation, name, type } = getAssetByID(source);
+ const uri = `${httpServerLocation}/${name}.${type}`;
+ return uri;
+ }
throw new Error("Image source is a number - this is not supported on the web");
}

diff --git a/node_modules/@shopify/react-native-skia/scripts/setup-canvaskit.js b/node_modules/@shopify/react-native-skia/scripts/setup-canvaskit.js
index c151639..c25ff97 100755
--- a/node_modules/@shopify/react-native-skia/scripts/setup-canvaskit.js
+++ b/node_modules/@shopify/react-native-skia/scripts/setup-canvaskit.js
@@ -7,6 +7,7 @@
* In `@expo/webpack-config` this is `./web` (default for now).
*
* This script does the following:
+ * 0. Try to detect if it's an expo project and if the bundler is set to metro
* 1. Resolve the public path relative to wherever the script is being run.
* 2. Log out some useful info about the web setup, just in case anything goes wrong.
* 3. Resolve the installed wasm file `canvaskit-wasm/bin/full/canvaskit.wasm`
@@ -15,14 +16,18 @@
*
*
* Usage:
- * $ `npx <script> web`
+ * $ `npx <script>`
*
+ * On webpack:
* -> Copies the file to `<project>/web/static/js/canvaskit.wasm`
+ * on metro:
+ * -> Copies the file to `<project>/public/canvaskit.wasm`
*
- * Tooling that uses `/public`:
- * $ `npx <script> public`
+ * Tooling that uses a custom static assets folder, like `/assets` for example:
+ * $ `npx <script> assets`
+ *
+ * -> Copies the file to `<project>/assets/canvaskit.wasm`
*
- * -> Copies the file to `<project>/public/static/js/canvaskit.wasm`
*/
const fs = require("fs");
const path = require("path");
@@ -32,6 +37,29 @@ const args = process.argv.slice(2);
const gray = (text) => `\x1b[90m${text}\x1b[0m`;
const lime = (text) => `\x1b[32m${text}\x1b[0m`;

+function getWetherItsAnExpoProjectWithMetro() {
+ try {
+ const appJsonPath = path.join(process.cwd(), 'app.json');
+
+ console.log(
+ `› Reading Expo settings from (if any):\n ${gray(appJsonPath)}`
+ );
+
+ const appJson = require(appJsonPath);
+ const isAnExpoProjectWithMetro = appJson.expo && appJson.expo.web && appJson.expo.web.bundler === 'metro';
+ if (isAnExpoProjectWithMetro) {
+ console.log(` ${gray(`Expo project with metro bundler detected`)}\n`);
+ return true;
+ } else {
+ console.log(` ${gray(`Metro bundler not detected. Assuming the project is using Webpack.`)}\n`);
+ return false;
+ }
+ } catch (error) {
+ console.log(` ${gray(`No Expo settings found`)}\n`);
+ return false;
+ }
+}
+
function getWasmFilePath() {
try {
return require.resolve("canvaskit-wasm/bin/full/canvaskit.wasm");
@@ -43,14 +71,14 @@ function getWasmFilePath() {
}
}

-function getOutputFilePath() {
+function getOutputFilePath(isAnExpoProjectWithMetro) {
// Default to using `web` public path.
- const publicFolder = path.resolve(args[0] || "web");
- const publicLocation = "./static/js/canvaskit.wasm";
+ const publicFolder = path.resolve(args[0] || (isAnExpoProjectWithMetro) ? "public" : "web/static/js");
+ const publicLocation = "./canvaskit.wasm";
const output = path.join(publicFolder, publicLocation);

console.log(
- `› Copying 'canvaskit.wasm' file to static folder:\n ${gray(output)}\n`
+ `› Copying 'canvaskit.wasm' file to public folder:\n ${gray(output)}\n`
);
return output;
}
@@ -61,9 +89,12 @@ function copyFile(from, to) {
fs.writeFileSync(to, data);
}

-// Copy the WASM file to `<static>/static/js/canvaskit.wasm`
(() => {
- copyFile(getWasmFilePath(), getOutputFilePath());
+ // Automatically detect if it's an expo project with a metro bundler
+ const isAnExpoProjectWithMetro = getWetherItsAnExpoProjectWithMetro();
+
+ // Copy the WASM file to `<static>/canvaskit.wasm`
+ copyFile(getWasmFilePath(), getOutputFilePath(isAnExpoProjectWithMetro));

console.log(lime("› Success! You are almost there:"));
console.log(
Binary file added public/canvaskit.wasm
Binary file not shown.

0 comments on commit 9fcfa94

Please sign in to comment.