Skip to content

Commit

Permalink
improve webgl overall
Browse files Browse the repository at this point in the history
  • Loading branch information
clementroche committed Nov 8, 2024
1 parent d4da350 commit cca7194
Show file tree
Hide file tree
Showing 20 changed files with 525 additions and 218 deletions.
2 changes: 2 additions & 0 deletions app/(pages)/(components)/wrapper/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client'

import cn from 'clsx'
import { Canvas } from 'libs/webgl/components/canvas'
import { Footer } from '../footer'
Expand Down
3 changes: 3 additions & 0 deletions app/debug/orchestra/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ function OrchestraPage({}) {
<OrchestraToggle id="minimap" className={s.button}>
🗺️
</OrchestraToggle>
<OrchestraToggle id="webgl" className={s.button}>
🧊
</OrchestraToggle>
</div>
)
}
Expand Down
5 changes: 4 additions & 1 deletion hooks/use-device-detection.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { useMediaQuery } from '@darkroom.engineering/hamo'
import { useOrchestra } from 'libs/orchestra/react'
import variables from 'styles/config.js'

export function useDeviceDetection() {
const breakpoint = variables.breakpoints.mobile.replace('px', '')

const { webgl } = useOrchestra()

const isMobile = useMediaQuery(`(max-width: ${breakpoint - 1}px)`)
const isDesktop = useMediaQuery(`(min-width: ${breakpoint}px)`)
const isReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
const isWebGL = isDesktop && !isReducedMotion
const isWebGL = isDesktop && !isReducedMotion && webgl
// TODO: const isLowPowerMode

return { isMobile, isDesktop, isReducedMotion, isWebGL }
Expand Down
15 changes: 15 additions & 0 deletions hooks/use-framerate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useFrame } from '@darkroom.engineering/hamo'
import { useRef } from 'react'

export function useFramerate(fps, callback) {
const timeRef = useRef(0)

useFrame((time, delaTime) => {
timeRef.current += delaTime

if (timeRef.current > 1000 / (typeof fps === 'function' ? fps() : fps)) {
timeRef.current = 0
callback?.(time, delaTime)
}
})
}
6 changes: 5 additions & 1 deletion hooks/use-lazy-state.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef } from 'react'

