diff --git a/docs/user-manual/playcanvas-react/building-a-scene.md b/docs/user-manual/playcanvas-react/building-a-scene.md new file mode 100644 index 00000000000..354c1de85d7 --- /dev/null +++ b/docs/user-manual/playcanvas-react/building-a-scene.md @@ -0,0 +1,173 @@ +--- +title: Building a Scene +--- + +import { Application, Entity } from '@playcanvas/react'; +import { Camera, Render, Light, Collision } from '@playcanvas/react/components'; +import { useState, lazy } from 'react'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import InteractiveSource from '!!raw-loader!@site/src/components/playcanvas-react-example'; +import CodeBlock from '@theme/CodeBlock'; +import BrowserOnly from '@docusaurus/BrowserOnly'; + +export const LazyInteractive = lazy(() => import('@site/src/components/playcanvas-react-example')); + +Let's build a simple 3D scene step by step using PlayCanvas React. We'll create a basic scene with a lit sphere and see it running live in your browser. + +## Starting Point + +First, let's create the basic structure of our application using the `Application` component: + +```jsx +import { Application } from '@playcanvas/react' + +export default function App() { + return ( + + {/* Your 3D scene will go here */} + + ) +} +``` + +This creates an empty 3D scene. However, we can't see anything rendered yet. We need a camera and some content. + +## Adding a Camera + +To view our scene, we need a camera. In PlayCanvas React, we use `Entity` components as containers and attach component behaviors like `Camera`: + +```jsx +import { Application, Entity } from '@playcanvas/react' +import { Camera } from '@playcanvas/react/components' + +export default function App() { + return ( + + + + + + ) +} +``` + +We've added a camera entity positioned 5 units down the positive Z axis. By default, a camera looks down the negative Z axis so our camera is now looking at the origin. The rendered scene shows a solid grey color (the clear color of the camera). + +## Adding a Light + +Let's add a directional light to illuminate our scene: + +```jsx +import { Application, Entity } from '@playcanvas/react' +import { Camera, Light } from '@playcanvas/react/components' + +export default function App() { + return ( + + + + + + + + + ) +} +``` + +The light is rotated to shine at an angle, which will create more interesting shading on our objects. + +## Adding an Object + +Now let's add a sphere to our scene using the `Render` component: + + + + +```jsx +import { Application, Entity } from '@playcanvas/react' +import { Camera, Light, Render } from '@playcanvas/react/components' + +export default function App() { + return ( + + + + + + + + + + + + ) +} +``` + + + + +export const CompleteScene = () => { + return ( +
+ + + + + + + + + + + +
+ ); +}; + + + +
+
+ +You should now see a white sphere in the center of your screen! + +## Making it Interactive + +One of the great advantages of using React is how easy it is to add interactivity. Let's make our sphere respond to clicks. + +**Click on the Demo Tab to view the results.** + + + + {InteractiveSource} + + + + {() => } + + + + +## Key Differences from Web Components + +When building scenes with PlayCanvas React compared to [Web Components](/user-manual/web-components/): + +1. **Component Structure**: Instead of HTML tags like ``, we use React components like `` +2. **Props vs Attributes**: We use React props with camelCase (e.g., `clearColor`) instead of HTML attributes +3. **Event Handling**: We can use React's event system directly (e.g., `onClick`) +4. **State Management**: We can leverage React hooks like `useState` for dynamic behavior +5. **Type Safety**: Full TypeScript support with type safety. + +## Next Steps + +Now that you've built your first scene, try: + +- Adding more objects with different shapes (`box`, `cylinder`, `cone`, etc.) +- Experimenting with different light types (`point`, `spot`) +- Adding movement with the `OrbitControls` script +- Exploring physics by adding `RigidBody` and `Collision` components + +Now you have the basics, see the [documentation](https://playcanvas-react.vercel.app/docs) for more examples. diff --git a/docs/user-manual/playcanvas-react/index.mdx b/docs/user-manual/playcanvas-react/index.mdx new file mode 100644 index 00000000000..d9ccdae7edb --- /dev/null +++ b/docs/user-manual/playcanvas-react/index.mdx @@ -0,0 +1,87 @@ +--- +title: PlayCanvas React +sidebar_position: 5.6 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +[PlayCanvas React](https://github.com/playcanvas/react) is a modern library that brings the power of the PlayCanvas 3D engine into the React ecosystem. It provides a declarative, component-based approach to building interactive 3D applications using familiar React patterns. + +Explore the [official documentation](https://playcanvas-react.vercel.app/docs) or browse the [GitHub repository](https://github.com/playcanvas/react) for more. + +
+ +
+ Getting Started → +
+
+ View on GitHub ↗ +
+ +
+ + Gaussian Splats Example + + + 3D Model Viewer Example + + + Physics Example + + + Clock Example + +
+ +## Why React? + +React is at the heart of modern web development, and 3D content is no longer just for games. **PlayCanvas React** brings real-time 3D into the React ecosystem, enabling developers to build immersive experiences using the same tools and patterns they already use. With support for hooks, context, and component composition, it’s 3D — the React way. + +Explore more in the [example gallery](https://playcanvas-react.vercel.app/examples). + +### Full Engine Power, React Simplicity + +PlayCanvas React gives you access to the **entire** PlayCanvas engine via intuitive React components, with all its powerful features built-in without needing third-party libraries: + +- **Physics** – Full Ammo.js integration +- **Gaussian Splats** – Cutting-edge 3D point cloud rendering +- **Pointer Events** – React-style event propagation and bubbling +- **Asset System** – Declarative and imperative loading of assets +- **Scripts** – Escape hatches for custom logic when needed + +## Quick Start + +**The fastest way to get started is with the [StackBlitz template](https://playcanvas-react.vercel.app/new)**. It's a one-click experience that launches a basic scene with a camera and a cube. Fork, edit, or clone the code to make it your own. + +Alternatively, follow the [Getting Started guide](https://playcanvas-react.vercel.app/docs/getting-started) or install it directly in your project: + +```bash +npm install @playcanvas/react + +Then follow the [first scene walkthrough](./building-a-scene) to learn how to compose a scene using PlayCanvas React components and APIs. + +## Learn More + +- [Getting Started Guide](https://playcanvas-react.vercel.app/docs/guide/getting-started) - Set up your first project +- [Features Showcase](https://playcanvas-react.vercel.app/docs#features) - Explore all capabilities +- [Interactive Playground](https://playcanvas-react.vercel.app/examples) - Learn by doing +- [API Reference](https://playcanvas-react.vercel.app/docs/api) - Complete component documentation + +Join the growing community of developers building the next generation of 3D web experiences with **PlayCanvas React**! diff --git a/docusaurus.config.js b/docusaurus.config.js index aaad656bb36..c12c35d98f0 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -99,7 +99,7 @@ const config = { return redirects; } }], - 'docusaurus-plugin-sass', + 'docusaurus-plugin-sass' ], presets: [ diff --git a/package-lock.json b/package-lock.json index e1aa22cb100..28f9d399ea8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,12 @@ "@docusaurus/preset-classic": "^3.8.1", "@docusaurus/theme-mermaid": "^3.8.1", "@mdx-js/react": "^3.1.0", + "@playcanvas/react": "^0.5.0", + "canvas-confetti": "^1.9.3", "clsx": "^2.1.1", "docusaurus-plugin-sass": "^0.2.6", "prism-react-renderer": "^2.4.1", + "raw-loader": "^4.0.2", "react": "^19.1.0", "react-dom": "^19.1.0", "sass": "^1.89.2" @@ -4227,6 +4230,25 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@playcanvas/react": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@playcanvas/react/-/react-0.5.0.tgz", + "integrity": "sha512-vNqzLiWGSRTexJvptvG7ksr9655Uv0/bVt0v7vOAYuP7pD0MvYTHoVF40ygkBZHaYFP/GDllQjIxZ27BJWLv8w==", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "playcanvas": "~2.8.2", + "react": "^18.3.1 || ^19.1.0", + "react-dom": "^18.3.1 || ^19.1.0", + "sync-ammo": "^0.1.2" + }, + "peerDependenciesMeta": { + "sync-ammo": { + "optional": true + } + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -5143,6 +5165,12 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, + "node_modules/@types/webxr": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.22.tgz", + "integrity": "sha512-Vr6Stjv5jPRqH690f5I5GLjVk8GSsoQSYJ2FVd/3jJF7KaqfwPi3ehfBS96mlQ2kPCwZaX6U0rG2+NGHBKkA/A==", + "peer": true + }, "node_modules/@types/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", @@ -5300,6 +5328,12 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@webgpu/types": { + "version": "0.1.61", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.61.tgz", + "integrity": "sha512-w2HbBvH+qO19SB5pJOJFKs533CdZqxl3fcGonqL321VHkW7W/iBo6H8bjDy6pr/+pbMwIu5dnuaAxH7NxBqUrQ==", + "peer": true + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -5720,6 +5754,27 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5744,6 +5799,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "peer": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -5875,6 +5942,31 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6015,6 +6107,30 @@ } ] }, + "node_modules/canvas": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.1.0.tgz", + "integrity": "sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || >= 20.9.0" + } + }, + "node_modules/canvas-confetti": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz", + "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -6166,6 +6282,13 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true, + "peer": true + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -7958,6 +8081,16 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "optional": true, + "peer": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -8306,6 +8439,16 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -8719,6 +8862,13 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true, + "peer": true + }, "node_modules/fs-extra": { "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", @@ -8822,6 +8972,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "optional": true, + "peer": true + }, "node_modules/github-slugger": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", @@ -9572,6 +9729,27 @@ "postcss": "^8.1.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -12933,6 +13111,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "optional": true, + "peer": true + }, "node_modules/mlly": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", @@ -13001,6 +13186,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "optional": true, + "peer": true + }, "node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -13023,6 +13215,19 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "optional": true, + "peer": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -13624,6 +13829,22 @@ "pathe": "^2.0.3" } }, + "node_modules/playcanvas": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-2.8.2.tgz", + "integrity": "sha512-wyIYzROAu0Txs0emdR2N0AnbiGS2TRTzmAKlsVmYa1JDTe9dMJ+6gmauwsQMfNeStnGWRlSv5VDIHZ4D4Gzb2A==", + "peer": true, + "dependencies": { + "@types/webxr": "^0.5.22", + "@webgpu/types": "^0.1.60" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "canvas": "3.1.0" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -15016,6 +15237,43 @@ "postcss": "^8.4.31" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "optional": true, + "peer": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -15114,6 +15372,17 @@ "node": ">= 0.10" } }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "optional": true, + "peer": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -15242,6 +15511,70 @@ "node": ">= 0.8" } }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/raw-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/raw-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/raw-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -16497,6 +16830,53 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -16899,6 +17279,36 @@ "node": ">=6" } }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "optional": true, + "peer": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "optional": true, + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/terser": { "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", @@ -17067,6 +17477,19 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", diff --git a/package.json b/package.json index 7e48c2e5316..23b646db5d0 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,12 @@ "@docusaurus/preset-classic": "^3.8.1", "@docusaurus/theme-mermaid": "^3.8.1", "@mdx-js/react": "^3.1.0", + "@playcanvas/react": "^0.5.0", + "canvas-confetti": "^1.9.3", "clsx": "^2.1.1", "docusaurus-plugin-sass": "^0.2.6", "prism-react-renderer": "^2.4.1", + "raw-loader": "^4.0.2", "react": "^19.1.0", "react-dom": "^19.1.0", "sass": "^1.89.2" diff --git a/src/components/playcanvas-react-example.jsx b/src/components/playcanvas-react-example.jsx new file mode 100644 index 00000000000..74bbeb1a491 --- /dev/null +++ b/src/components/playcanvas-react-example.jsx @@ -0,0 +1,22 @@ +import { Application, Entity } from '@playcanvas/react' +import { Camera, Light, Render } from '@playcanvas/react/components' +import confetti from "canvas-confetti" + +export default function Interactive() { + return ( + + + + + + + + confetti()} + onPointerOver={() => document.body.style.cursor = 'pointer'} + onPointerOut={() => document.body.style.cursor = 'default'} > + + + + ) +} diff --git a/src/css/custom.scss b/src/css/custom.scss index f07213c3741..8db588f0a05 100644 --- a/src/css/custom.scss +++ b/src/css/custom.scss @@ -151,3 +151,78 @@ html[data-theme='dark'] .navbar--github-link:before { .nowrap-first-col td:first-child { white-space: nowrap; } + +.example-demo { + width: 100%; + height: 400px; + display: flex; + flex-direction: column; +} + +.example-demo > canvas { + border-radius: var(--ifm-code-border-radius); +} + +.example-demo[hidden] { + display: none; +} + +.grabbing { + cursor: grab; +} + +.grabbing:active { + cursor: grabbing; +} + +.hover-grow { + transition: transform 0.2s ease-in-out; + overflow: hidden; +} + +.hover-grow:hover { + transform: scale(1.05); + transform-origin: center; +} + +.hover-grow img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.tablet { + padding: 0.5rem 1.5rem; + font-size: 1rem; + color: var(--ifm-color-primary); + border-radius: 100px; + background-color: var(--ifm-breadcrumb-item-background-active); + display: inline-block; +} + +.font-bold { + font-weight: 500; +} + +.flex { + display: flex; + align-items: center; + gap: 2rem; +} + +.py-1 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.mb-2 { + margin-bottom: 2rem; +} + +.foreground { + color: var(--ifm-hero-text-color); +} + +.underline { + text-decoration: underline; +} \ No newline at end of file diff --git a/static/img/user-manual/react/clock.png b/static/img/user-manual/react/clock.png new file mode 100644 index 00000000000..9813baa5185 Binary files /dev/null and b/static/img/user-manual/react/clock.png differ diff --git a/static/img/user-manual/react/physics.png b/static/img/user-manual/react/physics.png new file mode 100644 index 00000000000..07701588b9f Binary files /dev/null and b/static/img/user-manual/react/physics.png differ diff --git a/static/img/user-manual/react/render.png b/static/img/user-manual/react/render.png new file mode 100644 index 00000000000..a25ca5813d9 Binary files /dev/null and b/static/img/user-manual/react/render.png differ diff --git a/static/img/user-manual/react/splats.jpg b/static/img/user-manual/react/splats.jpg new file mode 100644 index 00000000000..df2e7c2b743 Binary files /dev/null and b/static/img/user-manual/react/splats.jpg differ