add interactive 3D sample animated by threejs
This commit is contained in:
parent
ee4e6adf06
commit
32dc56f9dd
17 changed files with 165 additions and 26 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -83,3 +83,7 @@ public/site/config/.license
|
|||
# Vendor
|
||||
# ---------------
|
||||
public/vendor
|
||||
|
||||
# Local
|
||||
# ---------------
|
||||
/local
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
BIN
public/assets/3D/flacon-test.glb
Normal file
BIN
public/assets/3D/flacon-test.glb
Normal file
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
|
|
@ -0,0 +1 @@
|
|||
Uuid: OcfuImJZoRDv8tPB
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
Favoriteforusers:
|
||||
|
||||
----
|
||||
|
||||
Uuid: INXC9OOVmvBjPHZw
|
||||
|
||||
----
|
||||
|
||||
Template: inspiration
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
Favoriteforusers:
|
||||
|
||||
----
|
||||
|
||||
Uuid: LDbC2tGWZmJYuyQR
|
||||
|
||||
----
|
||||
|
||||
Template: inspiration
|
||||
|
|
@ -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
|
||||
|
|
@ -28,6 +28,7 @@ tabs:
|
|||
type: date
|
||||
display: YY/MM
|
||||
required: true
|
||||
default: today
|
||||
width: 2/12
|
||||
new:
|
||||
label: Nouveauté
|
||||
|
|
|
|||
|
|
@ -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' => [
|
||||
|
|
|
|||
71
src/components/VirtualSample.vue
Normal file
71
src/components/VirtualSample.vue
Normal 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>
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue