diff --git a/package.json b/package.json index 911990e..d29c0ad 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@expo/metro-runtime": "^3.1.2", "@shopify/react-native-skia": "^0.1.240", "@types/react": "~18.2.45", - "canvaskit-js": "https://bit.ly/canvaskit-js_v210", + "canvaskit-js": "2.1.4", "clsx": "^2.1.0", "expo": "~50.0.4", "expo-constants": "~15.4.5", diff --git a/src/components/NoteRoll.tsx b/src/components/NoteRoll.tsx index 7310769..bc3331a 100644 --- a/src/components/NoteRoll.tsx +++ b/src/components/NoteRoll.tsx @@ -1,18 +1,18 @@ import { - Group, Paint, Rect, RoundedRect, + Group, multiply4, processTransform3d, translate, } from '@shopify/react-native-skia'; import { Easing, SharedValue, useDerivedValue, useSharedValue, withDelay, withTiming, } from 'react-native-reanimated'; -import { memo, useEffect } from 'react'; +import React, { memo, useEffect } from 'react'; import colors from 'tailwindcss/colors'; import { gameWidth, gameHeight, pianoKeyboardHeight, countdownBars, getDistFromBars, getTimeFromBars, keyWidth, keyNoteColors, accidentalNoteColors, isGamePlaying, getDurationInBars, } from '../utils/utils'; import { KeysState } from '../hooks/useKeyboard'; import { accidentalNames, keyNames, noteToKeyboardKey } from './PianoKeyboard'; -import { PlayMode } from './PlayingUI'; import { SongData } from '@/utils/songs'; +import { Plane } from './Plane'; const noteStrokeWidth = 8; @@ -20,27 +20,30 @@ const NoteRoll = ({ keysState, songData, noteRollY, -}:{ +}: { keysState: KeysState, songData: SongData noteRollY: SharedValue, }) => { - const noteRollTransform = useDerivedValue(() => [{ translateY: noteRollY?.value }]); const perspectiveRollIn = useSharedValue(0); - const threeDRollTransform = useDerivedValue(() => [ - // Go to the top of the piano keys, horizontally centered - { translateX: gameWidth / 2 }, - { translateY: gameHeight - pianoKeyboardHeight }, - - // Apply the perspective effect - { perspective: perspectiveRollIn.value }, - { rotateX: Math.PI / 10 }, - - // Go back to the top of the screen - { translateX: -gameWidth / 2 }, - { translateY: -gameHeight + pianoKeyboardHeight }, - ]); + const matrix = useDerivedValue(() => { + return processTransform3d([ + { translateX: gameWidth / 2 }, + { translateY: gameHeight - pianoKeyboardHeight }, + + // Apply the perspective effect + { perspective: perspectiveRollIn.value }, + { rotateX: Math.PI / 10 }, + + // Go back to the top of the screen + { translateX: -gameWidth / 2 }, + { translateY: -gameHeight + pianoKeyboardHeight }, + ]); + }); + const translatedMatrix = useDerivedValue(() => { + return multiply4(matrix.value, translate(0, noteRollY.value)); + }); useEffect(() => { perspectiveRollIn.value = 0; @@ -54,9 +57,9 @@ const NoteRoll = ({ }; }, [songData.name]); - return + return {/* Create a line at the center of each piano key black key and a colored bg if need be ! */} - { keysState && [...Array(11)].map((_, i) => { + {keysState && [...Array(11)].map((_, i) => { const xPos = i * (keyWidth); const keyPressed = keysState[keyNames[i]]; @@ -70,17 +73,17 @@ const NoteRoll = ({ return {/* BG */} - { (i < 10) && } + {(i < 10) && } {/* Lines */} - + ; - }) } + })} {/* Draw the notes for each key (black & white 🎹) */} - - { songData?.notes?.map((note, i) => { + + {songData?.notes?.map((note, i) => { if (note.noteName) { const yOfKeyboardHeight = gameHeight - pianoKeyboardHeight; @@ -89,7 +92,7 @@ const NoteRoll = ({ const noteIndex = keyNames.indexOf(keyboardKey); const noteAccidentalIndex = accidentalNames.indexOf(keyboardKey); - const roundedRectParams:{ + const roundedRectParams: { xPos?: number, yPos?: number, width?: number, @@ -111,22 +114,33 @@ const NoteRoll = ({ return <>; } - return - - - ; + return ( + + + + + ); } // ELSE: no noteName, so it's a rest return <>; - }) } + })} ; }; diff --git a/src/components/PianoKeyboard.tsx b/src/components/PianoKeyboard.tsx index fc59126..41ba9e5 100644 --- a/src/components/PianoKeyboard.tsx +++ b/src/components/PianoKeyboard.tsx @@ -41,7 +41,7 @@ const PianoKeyboard = ({ disableAnimation, showNoteNames, hideSideBackground, -}:{ +}: { keysState: KeysState, songName: string, disableAnimation?: boolean, @@ -72,10 +72,10 @@ const PianoKeyboard = ({ transform={scrollInTransform} > {/* BG */} - { !hideSideBackground && } + {!hideSideBackground && } {/* Draw 11 White keys using a loop */} - { keysState && [...Array(numberOfWhiteKeys)].map((_, i) => { + {keysState && [...Array(numberOfWhiteKeys)].map((_, i) => { const xPos = i * (gameWidth / numberOfWhiteKeys); const yPos = gameHeight - pianoKeyboardHeight; const keyName = keyNames[i]; @@ -89,36 +89,43 @@ const PianoKeyboard = ({ return {/* White Key */} + + + - + {/* */} + {/* Accidental (if there's one) */} - { hasAnAccidentalBefore && - - - } + {hasAnAccidentalBefore && <> + + + + + + } {/* Draw the note names (white & black keys! 🎹) */} - { showNoteNames && (screenWidth > 600) && - { hasAnAccidentalBefore && } + {showNoteNames && (screenWidth > 600) && + {hasAnAccidentalBefore && } } ; - }) } + })} ); }; -export default memo(PianoKeyboard); +export default PianoKeyboard; diff --git a/src/components/Plane.tsx b/src/components/Plane.tsx new file mode 100644 index 0000000..5811f8e --- /dev/null +++ b/src/components/Plane.tsx @@ -0,0 +1,36 @@ +import { Matrix4, PaintProps, Path, SkRect, Skia, TransformProps, Transforms3d, mapPoint3d, processTransform, processTransform3d } from "@shopify/react-native-skia"; +import { SharedValue, useDerivedValue } from "react-native-reanimated"; + +const addRect = (p: Skia.Path, rect: SkRect) => { + +}; + +interface PlaneProps extends PaintProps { + x: number; + y: number; + width: number; + height: number; + matrix: SharedValue; + r?: number; +} + +export const Plane = ({ x, y, width, height, matrix, r = 0, color, opacity, ...props }: PlaneProps) => { + const path = useDerivedValue(() => { + const p = Skia.Path.Make(); + const rct = Skia.XYWHRect(x, y, width, height); + if (r > 0) { + p.addRRect(Skia.RRectXY(rct, r, r)); + } else { + p.addRect(rct); + } + p.transform(matrix.value) + return p; + }); + const cl = Skia.Color(color); + cl[3] = opacity; + return ( + <> + + + ); +}; \ No newline at end of file diff --git a/src/components/PlayingUI.tsx b/src/components/PlayingUI.tsx index 13bd046..5773982 100644 --- a/src/components/PlayingUI.tsx +++ b/src/components/PlayingUI.tsx @@ -212,10 +212,9 @@ const PlayingUI = ({ { translateX: (screenWidth - gameWidth) / 2 }, { translateY: (screenHeight - gameHeight) / 2 }, ]}> - + + diff --git a/src/index.web.ts b/src/index.web.ts index c48cce9..cdd4078 100644 --- a/src/index.web.ts +++ b/src/index.web.ts @@ -6,17 +6,17 @@ import { App } from 'expo-router/build/qualified-entry'; import { renderRootComponent } from 'expo-router/build/renderRootComponent'; // RN Skia (WASM) -import { LoadSkiaWeb } from '@shopify/react-native-skia/lib/module/web'; +// import { LoadSkiaWeb } from '@shopify/react-native-skia/lib/module/web'; -LoadSkiaWeb({ - locateFile: (file) => `/static/js/${file}`, -}).then(async () => { - renderRootComponent(App); -}); +// LoadSkiaWeb({ +// locateFile: (file) => `/static/js/${file}`, +// }).then(async () => { +// renderRootComponent(App); +// }); // RN Skia (CanvasKitJS) -// import { CanvasKitJS } from 'canvaskit-js'; -// (async () => { -// global.CanvasKit = CanvasKitJS.getInstance(); -// renderRootComponent(App); -// })(); +import { CanvasKitJS } from 'canvaskit-js'; +(async () => { + global.CanvasKit = CanvasKitJS.getInstance(); + renderRootComponent(App); +})(); diff --git a/yarn.lock b/yarn.lock index e809036..24b05b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2262,10 +2262,10 @@ component-type "^1.2.1" join-component "^1.1.0" -"@shopify/react-native-skia@^0.1.237": - version "0.1.240" - resolved "https://registry.yarnpkg.com/@shopify/react-native-skia/-/react-native-skia-0.1.240.tgz#4bd88defc277175eadb52465f070e2df67909483" - integrity sha512-KcmmGiC4eHaVR1Erx5LCR6OUflLW9YN7fEuf3FOt0j7hvMbX3QZDXfqRP0D17jjh4W+BO26+YM4vxm6V7+htkQ== +"@shopify/react-native-skia@^0.1.240": + version "0.1.241" + resolved "https://registry.yarnpkg.com/@shopify/react-native-skia/-/react-native-skia-0.1.241.tgz#592471a565f3a7792e03a453eaf922518dd994e5" + integrity sha512-jQFJHiNUVONTGPPnL7LIJc3kg6x4PBPh3m7NAJuTxuhB8VOSVlQR3UWd4tDHHJgj7otG3S2dbHD7+bftxM5cFA== dependencies: canvaskit-wasm "0.39.1" react-reconciler "0.27.0" @@ -2968,9 +2968,10 @@ caniuse-lite@^1.0.30001580: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz#0dfd4db9e94edbdca67d57348ebc070dece279f4" integrity sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ== -"canvaskit-js@https://bit.ly/canvaskit-js_v210": - version "2.1.0" - resolved "https://bit.ly/canvaskit-js_v210#fbc8b5eba27e69ec725fefc8ac88cab289d3c469" +canvaskit-js@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/canvaskit-js/-/canvaskit-js-2.1.4.tgz#ad515cb2d59b47f4c69c306b6612271625ee0672" + integrity sha512-FbeP+mrm5flCQToQckgT2ZJkw5CEieiyHnZ9TRPcXR16nmcH1bYk7mK+LysIcKgONwIY0jsgnoxYxRYIE6N2Wg== canvaskit-wasm@0.39.1: version "0.39.1"