Skip to content

Instantly share code, notes, and snippets.

@netgfx
Created June 28, 2022 11:36
Show Gist options
  • Save netgfx/3d9abe32bdbc9afc82978cd79df1dde4 to your computer and use it in GitHub Desktop.
Save netgfx/3d9abe32bdbc9afc82978cd79df1dde4 to your computer and use it in GitHub Desktop.
Framer ARScene
// Welcome to Code in Framer
// Get Started: https://www.framer.com/docs/guides/
import { forwardRef, Suspense, useEffect, useRef } from "react"
import { ARCanvas, DefaultXRControllers } from "@react-three/xr"
import { useLoader } from "@react-three/fiber"
import { Object3D } from "three"
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
import throttle from "lodash.throttle"
const INITIAL_SCALE = 20
const Model = forwardRef<Object3D, any>((props: any, ref) => {
const gltf = useLoader(
GLTFLoader,
"https://qsxfdqhsuyovskknxkaj.supabase.co/storage/v1/object/public/threed/models/Chest.glb"
)
return (
<primitive
ref={ref}
position={[0, -10, -20]}
object={gltf.scene}
scale={INITIAL_SCALE}
{...props}
/>
)
})
/**
* These annotations control how your component sizes
* Learn more: https://www.framer.com/docs/guides/auto-sizing
*
* @framerSupportedLayoutWidth fixed
* @framerSupportedLayoutHeight fixed
*/
export default function ARScene(props) {
const modelRef = useRef<Object3D | null>(null)
useEffect(() => {
// for swipe gesture
let startX = 0
let startRotationX = 0
// for pinch gesture
let initialDistance = 0
let initialScale = INITIAL_SCALE
const handleTouchStart = (event: TouchEvent) => {
if (event.targetTouches.length === 2) {
/* store last scale value and initial distance between 2 fingers */
initialDistance = Math.abs(
event.targetTouches[1].pageX - event.targetTouches[0].pageX
)
initialScale = modelRef.current?.scale.x || INITIAL_SCALE
} else {
/* store last rotation value */
const touchX = event.changedTouches[0].pageX
startX = touchX
startRotationX = modelRef.current?.rotation.y || 0
}
}
const handleTouchMove = (event: TouchEvent) => {
if (modelRef.current) {
if (event.changedTouches.length === 2) {
/* 2 touches move -> scale object */
const currDistance = Math.abs(
event.changedTouches[1].pageX -
event.changedTouches[0].pageX
)
const diffDistance = currDistance - initialDistance
const diffDistanceScale = diffDistance / 10
modelRef.current.scale.setX(
initialScale + diffDistanceScale
)
modelRef.current.scale.setY(
initialScale + diffDistanceScale
)
modelRef.current.scale.setZ(
initialScale + diffDistanceScale
)
} else {
/* 1 touch move -> rotate object on Y axis */
const touchX = event.changedTouches[0].pageX
const diffX = (touchX - startX) / window.innerWidth
modelRef.current.rotation.y = startRotationX + diffX
}
}
}
const throttledTouchStart = throttle(handleTouchStart, 100)
const throttledTouchMove = throttle(handleTouchMove, 100)
window.addEventListener("touchstart", throttledTouchStart)
window.addEventListener("touchmove", throttledTouchMove)
return () => {
window.removeEventListener("touchstart", throttledTouchStart)
window.removeEventListener("touchmove", throttledTouchMove)
}
}, [])
return (
<div style={containerStyle}>
<ARCanvas>
<Suspense fallback={null}>
<Model ref={modelRef} />
</Suspense>
<pointLight position={[10, 10, 10]} />
<DefaultXRControllers />
</ARCanvas>
</div>
)
}
// Styles are written in object syntax
// Learn more: https://reactjs.org/docs/dom-elements.html#style
const containerStyle = {
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
overflow: "hidden",
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment