add interactive 3D sample animated by threejs

This commit is contained in:
isUnknown 2024-09-23 18:34:02 +02:00
parent ee4e6adf06
commit 32dc56f9dd
17 changed files with 165 additions and 26 deletions

View file

@ -0,0 +1,71 @@
<template>
<div class="virtual-sample" ref="virtualSampleContainer"></div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
const virtualSampleContainer = ref(null);
onMounted(() => {
const container = virtualSampleContainer.value;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setClearColor(0xffffff, 1);
container.appendChild(renderer.domElement);
const light = new THREE.AmbientLight(0xffffff);
scene.add(light);
const loader = new GLTFLoader();
loader.load(
"/assets/3D/flacon-test.glb",
(gltf) => {
const model = gltf.scene;
scene.add(model);
},
undefined,
(error) => {
console.error("Erreur lors du chargement du modèle", error);
}
);
camera.position.set(3, 2, 5);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
const animate = () => {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
};
animate();
window.addEventListener("resize", () => {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
});
});
</script>
<style scoped>
.virtual-sample {
width: 100%;
height: 100vh;
}
</style>

View file

@ -3,17 +3,15 @@
<div
id="inspirations-dropdown"
class="flex flex-col"
style="
--image: url('https://plus.unsplash.com/premium_photo-1675626791716-7f74187592f1?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');
"
:style="'--image: url(\'' + current.cover + '\');'"
>
<label for="inspirations-select" class="text-sm">Choisir une inspiration</label>
<label for="inspirations-select" class="text-sm"
>Choisir une inspiration</label
>
<Select
id="inspirations-select"
v-model="selectedInspiration"
:options="inspirations"
optionLabel="name"
optionValue="value"
v-model="current.uri"
:options="all"
class="font-serif"
data-icon="chevron-single-down"
checkmark
@ -22,13 +20,23 @@
</template>
<script setup>
import { ref } from "vue";
import { ref, watch } from "vue";
const selectedInspiration = ref("shape-of-nature");
const inspirations = ref([
{ name: "Shape of Nature", value: "shape-of-nature" },
{ name: "Inspiration Title", value: "inspiration-title" },
]);
const { all } = defineProps({
all: Array,
});
const current = ref(all[0]);
watch(current, (newValue) => {
console.log(newValue);
});
const emit = defineEmits(["update:currentInspiration"]);
function changeInspiration(inspirationUri) {
emit("update:currentInspiration", inspirationUri);
}
</script>
<style>
@ -80,7 +88,6 @@ const inspirations = ref([
cursor: pointer;
}
/* Icon */
#inspirations-select svg {
display: none; /* Hide default component svg */
@ -89,10 +96,10 @@ const inspirations = ref([
--icon-color: var(--color-grey-700);
position: absolute;
right: var(--space-8);
top: .625rem;
top: 0.625rem;
width: 2.5rem;
height: 2.5rem;
padding: .625rem;
padding: 0.625rem;
}
#inspirations-dropdown label {
color: var(--color-grey-700);
@ -144,7 +151,7 @@ const inspirations = ref([
/* Check */
#inspirations-select_list > * > svg {
position: absolute;
left: .875rem;
left: 0.875rem;
width: 1rem;
height: 1rem;
color: var(--color-grey-700);

View file

@ -1,9 +1,11 @@
<template>
<main>
<Projects />
<VirtualSample />
</main>
</template>
<script setup>
import Projects from "../components/Projects.vue";
import VirtualSample from "../components/VirtualSample.vue";
</script>

View file

@ -2,7 +2,7 @@
<main>
<h2 id="tabslist" class="sr-only">Inspirations</h2>
<Tabs :tabs="tabs" @update:currentTab="changeTab" />
<Selector />
<Selector :all="page.inspirations" />
<section :id="currentTab" class="inspiration">
<Header :inspiration="currentInspiration" />
<div class="masonry flow">