export function useLazyState(initialValue, callback) {
export function useLazyState(initialValue, callback, deps = []) {
const stateRef = useRef(initialValue)

useEffect(() => {
Expand All @@ -23,5 +23,9 @@ export function useLazyState(initialValue, callback) {

const get = useCallback(() => stateRef.current, [])

useEffect(() => {
callback(stateRef.current, stateRef.current)
}, [...deps])

return [get, set]
}
85 changes: 48 additions & 37 deletions hooks/use-scroll-trigger.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,31 @@ import { useTransform } from 'hooks/use-transform'
import { useLenis } from 'lenis/react'
import { clamp, mapRange } from 'libs/maths'
import { useMinimap } from 'libs/orchestra/minimap'
import { useCallback, useEffect, useLayoutEffect, useRef } from 'react'
import { useOrchestra } from 'libs/orchestra/react'
import { useCallback, useEffect, useRef } from 'react'
import { useLazyState } from './use-lazy-state'

// @refresh reset

function useMarker({
text = 'start',
color = 'green',
type = 'start',
fixed = false,
visible = false,
id = '',
} = {}) {
const elementRef = useRef()

const color = type === 'start' ? 'green' : 'red'
const text = type === 'start' ? 'start' : 'end'

const setElementRef = useMinimap({
color,
})

useLayoutEffect(() => {
if (process.env.NODE_ENV !== 'development') return
const { minimap } = useOrchestra()

useEffect(() => {
if (!minimap) return

if (!visible) return

Expand All @@ -52,6 +56,7 @@ function useMarker({
innerElement.style.cssText = `
position: absolute;
padding: 8px;
${type === 'start' ? 'left' : 'right'}: 0;
`
element.appendChild(innerElement)

Expand All @@ -66,7 +71,7 @@ function useMarker({
// setElementRef?.(null)
elementRef.current.remove()
}
}, [color, text, fixed, id, visible, type, setElementRef])
}, [color, text, fixed, id, visible, type, setElementRef, minimap])

const top = useCallback(
(value) => {
Expand Down Expand Up @@ -122,32 +127,24 @@ export function useScrollTrigger(

const elementMarkerStart = useMarker({
id,
text: 'start',
color: 'green',
type: 'start',
visible: markers,
})
const elementMarkerEnd = useMarker({
id,
text: 'end',
color: 'red',
type: 'end',
visible: markers,
})

const viewportMarkerStart = useMarker({
id,
text: 'start',
color: 'green',
type: 'end',
type: 'start',
fixed: true,
visible: markers,
})
const viewportMarkerEnd = useMarker({
id,
text: 'end',
color: 'red',
type: 'start',
type: 'end',
fixed: true,
visible: markers,
})
Expand Down Expand Up @@ -194,40 +191,54 @@ export function useScrollTrigger(
const startValue = elementStart - viewportStart
const endValue = elementEnd - viewportEnd

// eslint-disable-next-line no-unused-vars
const [getProgress, setProgress] = useLazyState(
undefined,
const onUpdate = useCallback(
(progress, lastProgress) => {
if (isNaN(progress)) return

if (
(progress >= 0 && lastProgress < 0) ||
(progress <= 1 && lastProgress > 1)
) {
onEnter?.()
}

if (
(progress < 0 && lastProgress >= 0) ||
(progress > 1 && lastProgress <= 1)
) {
onLeave?.()
}

if (clamp(0, progress, 1) === clamp(0, lastProgress, 1)) return
// if (
// (progress >= 0 && lastProgress < 0) ||
// (progress <= 1 && lastProgress > 1)
// ) {
// onEnter?.(progress)
// }

// if (
// (progress < 0 && lastProgress >= 0) ||
// (progress > 1 && lastProgress <= 1)
// ) {
// onLeave?.(progress)
// }

onProgress?.({
height: endValue - startValue,
isActive: progress >= 0 && progress <= 1,
progress: clamp(0, progress, 1),
lastProgress: lastProgress,
steps: Array.from({ length: steps }).map((_, i) =>
clamp(0, mapRange(i / steps, (i + 1) / steps, progress, 0, 1), 1),
),
})
},
[steps, startValue, endValue],
[endValue, startValue, steps, onProgress, onEnter, onLeave],
)

// eslint-disable-next-line no-unused-vars
const [getProgress, setProgress] = useLazyState(
undefined,
(progress, lastProgress) => {
if (isNaN(progress)) return
if (clamp(0, progress, 1) === clamp(0, lastProgress, 1)) return

onUpdate(progress, lastProgress)
},
[endValue, startValue, steps, onUpdate],
)

useEffect(() => {
const progress = getProgress()
if (isNaN(progress)) return

onUpdate(progress, progress)
}, [getProgress, onUpdate, ...deps])

const update = useCallback(
() => {
if (disabled) return
Expand Down
23 changes: 22 additions & 1 deletion hooks/use-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const DEFAULT_TRANSFORM = {
bottom: 0,
left: 0,
},
userData: {},
}

export const TransformContext = createContext({
Expand Down Expand Up @@ -63,6 +64,8 @@ export const TransformProvider = forwardRef(function TransformProvider(
transform.scale.y *= transformRef.current.scale.y
transform.scale.z *= transformRef.current.scale.z

transform.userData = { ...transformRef.current.userData }

return transform
}, [])

Expand Down Expand Up @@ -127,6 +130,14 @@ export const TransformProvider = forwardRef(function TransformProvider(
[update],
)

const setUserData = useCallback(
(key, value) => {
transformRef.current.userData[key] = value
update()
},
[update],
)

useTransform(
(transform) => {
parentTransformRef.current = structuredClone(transform)
Expand All @@ -144,11 +155,21 @@ export const TransformProvider = forwardRef(function TransformProvider(
setRotate,
setScale,
setClip,
setUserData,
}))

return (
<TransformContext.Provider
value={{ getTransform, addCallback, removeCallback }}
value={{
getTransform,
addCallback,
removeCallback,
setTranslate,
setRotate,
setScale,
setClip,
setUserData,
}}
>
{children}
</TransformContext.Provider>
Expand Down
6 changes: 4 additions & 2 deletions libs/orchestra/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { createStore } from 'zustand/vanilla'
const storageKey = 'orchestra'
const store = createStore(
persist(
subscribeWithSelector(() => ({})),
subscribeWithSelector(() => ({
webgl: true,
})),
{
name: storageKey,
storage: createJSONStorage(() => localStorage),
Expand Down Expand Up @@ -59,7 +61,7 @@ class Toggle {
class Orchestra {
constructor() {
this.domElement = document.createElement('div')

this.store = store
this.toggles = []
}

Expand Down
3 changes: 3 additions & 0 deletions libs/orchestra/minimap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ function Marker({ element, color }) {

useFrame(() => {
if (!element) return

if (!markerRef.current) return

// console.log(element)
const rect = element.getBoundingClientRect()
const top = rect.top / window.innerHeight
Expand Down
13 changes: 9 additions & 4 deletions libs/webgl/components/canvas/webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import { OrthographicCamera } from '@react-three/drei'
import { Canvas } from '@react-three/fiber'
import { SheetProvider } from 'libs/theatre'
import { FlowmapProvider } from '../flowmap'
import { PostProcessing } from '../postprocessing'
import { Preload } from '../preload'
import { RAF } from '../raf'
import { useCanvas } from './'
import s from './webgl.module.scss'

export function WebGLCanvas({ render = true, ...props }) {
export function WebGLCanvas({
render = true,
postprocessing = true,
...props
}) {
const { WebGLTunnel, DOMTunnel } = useCanvas()

return (
Expand All @@ -18,8 +23,7 @@ export function WebGLCanvas({ render = true, ...props }) {
powerPreference: 'high-performance',
antialias: true,
alpha: true,
// stencil: false,
// depth: false,
...(postprocessing && { stencil: false, depth: false }),
}}
dpr={[1, 2]}
orthographic
Expand All @@ -29,6 +33,7 @@ export function WebGLCanvas({ render = true, ...props }) {
flat
eventSource={document.documentElement}
eventPrefix="client"
resize={{ scroll: false, debounce: { resize: 500 } }}
>
{/* <StateListener onChange={onChange} /> */}
<SheetProvider id="webgl">
Expand All @@ -41,7 +46,7 @@ export function WebGLCanvas({ render = true, ...props }) {
/>
<RAF render={render} />
<FlowmapProvider>
{/* <PostProcessing /> */}
<PostProcessing />
<WebGLTunnel.Out />
</FlowmapProvider>
<Preload />
Expand Down
2 changes: 1 addition & 1 deletion libs/webgl/components/flowmap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function useFlowmap() {
}

export function FlowmapProvider({ children }) {
const gl = useThree(({ gl }) => gl)
const gl = useThree((state) => state.gl)

const fluidSimulation = useMemo(
() => new FluidSimulation({ renderer: gl, size: 128 }),
Expand Down
Loading

1 comment on commit cca7194

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡️ Lighthouse report for the changes in this commit:

🟠 Performance: 70
🟢 Accessibility: 90
🟢 Best practices: 96
🟠 SEO: 63

Lighthouse ran on https://satus-3vfuzerbx-darkroom-engineering.vercel.app/

Please sign in to comment.