├── static ├── banner.png └── favicon.png ├── gatsby-node.js ├── sandbox.config.json ├── src ├── pages │ ├── index.js │ └── 404.js ├── @rocketseat │ └── gatsby-theme-docs │ │ ├── components │ │ ├── Logo.js │ │ ├── Sidebar │ │ │ ├── InternalLink │ │ │ │ └── index.js │ │ │ ├── ExternalLink │ │ │ │ └── index.js │ │ │ ├── useSidebar.js │ │ │ ├── styles.js │ │ │ └── index.js │ │ └── Code │ │ │ ├── index.js │ │ │ └── styles.js │ │ └── styles │ │ └── global.js ├── docs │ ├── recipes │ │ ├── re-parenting.mdx │ │ ├── using-your-own-camera-rig.mdx │ │ ├── enabling-vr.mdx │ │ ├── rendering-only-when-needed.mdx │ │ ├── shader-materials.mdx │ │ ├── usage-with-react-native.mdx │ │ ├── switching-the-default-renderer.mdx │ │ ├── managing-imperative-code.mdx │ │ ├── dealing-with-effects-hijacking-main-render-loop.mdx │ │ ├── heads-up-display-rendering-multiple-scenes.mdx │ │ ├── animating-with-react-spring.mdx │ │ └── reducing-bundle-size.mdx │ ├── api │ │ ├── hooks │ │ │ ├── useUpdate.mdx │ │ │ ├── introduction.mdx │ │ │ ├── useResource.mdx │ │ │ ├── useFrame.mdx │ │ │ ├── useLoader.mdx │ │ │ └── useThree.mdx │ │ ├── additional.mdx │ │ ├── automatic-disposal.mdx │ │ ├── events.mdx │ │ ├── canvas.mdx │ │ └── objects-and-properties.mdx │ ├── getting-started.mdx │ ├── first-scene │ │ ├── animation.mdx │ │ ├── first-render.mdx │ │ └── lights.mdx │ └── index.mdx ├── components │ ├── useOutsideClick.js │ └── Home │ │ ├── styles.css │ │ ├── logo.js │ │ ├── why.js │ │ ├── index.js │ │ ├── Hero │ │ ├── Waterpass.js │ │ ├── index.js │ │ └── Glitchpass.js │ │ └── playground.js ├── assets │ └── logo.js └── config │ └── sidebar.yml ├── README.md ├── LICENSE ├── package.json ├── .gitignore └── gatsby-config.js /static/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmndrs/r3f-website/HEAD/static/banner.png -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmndrs/r3f-website/HEAD/static/favicon.png -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Node APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/node-apis/ 5 | */ 6 | -------------------------------------------------------------------------------- /sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "infiniteLoopProtection": false, 3 | "hardReloadOnChange": false, 4 | "view": "browser", 5 | "template": "gatsby" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from "react"; 3 | import Home from "../components/Home"; 4 | 5 | export default function Homepage() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Logo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "styled-components/macro"; 3 | import Logo from "../../../assets/logo.js"; 4 | 5 | export default () => ( 6 | 12 | ); 13 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Sidebar/InternalLink/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from 'gatsby'; 4 | 5 | export default function InternalLink({ link, label }) { 6 | return ( 7 | 8 | {label} 9 | 10 | ); 11 | } 12 | 13 | InternalLink.propTypes = { 14 | link: PropTypes.string.isRequired, 15 | label: PropTypes.string.isRequired, 16 | }; 17 | -------------------------------------------------------------------------------- /src/docs/recipes/re-parenting.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Re-parenting 3 | --- 4 | 5 | We support [portals](https://reactjs.org/docs/portals.html). You can use them to teleport a piece of the view into another container. Click [here](https://codesandbox.io/s/three-fibre-useFrame-test-fojbq) for a small demo. 6 | 7 | ```jsx 8 | import { createPortal } from 'react-three-fiber' 9 | 10 | function Component() { 11 | // "target" can be a three object, like a group, etc 12 | return createPortal(, target) 13 | ``` 14 | -------------------------------------------------------------------------------- /src/docs/recipes/using-your-own-camera-rig.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using your own camera rig 3 | --- 4 | 5 | ```jsx 6 | function Camera(props) { 7 | const ref = useRef() 8 | const { setDefaultCamera } = useThree() 9 | // Make the camera known to the system 10 | useEffect(() => void setDefaultCamera(ref.current), []) 11 | // Update it every frame 12 | useFrame(() => ref.current.updateMatrixWorld()) 13 | return 14 | } 15 | 16 | 17 | 18 | ``` 19 | -------------------------------------------------------------------------------- /src/docs/recipes/enabling-vr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Enabling VR 3 | --- 4 | 5 | Supplying the `vr` flag enables Three's VR mode and switches the render-loop to gl.setAnimationLoop [as described in Three's docs](https://threejs.org/docs/index.html#manual/en/introduction/How-to-create-VR-content). 6 | 7 | ```jsx 8 | import * as VR from "!exports-loader?WEBVR!three/examples/js/vr/WebVR"; 9 | import { Canvas } from "react-three-fiber"; 10 | 11 | document.body.appendChild(VR.createButton(gl))} 14 | />; 15 | ``` 16 | -------------------------------------------------------------------------------- /src/docs/api/hooks/useUpdate.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useUpdate 3 | --- 4 | 5 | ```jsx 6 | useUpdate(callback, dependencies, (optionalRef = undefined)); 7 | ``` 8 | 9 | When objects need to be updated imperatively. 10 | 11 | ```jsx 12 | import { useUpdate } from "react-three-fiber"; 13 | 14 | const ref = useUpdate( 15 | (geometry) => { 16 | geometry.addAttribute("position", getVertices(x, y, z)); 17 | geometry.attributes.position.needsUpdate = true; 18 | }, 19 | [x, y, z] // execute only if these properties change 20 | ); 21 | return ; 22 | ``` 23 | -------------------------------------------------------------------------------- /src/docs/api/hooks/introduction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hooks 3 | --- 4 | 5 | Hooks can only be used **inside** the Canvas element because they rely on context! You cannot expect something like this to work: 6 | 7 | ```jsx 8 | function App() { 9 | const { size } = useThree() // This will just crash 10 | return ( 11 | 12 | 13 | ``` 14 | 15 | Do this instead: 16 | 17 | ```jsx 18 | function SomeComponent() { 19 | const { size } = useThree() 20 | return 21 | } 22 | 23 | function App() { 24 | return ( 25 | 26 | 27 | ``` 28 | -------------------------------------------------------------------------------- /src/docs/getting-started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Getting started" 3 | --- 4 | 5 | The best way to get started with React Three fiber is by installing with using npm or yarn: 6 | 7 | ```bash 8 | # Using Yarn: 9 | yarn add react-three-fiber three 10 | 11 | # Using NPM: 12 | npm i react-three-fiber three 13 | ``` 14 | 15 | You can also use a good old script tag if you want: 16 | 17 | ```html 18 | 19 | 20 | ``` 21 | 22 | Let's create our first scene 23 | -------------------------------------------------------------------------------- /src/docs/recipes/rendering-only-when-needed.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Rendering only when needed 3 | --- 4 | 5 | By default it renders like a game loop 60fps. Switch on `invalidateFrameloop` to activate loop invalidation. Now it will render on demand when it detects prop changes. 6 | 7 | ```jsx 8 | 9 | ``` 10 | 11 | Sometimes you want to render single frames manually, for instance when you're dealing with async stuff or camera controls: 12 | 13 | ```jsx 14 | const { invalidate } = useThree(); 15 | const texture = useMemo(() => loader.load(url, invalidate), [url]); 16 | ``` 17 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Sidebar/ExternalLink/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { FiExternalLink } from 'react-icons/fi'; 4 | 5 | export default function ExternalLink({ link, label }) { 6 | return ( 7 | 8 | {label} 9 | 12 | 13 | ); 14 | } 15 | 16 | ExternalLink.propTypes = { 17 | link: PropTypes.string.isRequired, 18 | label: PropTypes.string.isRequired, 19 | }; 20 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | 4 | import Layout from '@rocketseat/gatsby-theme-docs/src/components/Layout'; 5 | import SEO from '@rocketseat/gatsby-theme-docs/src/components/SEO'; 6 | 7 | export default function NotFound() { 8 | return ( 9 | 10 | 11 |

You just hit a route that doesn't exist... the sadness.

12 |

13 | If you'd like to go back to homepage, click here 14 | . 15 |

16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/docs/api/additional.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Additional exports 3 | --- 4 | 5 | ```jsx 6 | import { 7 | addEffect, // Adds a global callback which is called each frame 8 | addTail, // Adds a global callback which is called when rendering stops 9 | invalidate, // Forces view global invalidation 10 | extend, // Extends the native-object catalogue 11 | createPortal, // Creates a portal (it's a React feature for re-parenting) 12 | render, // Internal: Renders three jsx into a scene 13 | unmountComponentAtNode, // Internal: Unmounts root scene 14 | applyProps, // Internal: Sets element properties 15 | } from "react-three-fiber"; 16 | ``` 17 | -------------------------------------------------------------------------------- /src/docs/recipes/shader-materials.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using Shader Materials 3 | --- 4 | 5 | ```jsx 6 | function CrossFade({ url1, url2, disp }) { 7 | const [texture1, texture2, dispTexture] = useLoader(THREE.TextureLoader, [url1, url2, disp]) 8 | 9 | return ( 10 | 11 | 12 | 19 | 20 | ) 21 | ``` 22 | -------------------------------------------------------------------------------- /src/docs/api/hooks/useResource.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useResource 3 | --- 4 | 5 | ```jsx 6 | useResource((optionalRef = undefined)); 7 | ``` 8 | 9 | Take advantage of React's `useRef` with the added consideration of rendering when a component is available (e.g. in the next frame). Useful when you want to share and re-use resources across components. 10 | 11 | ```jsx 12 | import { useResource } from 'react-three-fiber' 13 | 14 | const [ref, material] = useResource() 15 | return ( 16 | 17 | {material && ( 18 | 19 | 20 | 21 | )} 22 | ) 23 | ``` 24 | -------------------------------------------------------------------------------- /src/components/useOutsideClick.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | function useClickOutside(ref, fn) { 4 | useEffect(() => { 5 | /** 6 | * Alert if clicked on outside of element 7 | */ 8 | function handleClickOutside(event) { 9 | if (ref.current && !ref.current.contains(event.target)) { 10 | fn(); 11 | } 12 | } 13 | // Bind the event listener 14 | document.addEventListener("mousedown", handleClickOutside); 15 | return () => { 16 | // Unbind the event listener on clean up 17 | document.removeEventListener("mousedown", handleClickOutside); 18 | }; 19 | }, [ref, fn]); 20 | } 21 | 22 | export default useClickOutside; 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Three Fiber Website 2 | 3 | Demo: https://r3f.netlify.app 4 | 5 | [![Netlify Status](https://api.netlify.com/api/v1/badges/e60823a1-0b2a-4cc4-8cdb-b8ef6f400ead/deploy-status)](https://app.netlify.com/sites/r3f/deploys) 6 | 7 | ## How to run locally 8 | 9 | ```bash 10 | git clone git@github.com:react-spring/r3f-website.git 11 | cd r3f-website 12 | yarn && yarn start 13 | ``` 14 | 15 | ## How to add docs 16 | 17 | 1. Create a markdown file in the [docs folder](https://github.com/react-spring/r3f-website/tree/master/src/docs) in the section you feel it's a better suit for what you are writing. 18 | 19 | 2. To add to the sidebar please edit the [`sidebar.yaml`](https://github.com/react-spring/r3f-website/blob/master/src/config/sidebar.yml) to point yo your new doc 20 | 21 | -------------------------------------------------------------------------------- /src/docs/recipes/usage-with-react-native.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Usage with React Native 3 | --- 4 | 5 | You can use `react-three-fiber` to build universal (native and web) apps via Expo's WebGL package ([expo-gl](https://docs.expo.io/versions/latest/sdk/gl-view/)). 6 | 7 | > 💡 **Bootstrap**: `npx create-react-native-app -t with-react-three-fiber` 8 | 9 | Be sure to use a physical iOS or Android device for testing because the simulator can have issues running graphics heavy apps. 10 | 11 | ### Manual setup 12 | 13 | ```bash 14 | # Install the Expo CLI 15 | 16 | npm i -g expo-cli 17 | 18 | # Create a new project 19 | 20 | expo init myapp 21 | cd myapp 22 | 23 | # Install packages 24 | 25 | yarn add expo-gl expo-three three@latest react-three-fiber@beta 26 | 27 | # Start the project 28 | 29 | yarn start 30 | ``` 31 | -------------------------------------------------------------------------------- /src/docs/recipes/switching-the-default-renderer.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Switching the default renderer 3 | --- 4 | 5 | If you want to exchange the default renderer you can. [Here's](https://codesandbox.io/s/yq90n32zmx) a small example. 6 | 7 | ```jsx 8 | import { render, unmountComponentAtNode } from "react-three-fiber"; 9 | 10 | const renderer = new THREE.SVGRenderer(); 11 | renderer.setSize(window.innerWidth, window.innerHeight); 12 | document.body.appendChild(renderer.domElement); 13 | const camera = new THREE.PerspectiveCamera( 14 | 75, 15 | window.innerWidth / window.innerHeight, 16 | 0.1, 17 | 1000 18 | ); 19 | const scene = new THREE.Scene(); 20 | 21 | render( 22 | 23 | 24 | 25 | , 26 | scene 27 | ); 28 | ``` 29 | -------------------------------------------------------------------------------- /src/docs/api/automatic-disposal.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Automatic disposal 3 | --- 4 | 5 | Freeing resources is a [manual chore in Threejs](https://threejs.org/docs/#manual/en/introduction/How-to-dispose-of-objects), but React is aware of object-lifecycles, hence three-fiber will attempt to free resources for you by calling `object.dispose()`, if present, on all unmounted objects. 6 | 7 | If you manage assets by yourself, globally or in a cache, this may _not_ be what you want. You can switch it off by placing `dispose={null}` onto meshes, materials, etc, or even on parent containers like groups, it is now valid for the entire tree. 8 | 9 | ```jsx 10 | const globalGeometry = new THREE.BoxBufferGeometry() 11 | const globalMaterial = new THREE.MeshBasicMatrial() 12 | 13 | function Mesh() { 14 | return ( 15 | 16 | 17 | ``` 18 | -------------------------------------------------------------------------------- /src/docs/first-scene/animation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Your first animation 3 | --- 4 | 5 | To shadow/override this theme, you will need to place your files into `src/@rocketseat/gatsby-theme-docs/` folder. 6 | 7 | _ps: if you still have questions, check the [Shadowing in Gatsby Themes](https://www.gatsbyjs.org/docs/themes/shadowing/) article._ 8 | 9 | ## Changing the logo 10 | 11 | If you want to change the logo just create the following file: 12 | 13 | ```js title=src/@rocketseat/gatsby-theme-docs/components/Logo.js 14 | import styled from "@emotion/styled"; 15 | 16 | import logo from "../assets/logo.svg"; 17 | 18 | export default styled.div` 19 | width: 160px; 20 | height: 36px; 21 | background-size: contain; 22 | background: url(${logo}) center no-repeat; 23 | `; 24 | ``` 25 | 26 | ## Changing the home text 27 | 28 | Create a file at `src/@rocketseat/gatsby-theme-docs/text/index.mdx` to edit the text. 29 | -------------------------------------------------------------------------------- /src/components/Home/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html, 6 | body, 7 | #___gatsby { 8 | width: 100%; 9 | height: 100%; 10 | margin: 0; 11 | padding: 0; 12 | overflow: hidden; 13 | } 14 | 15 | #___gatsby { 16 | overflow: auto; 17 | } 18 | 19 | select { 20 | -webkit-appearance: none; 21 | padding: 4px; 22 | background: transparent; 23 | border: 1px solid #ffffff; 24 | color: white; 25 | border-radius: 0; 26 | } 27 | 28 | input { 29 | background: transparent; 30 | border: 1px solid #ffffff; 31 | width: 50px; 32 | color: white; 33 | padding: 4px; 34 | } 35 | 36 | canvas { 37 | overflow: hidden; 38 | } 39 | 40 | body { 41 | overscroll-behavior-y: none; 42 | font-family: "Josefin Sans", -apple-system, BlinkMacSystemFont, avenir next, 43 | avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, 44 | sans-serif; 45 | color: black; 46 | background: white; 47 | } 48 | -------------------------------------------------------------------------------- /src/assets/logo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Icon = (props) => ( 4 | 5 | 9 | 10 | ); 11 | 12 | export default Icon; 13 | -------------------------------------------------------------------------------- /src/components/Home/logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Icon = props => ( 4 | 5 | 9 | 10 | ) 11 | 12 | export default Icon 13 | -------------------------------------------------------------------------------- /src/docs/recipes/managing-imperative-code.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Managing imperative code 3 | --- 4 | 5 | Stick imperative stuff into useMemo and write out everything else declaratively. This is how you can quickly form reactive, re-usable components that can be bound to a store, graphql, etc. 6 | 7 | ```jsx 8 | function Extrusion({ start = [0, 0], paths, ...props }) { 9 | const shape = useMemo(() => { 10 | const shape = new THREE.Shape(); 11 | shape.moveTo(...start); 12 | paths.forEach((path) => shape.bezierCurveTo(...path)); 13 | return shape; 14 | }, [start, paths]); 15 | 16 | return ( 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | 24 | ; 34 | ``` 35 | -------------------------------------------------------------------------------- /src/docs/api/hooks/useFrame.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useFrame 3 | --- 4 | 5 | ```jsx 6 | useFrame((callback: (state, delta) => void), (renderPriority: number = 0)); 7 | ``` 8 | 9 | This hook calls you back every frame, which is good for running effects, updating controls, etc. You receive the state (same as useThree) and a clock delta. If you supply a render priority greater than zero it will switch off automatic rendering entirely, you can then control rendering yourself. If you have multiple frames with a render priority then they are ordered highest priority last, similar to the web's z-index. Frames are managed, three-fiber will remove them automatically when the component that holds them is unmounted. 10 | 11 | Updating controls: 12 | 13 | ```jsx 14 | import { useFrame } from "react-three-fiber"; 15 | 16 | const controls = useRef(); 17 | useFrame((state) => controls.current.update()); 18 | return ; 19 | ``` 20 | 21 | Taking over the render-loop: 22 | 23 | ```jsx 24 | useFrame(({ gl, scene, camera }) => gl.render(scene, camera), 1); 25 | ``` 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rocketseat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-three-fiber-homepage", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Homepage and docs for react three fiber", 6 | "license": "MIT", 7 | "dependencies": { 8 | "@rocketseat/gatsby-theme-docs": "^2.1.3", 9 | "@rocketseat/gatsby-theme-docs-core": "^1.1.1", 10 | "gatsby": "^2.24.47", 11 | "gatsby-plugin-canonical-urls": "^2.3.10", 12 | "gatsby-plugin-google-analytics": "^2.3.13", 13 | "gatsby-plugin-manifest": "^2.4.23", 14 | "gatsby-plugin-mdx": "^1.2.34", 15 | "gatsby-plugin-offline": "^3.2.23", 16 | "gatsby-plugin-sitemap": "^2.4.11", 17 | "gatsby-plugin-styled-components": "^3.3.10", 18 | "react": "16.13.1", 19 | "react-color": "2.18.1", 20 | "react-dom": "16.13.1", 21 | "react-live": "2.2.2", 22 | "react-markdown": "^4.3.1", 23 | "react-scripts": "3.4.3", 24 | "react-three-fiber": "4.2.10", 25 | "styled-components": "5.1.1", 26 | "three": "0.117.1" 27 | }, 28 | "scripts": { 29 | "build": "gatsby build", 30 | "start": "gatsby develop", 31 | "serve": "gatsby serve", 32 | "clean": "gatsby clean" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/docs/recipes/dealing-with-effects-hijacking-main-render-loop.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dealing with effects (hijacking main render-loop) 3 | --- 4 | 5 | Managing effects can get quite complex normally. Drop the component below into a scene and you have a live effect. Remove it and everything is as it was without any re-configuration. 6 | 7 | ```jsx 8 | import { extend, Canvas, useFrame, useThree } from 'react-three-fiber' 9 | import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer' 10 | import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass' 11 | import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass' 12 | extend({ EffectComposer, RenderPass, GlitchPass }) 13 | 14 | function Effects() { 15 | const { gl, scene, camera, size } = useThree() 16 | const composer = useRef() 17 | useEffect(() => void composer.current.setSize(size.width, size.height), [size]) 18 | useFrame(() => composer.current.render(), 1) 19 | return ( 20 | 21 | 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /src/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "React Three Fiber" 3 | --- 4 | 5 | React Three Fiber is a React reconciler for Threejs on the web and react-native. 6 | 7 | ## Why? 8 | Building dynamic scene graphs declaratively with re-usable components makes dealing with Threejs easier and brings order and sanity to your codebase. These components react to state changes, are interactive out of the box and can tap into React's infinite ecosystem. 9 | 10 | ## Does it have limitations? 11 | None. Everything that works in Threejs will work here. In contrast to "bindings" where a library ships/maintains dozens of wrapper components, it just reconciles JSX to Threejs dynamically: <mesh /> simply is another expression for new THREE.Mesh(). It does not know or target a specific Threejs version nor does it need updates for modified, added or removed upstream features. 12 | 13 | ## Is it slower than raw Threejs? 14 | No. Rendering performance is up to Threejs and the GPU. Components may participate in the renderloop outside of React, without any additional overhead. React is otherwise very efficient in building and managing component-trees, it could potentially outperform manual/imperative apps at scale. 15 | 16 | [Get started now!](/docs/getting-started) 17 | -------------------------------------------------------------------------------- /src/docs/api/hooks/useLoader.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useLoader (Experimental) 3 | --- 4 | 5 | ```jsx 6 | useLoader(loader, url: string | string[], extensions?) 7 | ``` 8 | 9 | This hook loads assets and suspends for easier fallback- and error-handling. 10 | 11 | ```jsx 12 | import React, { Suspense } from "react"; 13 | import { useLoader } from "react-three-fiber"; 14 | import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; 15 | 16 | function Asset({ url }) { 17 | const gltf = useLoader(GLTFLoader, url); 18 | return ; 19 | } 20 | 21 | }> 22 | 23 | ; 24 | ``` 25 | 26 | You can provide a callback if you need to configure your loader: 27 | 28 | ```jsx 29 | import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; 30 | 31 | useLoader(GLTFLoader, url, (loader) => { 32 | const dracoLoader = new DRACOLoader(); 33 | dracoLoader.setDecoderPath("/draco-gltf/"); 34 | loader.setDRACOLoader(dracoLoader); 35 | }); 36 | ``` 37 | 38 | It can also make multiple requests in parallel: 39 | 40 | ```jsx 41 | const [bumpMap, specMap, normalMap] = useLoader(TextureLoader, [ 42 | url1, 43 | url2, 44 | url2, 45 | ]); 46 | ``` 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variable files 55 | .env* 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | -------------------------------------------------------------------------------- /src/docs/api/hooks/useThree.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useThree 3 | --- 4 | 5 | ```jsx 6 | useThree(): SharedCanvasContext 7 | ``` 8 | 9 | This hook gives you access to all the basic objects that are kept internally, like the default renderer, scene, camera. It also gives you the current size of the canvas in screen and viewport coordinates. The hook is reactive, if you resize the browser, for instance, and you get fresh measurements, same applies to any of the defaults you can change. 10 | 11 | ```jsx 12 | import { useThree } from "react-three-fiber"; 13 | 14 | const { 15 | gl, // WebGL renderer 16 | scene, // Default scene 17 | camera, // Default camera 18 | raycaster, // Default raycaster 19 | size, // Bounds of the view (which stretches 100% and auto-adjusts) 20 | viewport, // Bounds of the viewport in 3d units + factor (size/viewport) 21 | aspect, // Aspect ratio (size.width / size.height) 22 | mouse, // Current, centered, normalized 2D mouse coordinates 23 | raycaster, // Intternal raycaster instance 24 | clock, // THREE.Clock (useful for useFrame deltas) 25 | invalidate, // Invalidates a single frame (for ) 26 | intersect, // Calls onMouseMove handlers for objects underneath the cursor 27 | setDefaultCamera, // Sets the default camera 28 | } = useThree(); 29 | ``` 30 | -------------------------------------------------------------------------------- /src/docs/recipes/heads-up-display-rendering-multiple-scenes.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Heads-up display (rendering multiple scenes) 3 | --- 4 | 5 | `useFrame` allows components to hook into the render-loop, or even to take it over entirely. That makes it possible for one component to render over the content of another. The order of these operations is established by the priority you give it, higher priority means it renders first. 6 | 7 | ```jsx 8 | function Main() { 9 | const scene = useRef() 10 | const { camera } = useThree() 11 | useFrame(({ gl }) => void ((gl.autoClear = true), gl.render(scene.current, camera)), 100) 12 | return {/* ... */} 13 | } 14 | 15 | function HeadsUpDisplay() { 16 | const scene = useRef() 17 | const { camera } = useThree() 18 | useFrame(({ gl }) => void ((gl.autoClear = false), gl.clearDepth(), gl.render(scene.current, camera)), 10) 19 | return {/* ... */} 20 | } 21 | 22 | function App() { 23 | const camera = useRef() 24 | const { size, setDefaultCamera } = useThree() 25 | useEffect(() => void setDefaultCamera(camera.current), []) 26 | useFrame(() => camera.current.updateMatrixWorld()) 27 | return ( 28 | <> 29 | self.updateProjectionMatrix()} 34 | /> 35 |
36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | siteTitle: `React Three Fiber`, 4 | defaultTitle: `React Three Fiber`, 5 | siteTitleShort: `react-three-fiber`, 6 | siteDescription: `React Three Fiber is a React reconciler for Threejs on the web and react-native.`, 7 | siteUrl: `https://react-three-fiber.com`, 8 | siteAuthor: `Paul Henschel`, 9 | siteImage: `/banner.png`, 10 | siteLanguage: `en`, 11 | themeColor: `#333`, 12 | basePath: `/docs`, 13 | footer: ``, 14 | }, 15 | plugins: [ 16 | `gatsby-plugin-styled-components`, 17 | { 18 | resolve: `@rocketseat/gatsby-theme-docs`, 19 | options: { 20 | basePath: "/docs", 21 | configPath: `src/config`, 22 | docsPath: `src/docs`, 23 | githubUrl: `https://github.com/react-spring/r3f-website`, 24 | }, 25 | }, 26 | { 27 | resolve: `gatsby-plugin-manifest`, 28 | options: { 29 | name: `React Three Fiber`, 30 | short_name: `React Three Fiber`, 31 | start_url: `/docs`, 32 | background_color: `#ffffff`, 33 | display: `standalone`, 34 | icon: `static/favicon.png`, 35 | }, 36 | }, 37 | `gatsby-plugin-sitemap`, 38 | { 39 | resolve: `gatsby-plugin-google-analytics`, 40 | options: { 41 | // trackingId: ``, 42 | }, 43 | }, 44 | { 45 | resolve: `gatsby-plugin-canonical-urls`, 46 | options: { 47 | siteUrl: `https://react-three-fiber.com`, 48 | }, 49 | }, 50 | `gatsby-plugin-offline`, 51 | ], 52 | }; 53 | -------------------------------------------------------------------------------- /src/docs/recipes/animating-with-react-spring.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Animating with react-spring 3 | --- 4 | 5 | [react-spring](https://www.react-spring.io/) supports react-three-fiber out of the box: 6 | 7 | ```jsx 8 | import { Canvas } from "react-three-fiber"; 9 | // this example is using react-spring@9 10 | import { useSpring } from "@react-spring/core"; 11 | import { a } from "@react-spring/three"; 12 | 13 | function Box(props) { 14 | const [active, setActive] = useState(0); 15 | 16 | // create a common spring that will be used later to interpolate other values 17 | const { spring } = useSpring({ 18 | spring: active, 19 | config: { mass: 5, tension: 400, friction: 50, precision: 0.0001 }, 20 | }); 21 | 22 | // interpolate values from commong spring 23 | const scale = spring.to([0, 1], [1, 5]); 24 | const rotation = spring.to([0, 1], [0, Math.PI]); 25 | const color = spring.to([0, 1], ["#6246ea", "#e45858"]); 26 | 27 | return ( 28 | // using a from react-spring will animate our component 29 | setActive(Number(!active))} 34 | > 35 | 36 | 37 | 38 | ); 39 | } 40 | ``` 41 | 42 | 49 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Sidebar/useSidebar.js: -------------------------------------------------------------------------------- 1 | import { graphql, useStaticQuery } from "gatsby"; 2 | import { resolveLink } from "@rocketseat/gatsby-theme-docs-core/util/url"; 3 | 4 | export function useSidebar() { 5 | const data = useStaticQuery(graphql` 6 | { 7 | allSidebarItems { 8 | edges { 9 | node { 10 | label 11 | link 12 | items { 13 | label 14 | link 15 | items { 16 | label 17 | link 18 | } 19 | } 20 | id 21 | } 22 | } 23 | } 24 | site { 25 | siteMetadata { 26 | basePath 27 | } 28 | } 29 | } 30 | `); 31 | 32 | const { basePath } = data.site.siteMetadata; 33 | 34 | const { 35 | allSidebarItems: { edges }, 36 | } = data; 37 | 38 | if (basePath) { 39 | const normalizedSidebar = edges.map( 40 | ({ node: { label, link, items, id } }) => { 41 | if (Array.isArray(items)) { 42 | items = items.map((item) => { 43 | if (Array.isArray(item.items)) { 44 | const items = item.items.map((i) => ({ 45 | label: i.label, 46 | link: resolveLink(i.link, basePath), 47 | })); 48 | 49 | return { 50 | label: item.label, 51 | link: resolveLink(item.link, basePath), 52 | items, 53 | }; 54 | } 55 | return { 56 | label: item.label, 57 | link: resolveLink(item.link, basePath), 58 | }; 59 | }); 60 | } 61 | 62 | return { 63 | node: { 64 | id, 65 | label, 66 | items, 67 | link: resolveLink(link, basePath), 68 | }, 69 | }; 70 | } 71 | ); 72 | 73 | return normalizedSidebar; 74 | } 75 | 76 | return edges; 77 | } 78 | -------------------------------------------------------------------------------- /src/components/Home/why.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "styled-components/macro"; 3 | 4 | const Why = () => { 5 | return ( 6 |
19 |
20 |

Why?

21 |

22 | Building dynamic scene graphs declaratively with re-usable components 23 | makes dealing with Threejs easier and brings order and sanity to your 24 | codebase. These components react to state changes, are interactive out 25 | of the box and can tap into React's infinite ecosystem. 26 |

27 |
28 |
29 |

Does it have limitations?

30 |

31 | None. Everything that works in Threejs will work here. In contrast to 32 | "bindings" where a library ships/maintains dozens of wrapper 33 | components, it just reconciles JSX to Threejs dynamically: <mesh /> 34 | simply is another expression for new THREE.Mesh(). It does not know or 35 | target a specific Threejs version nor does it need updates for 36 | modified, added or removed upstream features. 37 |

38 |
39 |
40 |

Is it slower than raw Threejs?

41 |

42 | No. Rendering performance is up to Threejs and the GPU. Components may 43 | participate in the renderloop outside of React, without any additional 44 | overhead. React is otherwise very efficient in building and managing 45 | component-trees, it could potentially outperform manual/imperative 46 | apps at scale. 47 |

48 |
49 |
50 | ); 51 | }; 52 | 53 | export default Why; 54 | -------------------------------------------------------------------------------- /src/config/sidebar.yml: -------------------------------------------------------------------------------- 1 | - label: "Introduction" 2 | link: "/" 3 | - label: "Getting started" 4 | link: "/getting-started" 5 | - label: First Scene 6 | items: 7 | - label: "First Things on the Screen" 8 | link: "/first-scene/first-render" 9 | - label: "Lights" 10 | link: "/first-scene/lights" 11 | - label: "Animation" 12 | link: "/first-scene/animation" 13 | - label: API 14 | items: 15 | - label: "Canvas" 16 | link: "/api/canvas" 17 | - label: "Objects and properties" 18 | link: "/api/objects-and-properties" 19 | - label: "Automatic disposal" 20 | link: "/api/automatic-disposal" 21 | - label: "Events" 22 | link: "/api/events" 23 | - label: "Hooks" 24 | items: 25 | - label: "Introduction" 26 | link: "/api/hooks/introduction" 27 | - label: "useThree" 28 | link: "/api/hooks/useThree" 29 | - label: "useFrame" 30 | link: "/api/hooks/useFrame" 31 | - label: "useResource" 32 | link: "/api/hooks/useResource" 33 | - label: "useUpdate" 34 | link: "/api/hooks/useUpdate" 35 | - label: "useLoader" 36 | link: "/api/hooks/useLoader" 37 | - label: "Additional exports" 38 | link: "/api/additional" 39 | 40 | - label: Recipes 41 | items: 42 | - label: "Animating with react-spring" 43 | link: "/recipes/animating-with-react-spring" 44 | - label: "Dealing with effects" 45 | link: "/recipes/dealing-with-effects-hijacking-main-render-loop" 46 | - label: "Using your own camera rig" 47 | link: "/recipes/using-your-own-camera-rig" 48 | - label: "Enabling VR" 49 | link: "/recipes/enabling-vr" 50 | - label: "Heads-up display" 51 | link: "/recipes/heads-up-display-rendering-multiple-scenes" 52 | - label: "Managing imperative code" 53 | link: "/recipes/managing-imperative-code" 54 | - label: "Re-parenting" 55 | link: "/recipes/re-parenting" 56 | - label: "Reducing bundle-size" 57 | link: "/recipes/reducing-bundle-size" 58 | - label: "Rendering only when needed" 59 | link: "/recipes/rendering-only-when-needed" 60 | - label: "Shader Materials" 61 | link: "/recipes/shader-materials" 62 | - label: "Switching the default renderer" 63 | link: "/recipes/switching-the-default-renderer" 64 | - label: "Usage with React Native" 65 | link: "/recipes/usage-with-react-native" 66 | -------------------------------------------------------------------------------- /src/docs/api/events.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Events 3 | --- 4 | 5 | Threejs objects that implement their own `raycast` method (meshes, lines, etc) can be interacted with by declaring events on them. We support pointer events, clicks and wheel-scroll. Events contain the browser event as well as the Threejs event data (object, point, distance, etc). You need to [polyfill](https://github.com/jquery/PEP) them yourself, if that's a concern. 6 | 7 | Additionally, there's a special `onUpdate` that is called every time the object gets fresh props, which is good for things like `self => (self.verticesNeedUpdate = true)`. 8 | 9 | Also notice the `onPointerMissed` on the canvas element, which fires on clicks that haven't hit any meshes. 10 | 11 | ```jsx 12 | console.log("click")} 14 | onWheel={(e) => console.log("wheel spins")} 15 | onPointerUp={(e) => console.log("up")} 16 | onPointerDown={(e) => console.log("down")} 17 | onPointerOver={(e) => console.log("over")} 18 | onPointerOut={(e) => console.log("out")} 19 | onPointerEnter={(e) => console.log("enter")} 20 | onPointerLeave={(e) => console.log("leave")} 21 | onPointerMove={(e) => console.log("move")} 22 | onUpdate={(self) => console.log("props have been updated")} 23 | /> 24 | ``` 25 | 26 | #### Event data 27 | 28 | ```jsx 29 | ({ 30 | ...DomEvent // All the original event data 31 | ...ThreeEvent // All of Three's intersection data 32 | intersections: Intersect[] // All intersections 33 | object: Object3D // The object that was actually hit 34 | eventObject: Object3D // The object that registered the event 35 | unprojectedPoint: Vector3 // Camera-unprojected point 36 | ray: Ray // The ray that was used to strike the object 37 | camera: Camera // The camera that was used in the raycaster 38 | sourceEvent: DomEvent // A reference to the host event 39 | delta: number // Initial-click delta 40 | }) => ... 41 | ``` 42 | 43 | #### Propagation and capturing 44 | 45 | ```jsx 46 | onPointerDown={e => { 47 | // Only the mesh closest to the camera will be processed 48 | e.stopPropagation() 49 | // You may optionally capture the target 50 | e.target.setPointerCapture(e.pointerId) 51 | }} 52 | onPointerUp={e => { 53 | e.stopPropagation() 54 | // Optionally release capture 55 | e.target.releasePointerCapture(e.pointerId) 56 | }} 57 | ``` 58 | -------------------------------------------------------------------------------- /src/docs/first-scene/first-render.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Creating our first cube 3 | --- 4 | 5 | Let's create our first cube using `react-three-fiber`. 6 | The first thing we need to do is import `Canvas` from `react-three-fiber` and we need to wrap the parent of the 3D scene with it. 7 | 8 | You can also just wrap the whole app like so: 9 | 10 | ```jsx 11 | import ReactDOM from "react-dom"; 12 | import React from "react"; 13 | import { Canvas } from "react-three-fiber"; 14 | 15 | ReactDOM.render( 16 | 17 | 18 | , 19 | document.getElementById("root") 20 | ); 21 | ``` 22 | 23 | We are all set up to create our scene component and in there add our first cube. 24 | 25 | To make show any type of object in our scene we need to create a mesh, and that consists of: 26 | 27 | - A geometry, in this case our cube but it can be any geometry from [threejs](https://threejs.org/docs/#api/en/geometries/BoxBufferGeometry) or even a custom model you made in blender or any other software. 28 | - A material, this tells the mesh how this object will be filled. In our case we will just add a simple color that reacts to light. 29 | 30 | We can now create a these components and wrap them in a mesh like so: 31 | 32 | ```jsx 33 | function Scene() { 34 | return ( 35 | 36 | 37 | 38 | 39 | ); 40 | } 41 | ``` 42 | 43 | We have a geometry but it has two issues, it's black and it looks like it's 2D as we can only see one side of it. 44 | 45 | Let's rotate it a bit just so we can see it better: 46 | 47 | ```jsx 48 | function Scene() { 49 | return ( 50 | 51 | 52 | 53 | 54 | ); 55 | } 56 | ``` 57 | 58 | 65 | 66 | In the next chapter we will learn about lights. 67 | -------------------------------------------------------------------------------- /src/docs/first-scene/lights.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding Lights 3 | --- 4 | 5 | A 3D scene will be black unless there is light and in the threejs world there are many types of lights. The most common ones are: 6 | 7 | - AmbientLight - This is a light that is just everywhere, doesn't shine anywhere in particular but just takes all over your scene. 8 | 9 | - Point Light - This is as the name says a light that focuses on point, you can think of it like a lamp. 10 | 11 | Let's start by adding an `ambientLight` in our scene: 12 | 13 | ```jsx 14 | function Scene() { 15 | return ( 16 | <> 17 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | ``` 26 | 27 | Our cube is now green as you can see but since we added an ambient light all the sides are lighted up the same way and it looks too consistent and not relaistic at all. 28 | 29 | Let's also add a point light to make it better: 30 | 31 | ```jsx 32 | function Scene() { 33 | return ( 34 | <> 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | ``` 45 | 46 | We can see that now one side is way more bright than the other because the light hits the cube from the right side since it's positioned `10` on the X axis and our cube by default will be placed at position `0` on all three axis. 47 | 48 | We can see it even better if we remove the `ambientLight` and turn up the intensity and move our light to `0` on the X axis like so: 49 | 50 | ```jsx 51 | 52 | ``` 53 | 54 | 61 | 62 | Let's now go over the last step of our first scene and animate the cube. 63 | -------------------------------------------------------------------------------- /src/components/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Hero from "./Hero"; 3 | import Logo from "./logo"; 4 | import Playground from "./playground"; 5 | import Why from "./why"; 6 | import styled from "styled-components/macro"; 7 | 8 | const Title = styled.div` 9 | background: black; 10 | position: absolute; 11 | color: white; 12 | font-size: 2.4em; 13 | text-transform: uppercase; 14 | font-weight: bold; 15 | margin: 0.4em 0; 16 | padding: 20px; 17 | margin-bottom: 0; 18 | line-height: 0.9; 19 | bottom: 0; 20 | width: 40%; 21 | max-width: 300px; 22 | font-size: 55px; 23 | left: 50%; 24 | transform: translateX(-50%); 25 | 26 | span { 27 | font-size: 1rem; 28 | display: block; 29 | line-height: 1.2; 30 | margin-top: 20px; 31 | font-weight: normal; 32 | } 33 | `; 34 | 35 | export default function Home() { 36 | return ( 37 | <> 38 |
46 |
58 | 59 | 60 | 85 |
86 | 87 | 88 | REACT THREE FIBER<span> is a react renderer for three.js</span> 89 | 90 |
91 |
97 |
104 | 105 | 106 |
107 |
108 | 109 | ); 110 | } 111 | -------------------------------------------------------------------------------- /src/docs/api/canvas.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Canvas 3 | --- 4 | 5 | The `Canvas` object is your portal into Threejs. It renders Threejs elements, _not DOM elements_! Here is a small hello-world that you can try out: 6 | 7 | ```jsx 8 | import ReactDOM from "react-dom"; 9 | import React from "react"; 10 | import { Canvas } from "react-three-fiber"; 11 | 12 | ReactDOM.render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | , 20 | document.getElementById("root") 21 | ); 22 | ``` 23 | 24 | The canvas stretches to 100% of the next relative/absolute parent-container. Make sure your canvas is given space to show contents! 25 | 26 | ```jsx 27 | // Response for pointer clicks that have missed a target 45 | ``` 46 | 47 | You can give it additional properties like style and className, which will be added to the container (a div) that holds the dom-canvas element. 48 | 49 | ### Defaults that the canvas component sets up 50 | 51 | Canvas will create a _translucent WebGL-renderer_ with the following properties: `antialias, alpha, setClearAlpha(0)` 52 | 53 | A default _perspective camera_: `fov: 75, near: 0.1, far: 1000, z: 5, lookAt: [0,0,0]` 54 | 55 | A default _orthographic camera_ if Canvas.orthographic is true: `near: 0.1, far: 1000, z: 5, lookAt: [0,0,0]` 56 | 57 | A default _shadowMap_ if Canvas.shadowMap is true: `type: PCFSoftShadowMap` 58 | 59 | A default _scene_ (into which all the JSX is rendered) and a _raycaster_. 60 | 61 | A _wrapping container_ with a [resize observer](https://github.com/react-spring/react-use-measure): `scroll: true, debounce: { scroll: 50, resize: 0 }` 62 | 63 | You do not have to use any of these objects, look under "Recipes" down below if you want to bring your own. 64 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Code/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Highlight, { defaultProps } from "prism-react-renderer"; 3 | import PropTypes from "prop-types"; 4 | import theme from "prism-react-renderer/themes/dracula"; 5 | import { LiveProvider, LiveEditor } from "react-live"; 6 | import { mdx } from "@mdx-js/react"; 7 | import { Canvas } from "react-three-fiber"; 8 | 9 | import { copyToClipboard } from "@rocketseat/gatsby-theme-docs/src/util/copy-to-clipboard"; 10 | import { 11 | CopyCode, 12 | LineNo, 13 | Pre, 14 | PreHeader, 15 | LiveWrapper, 16 | LivePreview, 17 | LiveError, 18 | StyledEditor, 19 | } from "./styles"; 20 | 21 | export default function CodeHighlight({ 22 | children, 23 | className, 24 | live, 25 | title, 26 | lineNumbers, 27 | }) { 28 | const [copied, setCopied] = useState(false); 29 | const codeString = children.trim(); 30 | const language = className.replace(/language-/, ""); 31 | 32 | if (live) { 33 | return ( 34 | `/** @jsx mdx */${code}`} 39 | scope={{ mdx, Canvas }} 40 | > 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ); 51 | } 52 | 53 | const handleClick = () => { 54 | setCopied(true); 55 | copyToClipboard(codeString); 56 | }; 57 | 58 | return ( 59 | <> 60 | {title && {title}} 61 |
62 | 68 | {({ 69 | className: blockClassName, 70 | style, 71 | tokens, 72 | getLineProps, 73 | getTokenProps, 74 | }) => ( 75 |
 76 |               
 77 |                 {copied ? "Copied!" : "Copy"}
 78 |               
 79 |               
 80 |                 {tokens.map((line, i) => (
 81 |                   
82 | {lineNumbers && {i + 1}} 83 | {line.map((token, key) => ( 84 | 85 | ))} 86 |
87 | ))} 88 |
89 |
90 | )} 91 |
92 |
93 | 94 | ); 95 | } 96 | 97 | CodeHighlight.propTypes = { 98 | children: PropTypes.string.isRequired, 99 | className: PropTypes.string.isRequired, 100 | live: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), 101 | title: PropTypes.string, 102 | lineNumbers: PropTypes.string, 103 | }; 104 | 105 | CodeHighlight.defaultProps = { 106 | live: false, 107 | title: null, 108 | lineNumbers: null, 109 | }; 110 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Sidebar/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { darken, lighten } from "polished"; 3 | 4 | export const Container = styled.aside` 5 | width: 20%; 6 | max-width: 280px; 7 | min-width: 280px; 8 | background-color: #1f1f1f; 9 | 10 | position: fixed; 11 | overflow-y: auto; 12 | left: 0; 13 | 14 | display: flex; 15 | justify-content: space-between; 16 | align-items: center; 17 | flex-direction: column; 18 | 19 | transition: transform 0.5s; 20 | 21 | height: 100vh; 22 | 23 | nav { 24 | width: 100%; 25 | align-self: flex-start; 26 | margin-bottom: 20px; 27 | flex: 1; 28 | } 29 | 30 | footer { 31 | padding: 24px 0 24px 30px; 32 | width: 100%; 33 | 34 | p { 35 | color: ${({ theme }) => theme.colors.sidebar.footer}; 36 | font-size: 12px; 37 | margin: 0; 38 | } 39 | } 40 | 41 | @media (max-width: 780px) { 42 | max-width: 240px; 43 | min-width: 240px; 44 | transform: translate3d( 45 | ${({ isMenuOpen }) => (isMenuOpen ? "0" : "-100%")}, 46 | 0, 47 | 0 48 | ); 49 | } 50 | `; 51 | 52 | export const LogoContainer = styled.div` 53 | width: 100%; 54 | height: 100%; 55 | max-height: 100px; 56 | min-height: 100px; 57 | padding: 20px 0; 58 | 59 | a { 60 | width: 100%; 61 | height: 100%; 62 | padding-left: 30px; 63 | 64 | display: flex; 65 | justify-content: flex-start; 66 | align-items: center; 67 | } 68 | `; 69 | 70 | export const List = styled.ul` 71 | list-style: none; 72 | width: 100%; 73 | padding-left: 0; 74 | 75 | display: flex; 76 | justify-content: flex-start; 77 | align-items: center; 78 | flex-direction: column; 79 | `; 80 | 81 | export const Heading = styled.li` 82 | padding-left: 30px; 83 | width: 100%; 84 | text-transform: uppercase; 85 | font-size: 13px; 86 | font-weight: bold; 87 | margin-top: 20px; 88 | color: #5991c1; 89 | letter-spacing: 0.142em; 90 | `; 91 | 92 | export const Item = styled.li` 93 | font-size: 15px; 94 | width: 100%; 95 | transition: all 200ms ease-in-out; 96 | padding: 0 20px; 97 | 98 | a, 99 | span { 100 | display: block; 101 | font-size: 15px; 102 | color: ${({ theme }) => theme.colors.sidebar.link}; 103 | background-color: #1f1f1f; 104 | padding: 4px 10px; 105 | margin: 4px 0; 106 | border-radius: 4px; 107 | font-weight: normal; 108 | 109 | text-decoration: none; 110 | width: 100%; 111 | height: 100%; 112 | 113 | display: flex; 114 | justify-content: flex-start; 115 | align-items: center; 116 | 117 | cursor: pointer; 118 | margin: 0 auto; 119 | 120 | transition: background-color 0.2s, color 0.2s, padding-left 0.2s; 121 | 122 | svg { 123 | width: 20px; 124 | height: 20px; 125 | margin-right: 10px; 126 | } 127 | 128 | &:not(.active-link):hover { 129 | padding-left: 20px; 130 | color: ${({ theme }) => 131 | darken("0.2", theme.colors.sidebar.link)} !important; 132 | } 133 | 134 | &.active-link { 135 | color: ${({ theme }) => darken("0.2", theme.colors.sidebar.link)}; 136 | background-color: ${lighten("0.1", "#1f1f1f")}; 137 | } 138 | } 139 | `; 140 | 141 | export const SubItem = styled(List)` 142 | margin-top: 5px; 143 | `; 144 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Code/styles.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { css } from "@emotion/core"; 3 | import { darken } from "polished"; 4 | import { 5 | LiveError as AuxLiveError, 6 | LivePreview as AuxLivePreview, 7 | } from "react-live"; 8 | 9 | export const Pre = styled.pre` 10 | text-align: left; 11 | margin: 0 0 16px 0; 12 | box-shadow: 1px 1px 20px rgba(20, 20, 20, 0.27); 13 | padding: 2rem 1rem 1rem 1rem; 14 | overflow: auto; 15 | word-wrap: normal; 16 | border-radius: ${({ hasTitle }) => (hasTitle ? "0 0 3px 3px" : "3px")}; 17 | webkit-overflow-scrolling: touch; 18 | 19 | & .token-line { 20 | line-height: 1.3rem; 21 | height: 1.3rem; 22 | font-size: 15px; 23 | } 24 | `; 25 | 26 | export const LiveWrapper = styled.div` 27 | display: flex; 28 | flex-direction: row; 29 | justify-content: stretch; 30 | align-items: stretch; 31 | border-radius: 3px; 32 | box-shadow: 1px 1px 20px rgba(20, 20, 20, 0.27); 33 | overflow: hidden; 34 | margin-bottom: 32px; 35 | 36 | @media (max-width: 600px) { 37 | flex-direction: column; 38 | } 39 | `; 40 | 41 | const column = css` 42 | flex-basis: 50%; 43 | width: 50%; 44 | max-width: 50%; 45 | 46 | @media (max-width: 600px) { 47 | flex-basis: auto; 48 | width: 100%; 49 | max-width: 100%; 50 | } 51 | `; 52 | 53 | export const StyledEditor = styled.div` 54 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", 55 | "Courier New", monospace; 56 | font-variant: no-common-ligatures no-discretionary-ligatures 57 | no-historical-ligatures no-contextual; 58 | font-size: 16px; 59 | line-height: 1.3rem; 60 | height: 350px; 61 | max-height: 350px; 62 | overflow: auto; 63 | ${column}; 64 | 65 | > div { 66 | height: 100%; 67 | } 68 | 69 | * > textarea:focus { 70 | outline: none; 71 | } 72 | 73 | .token { 74 | font-style: normal !important; 75 | } 76 | `; 77 | 78 | export const LivePreview = styled(AuxLivePreview)` 79 | position: relative; 80 | padding: 0.5rem; 81 | background: white; 82 | color: black; 83 | height: auto; 84 | overflow: hidden; 85 | ${column}; 86 | `; 87 | 88 | export const LiveError = styled(AuxLiveError)` 89 | display: block; 90 | color: rgb(248, 248, 242); 91 | white-space: pre-wrap; 92 | text-align: left; 93 | font-size: 15px; 94 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", 95 | "Courier New", monospace; 96 | font-variant: no-common-ligatures no-discretionary-ligatures 97 | no-historical-ligatures no-contextual; 98 | padding: 0.5rem; 99 | border-radius: 3px; 100 | background: rgb(255, 85, 85); 101 | margin-bottom: 32px; 102 | `; 103 | 104 | export const PreHeader = styled.div` 105 | background-color: ${darken("0.05", "#282a36")}; 106 | color: rgba(248, 248, 242, 0.75); 107 | font-size: 0.75rem; 108 | margin-top: 0.5rem; 109 | padding: 0.8rem 1rem; 110 | border-radius: 3px 3px 0 0; 111 | `; 112 | 113 | export const LineNo = styled.span` 114 | display: inline-block; 115 | width: 2rem; 116 | user-select: none; 117 | opacity: 0.3; 118 | `; 119 | 120 | export const CopyCode = styled.button` 121 | position: absolute; 122 | right: 0.75rem; 123 | top: 0.25rem; 124 | border: 0; 125 | background: none; 126 | border: none; 127 | cursor: pointer; 128 | color: rgb(248, 248, 242); 129 | border-radius: 4px; 130 | margin: 0.25em; 131 | transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1) 0s; 132 | 133 | &:hover { 134 | box-shadow: rgba(46, 41, 51, 0.08) 0px 1px 2px, 135 | rgba(71, 63, 79, 0.08) 0px 2px 4px; 136 | opacity: 0.8; 137 | } 138 | `; 139 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/components/Sidebar/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useStaticQuery, graphql, Link } from "gatsby"; 4 | import { useSidebar } from "./useSidebar"; 5 | 6 | import { 7 | Container, 8 | LogoContainer, 9 | List, 10 | Heading, 11 | Item, 12 | SubItem, 13 | } from "./styles"; 14 | import { isExternalUrl } from "@rocketseat/gatsby-theme-docs/src/util/url"; 15 | import ExternalLink from "./ExternalLink"; 16 | import InternalLink from "./InternalLink"; 17 | import Logo from "../Logo"; 18 | 19 | function ListWithSubItems({ children, text, deep }) { 20 | console.log(deep); 21 | return ( 22 | <> 23 | 33 | {text} 34 | 35 | 44 | {children} 45 | 46 | 47 | ); 48 | } 49 | 50 | export default function Sidebar({ isMenuOpen }) { 51 | const { 52 | site: { 53 | siteMetadata: { footer, basePath }, 54 | }, 55 | } = useStaticQuery(graphql` 56 | { 57 | site { 58 | siteMetadata { 59 | footer 60 | basePath 61 | } 62 | } 63 | } 64 | `); 65 | 66 | const data = useSidebar(); 67 | 68 | function renderLink(link, label) { 69 | return isExternalUrl(link) ? ( 70 | 71 | ) : ( 72 | 73 | ); 74 | } 75 | 76 | return ( 77 | 78 | 79 | 80 | 81 | 82 | 83 | 122 |
123 |

{footer}

124 |
125 |
126 | ); 127 | } 128 | 129 | ListWithSubItems.propTypes = { 130 | children: PropTypes.oneOfType([ 131 | PropTypes.arrayOf(PropTypes.element), 132 | PropTypes.node, 133 | ]).isRequired, 134 | text: PropTypes.string.isRequired, 135 | }; 136 | 137 | Sidebar.propTypes = { 138 | isMenuOpen: PropTypes.bool.isRequired, 139 | }; 140 | -------------------------------------------------------------------------------- /src/components/Home/Hero/Waterpass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple underwater shader 3 | * 4 | 5 | parameters: 6 | tDiffuse: texture 7 | time: this should increase with time passing 8 | distort_speed: how fast you want the distortion effect of water to proceed 9 | distortion: to what degree will the shader distort the screen 10 | centerX: the distortion center X coord 11 | centerY: the distortion center Y coord 12 | 13 | explaination: 14 | the shader is quite simple 15 | it chooses a center and start from there make pixels around it to "swell" then "shrink" then "swell"... 16 | this is of course nothing really similar to underwater scene 17 | but you can combine several this shaders together to create the effect you need... 18 | And yes, this shader could be used for something other than underwater effect, for example, magnifier effect :) 19 | 20 | * @author vergil Wang 21 | */ 22 | 23 | import { Mesh, OrthographicCamera, PlaneBufferGeometry, Scene, ShaderMaterial, UniformsUtils, Vector2 } from 'three' 24 | import { Pass } from 'three/examples/jsm/postprocessing/Pass' 25 | 26 | var WaterShader = { 27 | uniforms: { 28 | byp: { value: 0 }, //apply the glitch ? 29 | texture: { type: 't', value: null }, 30 | time: { type: 'f', value: 0.0 }, 31 | factor: { type: 'f', value: 0.0 }, 32 | resolution: { type: 'v2', value: null } 33 | }, 34 | 35 | vertexShader: `varying vec2 vUv; 36 | void main(){ 37 | vUv = uv; 38 | vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0); 39 | gl_Position = projectionMatrix * modelViewPosition; 40 | }`, 41 | 42 | fragmentShader: `uniform int byp; //should we apply the glitch ? 43 | uniform float time; 44 | uniform float factor; 45 | uniform vec2 resolution; 46 | uniform sampler2D texture; 47 | 48 | varying vec2 vUv; 49 | 50 | void main() { 51 | if (byp<1) { 52 | vec2 uv1 = vUv; 53 | vec2 uv = gl_FragCoord.xy/resolution.xy; 54 | float frequency = 6.0; 55 | float amplitude = 0.015 * factor; 56 | float x = uv1.y * frequency + time * .7; 57 | float y = uv1.x * frequency + time * .3; 58 | uv1.x += cos(x+y) * amplitude * cos(y); 59 | uv1.y += sin(x-y) * amplitude * cos(y); 60 | vec4 rgba = texture2D(texture, uv1); 61 | gl_FragColor = rgba; 62 | } else { 63 | gl_FragColor = texture2D(texture, vUv); 64 | } 65 | }` 66 | } 67 | 68 | var WaterPass = function(dt_size) { 69 | Pass.call(this) 70 | if (WaterShader === undefined) console.error('THREE.WaterPass relies on THREE.WaterShader') 71 | var shader = WaterShader 72 | this.uniforms = UniformsUtils.clone(shader.uniforms) 73 | if (dt_size === undefined) dt_size = 64 74 | this.uniforms['resolution'].value = new Vector2(dt_size, dt_size) 75 | this.material = new ShaderMaterial({ 76 | uniforms: this.uniforms, 77 | vertexShader: shader.vertexShader, 78 | fragmentShader: shader.fragmentShader 79 | }) 80 | this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1) 81 | this.scene = new Scene() 82 | this.quad = new Mesh(new PlaneBufferGeometry(2, 2), null) 83 | this.quad.frustumCulled = false // Avoid getting clipped 84 | this.scene.add(this.quad) 85 | this.factor = 0 86 | this.time = 0 87 | } 88 | 89 | WaterPass.prototype = Object.assign(Object.create(Pass.prototype), { 90 | constructor: WaterPass, 91 | 92 | render: function(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { 93 | const factor = Math.max(0, this.factor) 94 | this.uniforms['byp'].value = factor ? 0 : 1 95 | this.uniforms['texture'].value = readBuffer.texture 96 | this.uniforms['time'].value = this.time 97 | this.uniforms['factor'].value = this.factor 98 | this.time += 0.05 99 | this.quad.material = this.material 100 | if (this.renderToScreen) { 101 | renderer.setRenderTarget(null) 102 | renderer.render(this.scene, this.camera) 103 | } else { 104 | renderer.setRenderTarget(writeBuffer) 105 | if (this.clear) renderer.clear() 106 | renderer.render(this.scene, this.camera) 107 | } 108 | } 109 | }) 110 | 111 | export { WaterPass } 112 | -------------------------------------------------------------------------------- /src/docs/api/objects-and-properties.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Objects and properties 3 | --- 4 | 5 | You can use [Threejs's entire object catalogue and all properties](https://threejs.org/docs). When in doubt, always consult the docs. 6 | 7 | You could lay out an object like this: 8 | 9 | ```jsx 10 | 23 | ``` 24 | 25 | The problem is that all of these properties will always be re-created. Instead, you should define properties declaratively. 26 | 27 | ```jsx 28 | 34 | 35 | 36 | 37 | ``` 38 | 39 | #### Shortcuts (set) 40 | 41 | All properties whose underlying object has a `.set()` method can directly receive the same arguments that `set` would otherwise take. For example [THREE.Color.set](https://threejs.org/docs/index.html#api/en/math/Color.set) can take a color string, so instead of `color={new THREE.Color('hotpink')}` you can simply write `color="hotpink"`. Some `set` methods take multiple arguments, for instance [THREE.Vector3](https://threejs.org/docs/index.html#api/en/math/Vector3.set), give it an array in that case `position={[100, 0, 0]}`. 42 | 43 | #### Dealing with non-Object3D's 44 | 45 | You can put non-Object3D primitives (geometries, materials, etc) into the render tree as well so that they become managed and reactive. They take the same properties they normally would, constructor arguments are passed as an array via `args`. If args change, later on, the object gets re-constructed from scratch! Using the `attach` property objects bind to their parent and are taken off once they unmount. 46 | 47 | You can nest primitive objects, too: 48 | 49 | ```jsx 50 | 51 | 52 | img && (self.needsUpdate = true)} /> 53 | ``` 54 | 55 | Sometimes attaching isn't enough. For example, the following example attaches effects to an array called "passes" of the parent `effectComposer`. Note the use of `attachArray` which adds the object to the target array and takes it out on unmount: 56 | 57 | ```jsx 58 | 59 | 60 | 61 | ``` 62 | 63 | You can also attach to named parent properties using `attachObject={[target, name]}`, which adds the object and takes it out on unmount. The following adds a buffer-attribute to parent.attributes.position. 64 | 65 | ```jsx 66 | 67 | 68 | ``` 69 | 70 | #### Piercing into nested properties 71 | 72 | If you want to reach into nested attributes (for instance: `mesh.rotation.x`), just use dash-case. 73 | 74 | ```jsx 75 | 79 | ``` 80 | 81 | #### Putting already existing objects into the scene-graph 82 | 83 | You can use the `primitive` placeholder for that. You can still give it properties or attach nodes to it. Never add the same object multiples times, this is not allowed in Threejs! 84 | 85 | ```jsx 86 | const mesh = useMemo(() => new THREE.Mesh(), []); 87 | return ; 88 | ``` 89 | 90 | #### Using 3rd-party objects declaratively 91 | 92 | The `extend` function extends three-fibers catalogue of JSX elements. 93 | 94 | ```jsx 95 | import { extend } from 'react-three-fiber' 96 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' 97 | import { TransformControls } from 'three/examples/jsm/controls/TransformControls' 98 | extend({ OrbitControls, TransformControls }) 99 | 100 | // ... 101 | return ( 102 | <> 103 | 104 | 105 | ``` 106 | -------------------------------------------------------------------------------- /src/components/Home/playground.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from "react"; 2 | import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live"; 3 | import { Canvas, useFrame } from "react-three-fiber"; 4 | import "styled-components/macro"; 5 | import { ChromePicker } from "react-color"; 6 | import useClickOutside from "./../useOutsideClick"; 7 | import nightOwl from "prism-react-renderer/themes/nightOwl"; 8 | 9 | const scope = { Canvas, useFrame, useRef }; 10 | 11 | const box = ``; 12 | const sphere = ``; 13 | 14 | const cone = ` `; 15 | const dodecahedron = ``; 16 | const positions = [ 17 | [-2, 0, 0], 18 | [0, 0, 0], 19 | [2, 0, 0], 20 | [-2, 2, 0], 21 | [0, 2, 0], 22 | [2, 2, 0], 23 | [-2, -2, 0], 24 | [0, -2, 0], 25 | [2, -2, 0], 26 | ]; 27 | 28 | const code = (activeGeometry, color, numberOfGeometry) => ` 29 | const Mesh = (props) => { 30 | const mesh = useRef() 31 | 32 | useFrame(() => { 33 | mesh.current.rotation.x = mesh.current.rotation.y += 0.01 34 | }) 35 | 36 | return ( 37 | 38 | ${activeGeometry} 39 | 40 | 41 | ) 42 | } 43 | 44 | render( 45 | 46 | 47 | ${[...Array(parseInt(numberOfGeometry, 10)).keys()] 48 | .map((_, i) => ` \n `) 49 | .join(" ")} 50 | 51 | ) 52 | 53 | `; 54 | 55 | const Playground = () => { 56 | const [activeGeometry, setActiveGeometry] = useState(box); 57 | const [color, setColor] = useState("#ccc"); 58 | const [numberOfGeometry, setNumberOfGeometry] = useState(1); 59 | const [picker, showPicker] = useState(false); 60 | const pickerButton = useRef(); 61 | useClickOutside(pickerButton, () => showPicker(false)); 62 | return ( 63 |
68 |

Try it yourself

69 |

76 | I want to render a{" "} 77 | {" "} 89 | in a{" "} 90 | 96 |

169 | ); 170 | }; 171 | 172 | export default Playground; 173 | -------------------------------------------------------------------------------- /src/components/Home/Hero/index.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three' 2 | import React, { useState, useCallback, useEffect, useRef, useMemo } from 'react' 3 | import { Canvas, extend, useFrame, useThree } from 'react-three-fiber' 4 | import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer' 5 | import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass' 6 | import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass' 7 | import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass' 8 | import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass' 9 | import { GlitchPass } from './Glitchpass' 10 | import { WaterPass } from './Waterpass' 11 | import '../styles.css' 12 | 13 | // Makes these prototypes available as "native" jsx-string elements 14 | extend({ EffectComposer, ShaderPass, RenderPass, WaterPass, UnrealBloomPass, FilmPass, GlitchPass }) 15 | 16 | function Swarm({ count, mouse }) { 17 | const mesh = useRef() 18 | const light = useRef() 19 | const { size, viewport } = useThree() 20 | const aspect = size.width / viewport.width 21 | 22 | const dummy = useMemo(() => new THREE.Object3D(), []) 23 | // Generate some random positions, speed factors and timings 24 | const particles = useMemo(() => { 25 | const temp = [] 26 | for (let i = 0; i < count; i++) { 27 | const t = Math.random() * 100 28 | const factor = 20 + Math.random() * 100 29 | const speed = 0.01 + Math.random() / 200 30 | const xFactor = -50 + Math.random() * 100 31 | const yFactor = -50 + Math.random() * 100 32 | const zFactor = -50 + Math.random() * 100 33 | temp.push({ t, factor, speed, xFactor, yFactor, zFactor, mx: 0, my: 0 }) 34 | } 35 | return temp 36 | }, [count]) 37 | // The innards of this hook will run every frame 38 | useFrame((state) => { 39 | // Makes the light follow the mouse 40 | light.current.position.set(mouse.current[0] / aspect, -mouse.current[1] / aspect, 0) 41 | // Run through the randomized data to calculate some movement 42 | particles.forEach((particle, i) => { 43 | let { t, factor, speed, xFactor, yFactor, zFactor } = particle 44 | // There is no sense or reason to any of this, just messing around with trigonometric functions 45 | t = particle.t += speed / 2 46 | const a = Math.cos(t) + Math.sin(t * 1) / 10 47 | const b = Math.sin(t) + Math.cos(t * 2) / 10 48 | const s = Math.cos(t) 49 | particle.mx += (mouse.current[0] - particle.mx) * 0.01 50 | particle.my += (mouse.current[1] * -1 - particle.my) * 0.01 51 | // Update the dummy object 52 | dummy.position.set( 53 | (particle.mx / 10) * a + xFactor + Math.cos((t / 10) * factor) + (Math.sin(t * 1) * factor) / 10, 54 | (particle.my / 10) * b + yFactor + Math.sin((t / 10) * factor) + (Math.cos(t * 2) * factor) / 10, 55 | (particle.my / 10) * b + zFactor + Math.cos((t / 10) * factor) + (Math.sin(t * 3) * factor) / 10 56 | ) 57 | dummy.scale.set(s, s, s) 58 | dummy.rotation.set(s * 5, s * 5, s * 5) 59 | dummy.updateMatrix() 60 | // And apply the matrix to the instanced item 61 | mesh.current.setMatrixAt(i, dummy.matrix) 62 | }) 63 | mesh.current.instanceMatrix.needsUpdate = true 64 | }) 65 | return ( 66 | <> 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ) 79 | } 80 | 81 | function Effect({ down }) { 82 | const composer = useRef() 83 | const { scene, gl, size, camera } = useThree() 84 | const aspect = useMemo(() => new THREE.Vector2(size.width, size.height), [size]) 85 | useEffect(() => void composer.current.setSize(size.width, size.height), [size]) 86 | useFrame(() => composer.current.render(), 1) 87 | return ( 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | ) 96 | } 97 | 98 | export default function App() { 99 | const [down, set] = useState(false) 100 | const mouse = useRef([300, -200]) 101 | const onMouseMove = useCallback(({ clientX: x, clientY: y }) => (mouse.current = [x - window.innerWidth / 2, y - window.innerHeight / 2]), []) 102 | return ( 103 | set(false)} onMouseDown={() => set(true)}> 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | ) 114 | } 115 | -------------------------------------------------------------------------------- /src/docs/recipes/reducing-bundle-size.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reducing bundle-size 3 | --- 4 | 5 | Threejs is quite heavy and tree-shaking doesn't yet yield the results you would hope for atm. But you can always create your own export-file and alias "three" towards it. This way you can reduce it to 80-50kb, or perhaps less, depending on what you need. 6 | 7 | three-exports.js 8 | 9 | ```js 10 | // Only export the things that are actually needed, cut out everything else 11 | 12 | export { WebGLRenderer } from "three/src/renderers/WebGLRenderer.js"; 13 | export { ShaderLib } from "three/src/renderers/shaders/ShaderLib.js"; 14 | export { UniformsLib } from "three/src/renderers/shaders/UniformsLib.js"; 15 | export { UniformsUtils } from "three/src/renderers/shaders/UniformsUtils.js"; 16 | export { ShaderChunk } from "three/src/renderers/shaders/ShaderChunk.js"; 17 | export { Scene } from "three/src/scenes/Scene.js"; 18 | export { Mesh } from "three/src/objects/Mesh.js"; 19 | export { LineSegments } from "three/src/objects/LineSegments.js"; 20 | export { Line } from "three/src/objects/Line.js"; 21 | export { CubeTexture } from "three/src/textures/CubeTexture.js"; 22 | export { CanvasTexture } from "three/src/textures/CanvasTexture.js"; 23 | export { Group } from "three/src/objects/Group.js"; 24 | export { 25 | SphereGeometry, 26 | SphereBufferGeometry, 27 | } from "three/src/geometries/SphereGeometry.js"; 28 | export { 29 | PlaneGeometry, 30 | PlaneBufferGeometry, 31 | } from "three/src/geometries/PlaneGeometry.js"; 32 | export { 33 | BoxGeometry, 34 | BoxBufferGeometry, 35 | } from "three/src/geometries/BoxGeometry.js"; 36 | export { 37 | ConeGeometry, 38 | ConeBufferGeometry, 39 | } from "three/src/geometries/ConeGeometry.js"; 40 | export { 41 | CylinderGeometry, 42 | CylinderBufferGeometry, 43 | } from "three/src/geometries/CylinderGeometry.js"; 44 | export { 45 | CircleGeometry, 46 | CircleBufferGeometry, 47 | } from "three/src/geometries/CircleGeometry.js"; 48 | export { 49 | RingGeometry, 50 | RingBufferGeometry, 51 | } from "three/src/geometries/RingGeometry.js"; 52 | export { EdgesGeometry } from "three/src/geometries/EdgesGeometry.js"; 53 | export { Material } from "three/src/materials/Material.js"; 54 | export { MeshPhongMaterial } from "three/src/materials/MeshPhongMaterial.js"; 55 | export { MeshPhysicalMaterial } from "three/src/materials/MeshPhysicalMaterial.js"; 56 | export { MeshBasicMaterial } from "three/src/materials/MeshBasicMaterial.js"; 57 | export { LineDashedMaterial } from "three/src/materials/LineDashedMaterial.js"; 58 | export { SpriteMaterial } from "three/src/materials/SpriteMaterial.js"; 59 | export { LineBasicMaterial } from "three/src/materials/LineBasicMaterial.js"; 60 | export { TextureLoader } from "three/src/loaders/TextureLoader.js"; 61 | export { Texture } from "three/src/textures/Texture.js"; 62 | export { Sprite } from "three/src/objects/Sprite.js"; 63 | export { SpotLightShadow } from "three/src/lights/SpotLightShadow.js"; 64 | export { SpotLight } from "three/src/lights/SpotLight.js"; 65 | export { SpotLightHelper } from "three/src/helpers/SpotLightHelper.js"; 66 | export { CameraHelper } from "three/src/helpers/CameraHelper.js"; 67 | export { PointLight } from "three/src/lights/PointLight.js"; 68 | export { DirectionalLight } from "three/src/lights/DirectionalLight.js"; 69 | export { AmbientLight } from "three/src/lights/AmbientLight.js"; 70 | export { LightShadow } from "three/src/lights/LightShadow.js"; 71 | export { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera.js"; 72 | export { OrthographicCamera } from "three/src/cameras/OrthographicCamera.js"; 73 | export { BufferGeometry } from "three/src/core/BufferGeometry.js"; 74 | export { Geometry } from "three/src/core/Geometry.js"; 75 | export * from "three/src/core/BufferAttribute.js"; 76 | export { Face3 } from "three/src/core/Face3.js"; 77 | export { Object3D } from "three/src/core/Object3D.js"; 78 | export { Raycaster } from "three/src/core/Raycaster.js"; 79 | export { Triangle } from "three/src/math/Triangle.js"; 80 | export { _Math as Math } from "three/src/math/Math.js"; 81 | export { Spherical } from "three/src/math/Spherical.js"; 82 | export { Cylindrical } from "three/src/math/Cylindrical.js"; 83 | export { Plane } from "three/src/math/Plane.js"; 84 | export { Frustum } from "three/src/math/Frustum.js"; 85 | export { Sphere } from "three/src/math/Sphere.js"; 86 | export { Ray } from "three/src/math/Ray.js"; 87 | export { Matrix4 } from "three/src/math/Matrix4.js"; 88 | export { Matrix3 } from "three/src/math/Matrix3.js"; 89 | export { Box3 } from "three/src/math/Box3.js"; 90 | export { Box2 } from "three/src/math/Box2.js"; 91 | export { Line3 } from "three/src/math/Line3.js"; 92 | export { Euler } from "three/src/math/Euler.js"; 93 | export { Vector4 } from "three/src/math/Vector4.js"; 94 | export { Vector3 } from "three/src/math/Vector3.js"; 95 | export { Vector2 } from "three/src/math/Vector2.js"; 96 | export { Quaternion } from "three/src/math/Quaternion.js"; 97 | export { Color } from "three/src/math/Color.js"; 98 | export { GridHelper } from "three/src/helpers/GridHelper.js"; 99 | export { AxesHelper } from "three/src/helpers/AxesHelper.js"; 100 | export * from "three/src/constants.js"; 101 | export { InstancedBufferGeometry } from "three/src/core/InstancedBufferGeometry.js"; 102 | export { InstancedInterleavedBuffer } from "three/src/core/InstancedInterleavedBuffer.js"; 103 | export { InterleavedBufferAttribute } from "three/src/core/InterleavedBufferAttribute.js"; 104 | export { ShaderMaterial } from "three/src/materials/ShaderMaterial.js"; 105 | export { WireframeGeometry } from "three/src/geometries/WireframeGeometry.js"; 106 | ``` 107 | 108 | webpack.config.js 109 | 110 | ```js 111 | module.exports = { 112 | resolve: { 113 | alias: { 114 | // Forward all three imports to our exports file 115 | three$: path.resolve("./build/three-exports.js"), 116 | }, 117 | }, 118 | }; 119 | ``` 120 | -------------------------------------------------------------------------------- /src/components/Home/Hero/Glitchpass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | /** 6 | * @author felixturner / http://airtight.cc/ 7 | * 8 | * RGB Shift Shader 9 | * Shifts red and blue channels from center in opposite directions 10 | * Ported from http://kriss.cx/tom/2009/05/rgb-shift/ 11 | * by Tom Butterworth / http://kriss.cx/tom/ 12 | * 13 | * amount: shift distance (1 is width of input) 14 | * angle: shift angle in radians 15 | */ 16 | 17 | import { DataTexture, FloatType, Math as _Math, Mesh, OrthographicCamera, PlaneBufferGeometry, RGBFormat, Scene, ShaderMaterial, UniformsUtils } from 'three' 18 | import { Pass } from 'three/examples/jsm/postprocessing/Pass.js' 19 | 20 | var DigitalGlitch = { 21 | uniforms: { 22 | tDiffuse: { value: null }, //diffuse texture 23 | tDisp: { value: null }, //displacement texture for digital glitch squares 24 | byp: { value: 0 }, //apply the glitch ? 25 | amount: { value: 0.08 }, 26 | angle: { value: 0.02 }, 27 | seed: { value: 0.02 }, 28 | seed_x: { value: 0.02 }, //-1,1 29 | seed_y: { value: 0.02 }, //-1,1 30 | distortion_x: { value: 0.5 }, 31 | distortion_y: { value: 0.6 }, 32 | col_s: { value: 0.05 } 33 | }, 34 | 35 | vertexShader: `varying vec2 vUv; 36 | void main() { 37 | vUv = uv; 38 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 39 | }`, 40 | 41 | fragmentShader: `uniform int byp; //should we apply the glitch 42 | uniform sampler2D tDiffuse; 43 | uniform sampler2D tDisp; 44 | uniform float amount; 45 | uniform float angle; 46 | uniform float seed; 47 | uniform float seed_x; 48 | uniform float seed_y; 49 | uniform float distortion_x; 50 | uniform float distortion_y; 51 | uniform float col_s; 52 | varying vec2 vUv; 53 | float rand(vec2 co){ 54 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 55 | } 56 | void main() { 57 | if(byp<1) { 58 | vec2 p = vUv; 59 | float xs = floor(gl_FragCoord.x / 0.5); 60 | float ys = floor(gl_FragCoord.y / 0.5); 61 | //based on staffantans glitch shader for unity https://github.com/staffantan/unityglitch 62 | vec4 normal = texture2D (tDisp, p*seed*seed); 63 | if(p.ydistortion_x-col_s*seed) { 64 | if(seed_x>0.){ 65 | p.y = 1. - (p.y + distortion_y); 66 | } 67 | else { 68 | p.y = distortion_y; 69 | } 70 | } 71 | p.x+=normal.x*seed_x*(seed/5.); 72 | p.y+=normal.y*seed_y*(seed/5.); 73 | //base from RGB shift shader 74 | vec2 offset = amount * vec2( cos(angle), sin(angle)); 75 | vec4 cr = texture2D(tDiffuse, p + offset); 76 | vec4 cga = texture2D(tDiffuse, p); 77 | vec4 cb = texture2D(tDiffuse, p - offset); 78 | gl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a); 79 | //add noise 80 | vec4 snow = 200.*amount*vec4(rand(vec2(xs * seed,ys * seed*50.))*0.05); 81 | gl_FragColor = gl_FragColor+ snow; 82 | } 83 | else { 84 | gl_FragColor=texture2D (tDiffuse, vUv); 85 | } 86 | }` 87 | } 88 | 89 | var GlitchPass = function(dt_size) { 90 | Pass.call(this) 91 | if (DigitalGlitch === undefined) console.error('THREE.GlitchPass relies on THREE.DigitalGlitch') 92 | var shader = DigitalGlitch 93 | this.uniforms = UniformsUtils.clone(shader.uniforms) 94 | if (dt_size === undefined) dt_size = 64 95 | this.uniforms['tDisp'].value = this.generateHeightmap(dt_size) 96 | this.material = new ShaderMaterial({ 97 | uniforms: this.uniforms, 98 | vertexShader: shader.vertexShader, 99 | fragmentShader: shader.fragmentShader 100 | }) 101 | this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1) 102 | this.scene = new Scene() 103 | this.quad = new Mesh(new PlaneBufferGeometry(2, 2), null) 104 | this.quad.frustumCulled = false // Avoid getting clipped 105 | this.scene.add(this.quad) 106 | this.factor = 0 107 | } 108 | 109 | GlitchPass.prototype = Object.assign(Object.create(Pass.prototype), { 110 | constructor: GlitchPass, 111 | 112 | render: function(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { 113 | const factor = Math.max(0, this.factor) 114 | this.uniforms['tDiffuse'].value = readBuffer.texture 115 | this.uniforms['seed'].value = Math.random() * factor //default seeding 116 | this.uniforms['byp'].value = 0 117 | if (factor) { 118 | this.uniforms['amount'].value = (Math.random() / 90) * factor 119 | this.uniforms['angle'].value = _Math.randFloat(-Math.PI, Math.PI) * factor 120 | this.uniforms['distortion_x'].value = _Math.randFloat(0, 1) * factor 121 | this.uniforms['distortion_y'].value = _Math.randFloat(0, 1) * factor 122 | this.uniforms['seed_x'].value = _Math.randFloat(-0.3, 0.3) * factor 123 | this.uniforms['seed_y'].value = _Math.randFloat(-0.3, 0.3) * factor 124 | } else this.uniforms['byp'].value = 1 125 | this.quad.material = this.material 126 | if (this.renderToScreen) { 127 | renderer.setRenderTarget(null) 128 | renderer.render(this.scene, this.camera) 129 | } else { 130 | renderer.setRenderTarget(writeBuffer) 131 | if (this.clear) renderer.clear() 132 | renderer.render(this.scene, this.camera) 133 | } 134 | }, 135 | 136 | generateHeightmap: function(dt_size) { 137 | var data_arr = new Float32Array(dt_size * dt_size * 3) 138 | var length = dt_size * dt_size 139 | 140 | for (var i = 0; i < length; i++) { 141 | var val = _Math.randFloat(0, 1) 142 | data_arr[i * 3 + 0] = val 143 | data_arr[i * 3 + 1] = val 144 | data_arr[i * 3 + 2] = val 145 | } 146 | 147 | var texture = new DataTexture(data_arr, dt_size, dt_size, RGBFormat, FloatType) 148 | texture.needsUpdate = true 149 | return texture 150 | } 151 | }) 152 | 153 | export { GlitchPass } 154 | -------------------------------------------------------------------------------- /src/@rocketseat/gatsby-theme-docs/styles/global.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Global, css } from "@emotion/core"; 3 | import { useTheme } from "emotion-theming"; 4 | import { lighten } from "polished"; 5 | 6 | export default function GlobalStyle() { 7 | const theme = useTheme(); 8 | 9 | return ( 10 | 277 | ); 278 | } 279 | --------------------------------------------------------------------------------