Skip to content

Commit

Permalink
feat(magical-marbles): init example
Browse files Browse the repository at this point in the history
  • Loading branch information
damienmontastier committed Feb 28, 2024
1 parent 13c0d45 commit dee1d7a
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
Expand Down
314 changes: 314 additions & 0 deletions components/content/magical-marbles/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
<script setup lang="ts">
import { Color, RepeatWrapping, NearestFilter, MeshStandardMaterial } from 'three'
import { vertex, fragment } from './shaders'
import { gsap } from 'gsap'
const gl = {
alpha: true,
shadows: true,
powerPreference: "high-performance"
}
let ctx, tl
const sphereRef = shallowRef(null)
const backgroundRef = ref(null)
const mainRef = ref(null)
const indexColor = ref(0)
const isDragging = ref(false)
const clickDisabled = ref(false);
const colors = ref([
[0, 100, 50],
[60, 100, 50],
[150, 100, 50],
[240, 70, 60],
[0, 0, 80],
])
const params = reactive({
roughness: 0.15,
speed: 0.05,
iterations: 48,
depth: 0.6,
smoothing: 0.2,
displacement: 0.1,
metalness: 0,
colorB: "#008080",
})
const heightMap = await useTexture(['/magical-marbles/heightMap.jpeg'])
const displacementMap = await useTexture(['/magical-marbles/displacementMap.jpeg'])
heightMap.minFilter = displacementMap.minFilter = NearestFilter
displacementMap.wrapS = displacementMap.wrapT = RepeatWrapping
const { onLoop } = useRenderLoop()
const { roughness, iterations, depth, smoothing, displacement, metalness, speed } = useControls({
roughness: {
value: params.roughness,
min: 0,
max: 1,
step: 0.01
},
metalness: {
value: params.metalness,
min: 0,
max: 1,
step: 0.01
},
iterations: {
value: params.iterations,
min: 1,
max: 64,
step: 1
},
depth: {
value: params.depth,
min: 0,
max: 1,
step: 0.01
},
smoothing: {
value: params.smoothing,
min: 0,
max: 1,
step: 0.01
},
displacement: {
value: params.displacement,
min: 0,
max: .35,
step: 0.01
},
speed: {
value: params.speed,
min: 0,
max: .5,
step: 0.001
},
colorB: params.colorB,
})
const currentColor = computed(() => colors.value[indexColor.value])
const colorFinalB = computed(() => new Color(`hsl(${currentColor.value[0]}, ${currentColor.value[1]}%, ${currentColor.value[2]}%)`))
const backgroundGradient = computed(() => `radial-gradient(hsl(${currentColor.value[0]}, ${currentColor.value[1] * 0.7}%, ${currentColor.value[2]}%), hsl(${currentColor.value[0]},${currentColor.value[1] * 0.4}%, ${currentColor.value[2] * 0.2}%))`)
const uniforms = reactive({
time: { value: 0 },
colorA: { value: new Color(0, 0, 0) },
colorB: { value: colorFinalB },
heightMap: { value: heightMap },
displacementMap: { value: displacementMap },
iterations,
depth,
smoothing,
displacement,
})
onMounted(() => {
ctx = gsap.context(() => { }, mainRef.value);
})
onUnmounted(() => {
ctx.revert();
})
watch(sphereRef, (value) => {
updateBackground(true)
})
const onPointerClick = () => {
if (isDragging.value) return;
indexColor.value = (indexColor.value + 1) % colors.value.length
updateBackground()
}
const onStartDragging = (e) => {
isDragging.value = true;
}
const onEndDragging = async () => {
isDragging.value = false;
}
const updateBackground = (immediate = false) => {
if (isDragging.value) return
if (immediate) {
ctx.add(() => {
gsap.set(backgroundRef.value, {
background: `${backgroundGradient.value}`
})
});
} else {
ctx.add(() => {
tl = gsap.timeline()
.addLabel('sphereAnimation')
.to(backgroundRef.value, {
background: `${backgroundGradient.value}`,
duration: .5,
ease: 'power3.out'
}, 'sphereAnimation')
.to(uniforms.colorB.value, {
r: colorFinalB.value.r,
g: colorFinalB.value.g,
b: colorFinalB.value.b,
duration: .5,
ease: 'power3.out'
}, 'sphereAnimation')
.to(sphereRef.value.value.scale, {
x: 1.065,
y: 1.065,
z: 1.065,
duration: .25,
ease: 'power3.out'
}, 'sphereAnimation')
.to(sphereRef.value.value.scale, {
x: 1,
y: 1,
z: 1,
duration: .25,
ease: 'power3.out'
}, 'sphereAnimation+=100%')
});
}
}
onLoop(({ delta, elapsed, clock }) => {
uniforms.time.value += delta * speed.value.value
})
</script>

<template>
<main ref="mainRef" class="magical-marbles">

<NuxtLink class="magical-marbles__logo" to="/">
<img src="/lab.svg" alt="TresJS Logo" />
</NuxtLink>

<button class="magical-marbles__cta" @click.stop="onPointerClick">
shuffle colors
</button>

<div class="magical-marbles__infos">
<NuxtLink to="/">See more experiments and examples</NuxtLink>
<p>Magical Marbles inspired by the
<a target="_blank" href="https://tympanus.net/Tutorials/MagicalMarbles/">
Codrops tutorial
</a>
</p>
</div>

<div ref="backgroundRef" class="magical-marbles__bg" />

<!-- <TresLeches /> -->

<TresCanvas window-size v-bind="gl">
<TresPerspectiveCamera :position="[0, 0, 4.5]" :fov="45" :near=".1" :far="1000" />
<OrbitControls @start="onStartDragging" @end="onEndDragging" auto-rotate make-default />

<Suspense>
<Environment preset="hangar" />
</Suspense>

<Sphere ref="sphereRef" :args="[1, 64, 32]">
<CustomShaderMaterial :roughness="roughness.value" :metalness="metalness.value"
:baseMaterial="MeshStandardMaterial" :vertexShader="vertex" :fragmentShader="fragment"
:uniforms="uniforms" silent />
</Sphere>
</TresCanvas>
</main>
</template>

<style scoped>
.magical-marbles {
width: 100%;
height: 100%;
}
* {
font-family: "Segoe UI", Helvetica, Arial, sans-serif;
}
.magical-marbles__bg {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
background-color: #d8bcac;
pointer-events: none;
}
.magical-marbles__logo {
align-self: flex-start;
position: absolute;
top: 40px;
left: 60px;
width: 5%;
z-index: 3;
}
.magical-marbles__infos {
margin-top: auto;
position: absolute;
bottom: 40px;
left: 60px;
z-index: 3;
}
.magical-marbles__cta {
z-index: 3;
background: white;
position: absolute;
top: 40px;
right: 60px;
padding: 10px 20px;
color: black;
border-radius: 5px;
transition: transform .3s cubic-bezier(0.33, 1, 0.68, 1);
will-change: transform;
text-transform: uppercase;
font-weight: 800;
font-size: 1vw;
transform: scale(1)
}
.magical-marbles__cta:hover {
transform: scale(1.05)
}
.magical-marbles__infos p {
color: #FFF;
}
.magical-marbles__infos a {
pointer-events: auto;
color: #ad836d;
transition: color 0.25s;
}
.magical-marbles__infos a:hover {
color: #79573e;
}
canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 2;
/* pointer-events: none !important; */
}
</style>
Loading

0 comments on commit dee1d7a

Please sign in to comment.