Created
January 22, 2024 04:26
-
-
Save ejfox/12bfcdaa3b09805f410ac0c1439b8a25 to your computer and use it in GitHub Desktop.
Using anime, tresjs, and canvas to make rotating annotated objects in 3D
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup> | |
import { animate, createAnimatable, stagger } from '~/anime.esm.min.js' | |
import { TresCanvas, useLoader, extend } from '@tresjs/core' | |
import { createNoise3D } from 'simplex-noise'; | |
import { EffectComposer, DepthOfField, Glitch, Noise, Outline, Pixelation, Vignette } from '@tresjs/post-processing' | |
import { Html, Line2 } from '@tresjs/cientos' | |
import * as THREE from 'three' | |
const { pressed } = useMousePressed() | |
const { x: mouseX, y: mouseY } = useMouse() | |
const originalSphereGroupRotation = { x: 0, y: 0, z: 0 } | |
const sphereGroupRotation = ref({ x: 0, y: 0, z: 0 }) | |
const orbitGroupRotation = ref({ x: 0, y: 0, z: 0 }) | |
const animSphereGroupRotation = createAnimatable(sphereGroupRotation.value, { | |
x: 200, | |
y: 0, | |
z: 0, | |
duration: 1000, | |
ease: 'out(10)' | |
}) | |
const { onLoop } = useRenderLoop() | |
const { width, height } = useWindowSize() | |
const mult = 0.001 | |
onLoop(() => { | |
// slowly orbit the orbit group around the center | |
orbitGroupRotation.value.y += 0.0075 | |
// with animation | |
if (pressed.value) { | |
const chgAmtX = (mouseY.value - (width.value / 2)) * mult | |
const chgAmtY = (mouseX.value - (height.value / 2)) * mult | |
animSphereGroupRotation.x.to(chgAmtX) | |
animSphereGroupRotation.y.to(chgAmtY) | |
} else { | |
const multMult = 125 | |
if (!animSphereGroupRotation) return | |
// nudge the rotation back to the original rotation | |
const currentX = sphereGroupRotation.value.x | |
const currentY = sphereGroupRotation.value.y | |
const currentZ = sphereGroupRotation.value.z | |
const targetX = originalSphereGroupRotation.x | |
const targetY = originalSphereGroupRotation.y | |
const targetZ = originalSphereGroupRotation.z | |
const diffX = targetX - currentX | |
const diffY = targetY - currentY | |
const diffZ = targetZ - currentZ | |
const chgAmtX = diffX * (mult * multMult) | |
const chgAmtY = diffY * (mult * multMult) | |
const chgAmtZ = diffZ * (mult * multMult) | |
animSphereGroupRotation.x.to(currentX + chgAmtX) | |
animSphereGroupRotation.y.to(currentY + chgAmtY) | |
animSphereGroupRotation.z.to(currentZ + chgAmtZ) | |
} | |
// draw circles on the canvas | |
if (annotationCtx.value) { | |
drawText(`x: ${sphereGroupRotation.value.x.toFixed(2)}, y: ${sphereGroupRotation.value.y.toFixed(2)}`) | |
} | |
}) | |
const annotation = ref(null) // the canvas we will draw the annotation text in | |
const annotationCtx = ref(null) // the canvas context | |
const annotationTexture = ref(null) // the annotation texture we will apply to the plane | |
function drawText(textString) { | |
if (!annotationCtx.value) return | |
annotationCtx.value.clearRect(0, 0, annotation.value.width, annotation.value.height) | |
annotationCtx.value.fillStyle = 'white' | |
annotationCtx.value.font = 'bold 50px monospace' | |
annotationCtx.value.fillText(textString, 0, 50) | |
annotationTexture.value = new THREE.CanvasTexture(annotation.value) | |
} | |
onMounted(() => { | |
// get the canvas context | |
annotationCtx.value = annotation.value.getContext('2d') | |
// double the DPI of the canvas | |
const dpi = window.devicePixelRatio * 4 | |
annotation.value.width = 100 * dpi | |
annotation.value.height = 100 * dpi | |
// create a texture from the canvas | |
annotationTexture.value = new THREE.CanvasTexture(annotation.value) | |
}) | |
const orbitalObjects = ref([ | |
{ offsetPosition: [4, 0, -0.2], color: '0x00ff00' }, | |
{ offsetPosition: [-3.4, -3.4, 0], color: '0x0000ff' }, | |
{ offsetPosition: [0, 1.1, 4], color: '0xff0000' }, | |
{ offsetPosition: [0, -4, 0], color: '0x00ffff' }, | |
{ offsetPosition: [-2, 1.4, -4], color: '0xffff00' }, | |
]); | |
</script> | |
<template> | |
<div> | |
<canvas id="annotation" ref="annotation" class="hidden w-96 h-96 border border-red-500"></canvas> | |
<Suspense> | |
<div class="pointer-events-none"> | |
<TresCanvas window-size shadow alpha> | |
<EffectComposer> | |
<Bloom :radius="4.85" :intensity="10" :luminance-threshold="0.04" :luminance-smoothing="0.3" mipmap-blur /> | |
<Vignette /> | |
<!-- <Pixelation :granularity="9" /> --> | |
</EffectComposer> | |
<!-- <TresPerspectiveCamera :position="[-0.2, 0, 5]" /> --> | |
<TresPerspectiveCamera :position="[-0.2, 0, 15]" /> | |
<!-- <OrbitControls /> --> | |
<TresGroup :rotation="[sphereGroupRotation.x, sphereGroupRotation.y, sphereGroupRotation.z]"> | |
<!-- <TransformControls :object="sphereRef" :size="2" /> --> | |
<TresMesh ref="sphereRef"> | |
<TresSphereGeometry :args="[1, 16, 16]" /> | |
<TresMeshStandardMaterial :color="0xff0000" /> | |
</TresMesh> | |
<TresGroup :rotation="[orbitGroupRotation.x, orbitGroupRotation.y, orbitGroupRotation.z]"> | |
<OrbitalObject v-for="(item, index) in orbitalObjects" :key="index" :offsetPosition="item.offsetPosition" | |
:color="item.color" /> | |
<!-- and a line to the center --> | |
<TresMesh ref="lineRef2"> | |
<!-- draw a line from the sphere to the annotation --> | |
<Line2 :points="[[0, 0], [4, 0]]" :line-width="2" color="#82dbc5" /> | |
<TresLineBasicMaterial :color="'#ffffff'" /> | |
</TresMesh> | |
<TresGroup :position="[4, 0, 0]"> | |
<TresMesh ref="orbitRef"> | |
<!-- offset the orbit group so that it is centered on the sphere --> | |
<TresSphereGeometry :args="[0.2, 8, 8]" /> | |
<TresMeshStandardMaterial :color="0x00ff00" /> | |
<TresMesh ref="annotationRef" | |
:rotation="[-orbitGroupRotation.x, -orbitGroupRotation.y, orbitGroupRotation.z]"> | |
> | |
<TresPlaneGeometry :args="[2, 2]" /> | |
<TresMeshBasicMaterial :map="annotationTexture" :transparent="true" /> | |
</TresMesh> | |
</TresMesh> | |
</TresGroup> | |
</TresGroup> | |
<!-- draw annotation plane --> | |
<TresMesh ref="annotationPlaneRef"> | |
<TresPlaneGeometry :args="[2, 3]" /> | |
<TresMeshBasicMaterial :map="annotationTexture" :transparent="true" /> | |
</TresMesh> | |
<TresMesh ref="lineRef"> | |
<!-- draw a line from the sphere to the annotation --> | |
<Line2 :points="[[0, 0], [0, 1.2]]" :line-width="2" color="#82dbc5" /> | |
<TresLineBasicMaterial :color="'#82dbc5'" /> | |
</TresMesh> | |
</TresGroup> | |
<TresAmbientLight :intensity="3" /> | |
<TresDirectionalLight :position="[0, 2, 4]" :intensity="10" /> | |
</TresCanvas> | |
</div> | |
</Suspense> | |
</div> | |
</template> | |
<style scoped> | |
.label { | |
position: absolute; | |
transform-style: preserve-3d; | |
color: #000; | |
background: #fff; | |
padding: 5px; | |
border-radius: 5px; | |
} | |
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<TresGroup :position="offsetPosition"> | |
<!-- Line from this orbital object to the center --> | |
<TresMesh> | |
<Line2 :points="[[0, 0, 0], [-offsetPosition[0], -offsetPosition[1], -offsetPosition[2]]]" :line-width="2" color="#82dbc5" /> | |
<TresLineBasicMaterial :color="'#ffffff'" /> | |
</TresMesh> | |
<!-- Orbital Object Sphere --> | |
<TresMesh> | |
<TresSphereGeometry :args="[0.2, 8, 8]" /> | |
<TresMeshStandardMaterial :color="color" /> | |
</TresMesh> | |
<!-- Add Annotation Here --> | |
</TresGroup> | |
</template> | |
<script setup> | |
const props = defineProps({ | |
offsetPosition: Array, | |
color: String, | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment