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

4
.gitignore vendored
View file

@ -83,3 +83,7 @@ public/site/config/.license
# Vendor
# ---------------
public/vendor
# Local
# ---------------
/local

6
package-lock.json generated
View file

@ -13,6 +13,7 @@
"pinia": "^2.1.7",
"primevue": "^4.0.6",
"slugify": "^1.6.6",
"three": "^0.168.0",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
},
@ -1061,6 +1062,11 @@
"node": ">=0.10.0"
}
},
"node_modules/three": {
"version": "0.168.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz",
"integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw=="
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",

View file

@ -14,6 +14,7 @@
"pinia": "^2.1.7",
"primevue": "^4.0.6",
"slugify": "^1.6.6",
"three": "^0.168.0",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
},

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -0,0 +1 @@
Uuid: OcfuImJZoRDv8tPB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View file

@ -0,0 +1,9 @@
Favoriteforusers:
----
Uuid: INXC9OOVmvBjPHZw
----
Template: inspiration

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View file

@ -0,0 +1,9 @@
Favoriteforusers:
----
Uuid: LDbC2tGWZmJYuyQR
----
Template: inspiration

View file

@ -0,0 +1,28 @@
Title: Autre inspiration
----
Cover: - file://OcfuImJZoRDv8tPB
----
Date: 2024-09-24
----
New: false
----
Description: Nulla arcu gravida sollicitudin suspendisse proin tincidunt eu erat fusce sem massa dolor lorem enim nisi commodo scelerisque diam hendrerit amet adipiscing amet aliquam congue. Nisi arcu nunc congue lacus sem quam nisi ex sit urna ac rutrum cursus suspendisse sit amet condimentum portaest amet elit ac felis eget id.
----
Media:
- file://LDbC2tGWZmJYuyQR
- file://INXC9OOVmvBjPHZw
----
Uuid: 7wws4Jxf2f5iR10B

View file

@ -28,6 +28,7 @@ tabs:
type: date
display: YY/MM
required: true
default: today
width: 2/12
new:
label: Nouveauté

View file

@ -1,9 +1,9 @@
<?php
return function($newPage, $oldPage) {
if ($newPage->template() != "project") return;
return function($page) {
if ($page->template() != "project") return;
$newPage->createChild([
$page->createChild([
'slug' => 'client-brief',
'template' => 'client-brief',
'content' => [
@ -11,7 +11,7 @@ return function($newPage, $oldPage) {
]
]);
$newPage->createChild([
$page->createChild([
'slug' => 'proposal',
'template' => 'proposal',
'content' => [
@ -19,7 +19,7 @@ return function($newPage, $oldPage) {
]
]);
$newPage->createChild([
$page->createChild([
'slug' => 'extended-brief',
'template' => 'extended-brief',
'content' => [
@ -27,7 +27,7 @@ return function($newPage, $oldPage) {
]
]);
$newPage->createChild([
$page->createChild([
'slug' => 'sample',
'template' => 'sample',
'content' => [

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">