Comment charger des fichiers 3D avec Vitepress ?
#[Ceci est un modèle 3D](/it/NeilArmstrong.glb)
#[Ceci est un modèle 3D](/it/NeilArmstrong.glb)
Source : Neil Armstrong Space Suit provided by the Smithsonian Digitization Programs Office and the National Air and Space Museum.
Ce site à pour but de résumer plusieurs de mes projets d'électronique, de développement et de mécanique. Pour ces derniers, je voulais ajouter un lecteur de modèle 3D avec tout de mêmes quelques exigences :
- Le format des modèles sera de préférence en GL Tansmission Format
- Le lecteur sera intégré au site (suivi du thème)
- L'utilisateur pourra contrôler le modèle (à minima en rotation)
J'ajoute également quelques exigences de direction mais non obligatoire :
- Interprétable en Markdown
- Une UI épurée
<model-viewer>
- La librairie tout en un de Google
<model-viewer>
est un composant web personnalisé développé par Google qui permet d'intégrer facilement des modèles 3D interactifs dans des pages web avec une compatibilité pour la réalité augmentée. Voici quelques points clés sur <model-viewer>
:
Facilité d'Intégration : Vous pouvez intégrer des modèles 3D dans une page web en utilisant une balise HTML simple, tout comme vous le feriez avec une image ou une vidéo.
Compatibilité Réalité Augmentée : Le composant permet aux utilisateurs de lancer une expérience de réalité augmentée directement depuis la visualisation 3D, sur les appareils compatibles.
Adaptable et Personnalisable : Les développeurs peuvent contrôler l'apparence, le comportement et le style du visualiseur pour l'adapter à leurs besoins.
Compatibilité Navigateur : Bien que développé par Google,
<model-viewer>
est conçu pour fonctionner sur une gamme de navigateurs et de dispositifs, assurant ainsi une large portée.Outils de Qualité : Il offre des ombres réalistes, des éclairages HDR et d'autres outils pour améliorer la qualité de la visualisation.
En somme, <model-viewer>
est un outil précieux pour toute organisation ou développeur souhaitant intégrer facilement des modèles 3D et des expériences de réalité augmentée dans leurs applications ou sites web sans avoir besoin de compétences spécialisées en développement 3D ou AR.
Intégration dans Vitepress
Pour intégration <model-viewer>
dans vitepress, il nous faut :
- Importer la librarie
- Ajouter une extension markdown pour les fichiers 3D
Importation de la librarie
Pour importer la librairie, j'ai choisi la solution la plus simple : charger la librairie sur toute les pages.
Pour ce faire, il suffit seulement de surcharger le thème courant avec un layout personnalisé dans lequel on chargera la librairie.
//.vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import Layout from './vue/components/Layout.vue'
export default {
...DefaultTheme,
Layout: Layout
}
//.vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import Layout from './vue/components/Layout.vue'
export default {
...DefaultTheme,
Layout: Layout
}
<!--.vitepress/theme/vue/components/Layout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
import('@google/model-viewer')
const { Layout } = DefaultTheme
</script>
<template>
<Layout />
</template>
<style>
model-viewer {
width: 100%;
aspect-ratio: 1/1;
height: auto;
}
</style>
<!--.vitepress/theme/vue/components/Layout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
import('@google/model-viewer')
const { Layout } = DefaultTheme
</script>
<template>
<Layout />
</template>
<style>
model-viewer {
width: 100%;
aspect-ratio: 1/1;
height: auto;
}
</style>
Extension Markdown-it
markdown-it
est une bibliothèque JavaScript moderne et extensible qui permet de convertir le Markdown en HTML. Il est possible d'ajouter des plugins pour ajouter des fonctionnalités, c'est d'ailleurs très facile.
L'objectif de l'extension est d'interpréter le texte #[label](url)
comme un fichier 3D, et à partir de ces informations, générer le code html suivant <model-viewer src="[src]" label="[label]" camera-controls></model-viewer>
.
// .vitepress/markdown-it/3d-plugin.js
export default (md) => {
md.inline.ruler.push('model3d', rule);
md.renderer.rules.model3d = (tokens, idx, options, env, slf) => render(tokens, idx, options, env, slf);
}
function rule(state) {
// Should begin with #[
if (state.src.charCodeAt(state.pos) !== 0x23/* # */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
// Extract label and url from "#[label](url)"
const regex = /\#\[(.*?)\]\((.*?)\)/;
const matches = state.src.match(regex);
const label = matches[1];
const url = matches[2];
if (!matches || !label || !url) {
return false;
}
const token = state.push('model3d', 'model-viewer', 0);
token.attrs = [['src', url], ['alt', label]];
state.pos += label.length + url.length + 5;
return true;
}
function render(tokens, idx, options, env, slf) {
var attrs = Object.fromEntries(tokens[idx].attrs)
return `</p>
<div>
<model-viewer src="${attrs.src}" alt="${attrs.alt}" camera-controls></model-viewer>
</div>
<p>`.replace(/ +/g, '')
}
// .vitepress/markdown-it/3d-plugin.js
export default (md) => {
md.inline.ruler.push('model3d', rule);
md.renderer.rules.model3d = (tokens, idx, options, env, slf) => render(tokens, idx, options, env, slf);
}
function rule(state) {
// Should begin with #[
if (state.src.charCodeAt(state.pos) !== 0x23/* # */) { return false; }
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
// Extract label and url from "#[label](url)"
const regex = /\#\[(.*?)\]\((.*?)\)/;
const matches = state.src.match(regex);
const label = matches[1];
const url = matches[2];
if (!matches || !label || !url) {
return false;
}
const token = state.push('model3d', 'model-viewer', 0);
token.attrs = [['src', url], ['alt', label]];
state.pos += label.length + url.length + 5;
return true;
}
function render(tokens, idx, options, env, slf) {
var attrs = Object.fromEntries(tokens[idx].attrs)
return `</p>
<div>
<model-viewer src="${attrs.src}" alt="${attrs.alt}" camera-controls></model-viewer>
</div>
<p>`.replace(/ +/g, '')
}
Quelques explications :
Extension du parseur Markdown :
javascriptexport default (md) => { md.inline.ruler.push('model3d', rule); md.renderer.rules.model3d = (tokens, idx, options, env, slf) => render(tokens, idx, options, env, slf); }
export default (md) => { md.inline.ruler.push('model3d', rule); md.renderer.rules.model3d = (tokens, idx, options, env, slf) => render(tokens, idx, options, env, slf); }
Ce code est une fonction qui accepte un objet
md
(qui représente le parseur Markdown). Il étend le parseur pour prendre en charge une nouvelle règle appelée 'model3d' et définit comment elle doit être rendue.La fonction de règle :
javascriptfunction rule(state) { ... }
function rule(state) { ... }
Cette fonction est utilisée pour détecter et traiter une syntaxe personnalisée dans le texte Markdown. La syntaxe ciblée est
#[label](url)
. Si cette séquence est détectée, elle est traitée comme une référence à un modèle 3D, oùlabel
est un texte descriptif eturl
est l'emplacement du modèle 3D.La fonction de rendu :
javascriptfunction render(tokens, idx, options, env, slf) { ... }
function render(tokens, idx, options, env, slf) { ... }
Une fois que la fonction de règle a détecté la syntaxe personnalisée et l'a transformée en un token
model3d
, cette fonction de rendu est appelée pour convertir ce token en HTML. Ici, elle convertit le token en une balise<model-viewer>
, qui est utilisée pour afficher des modèles 3D sur une page web.
Il ne manque plus qu'à ajouter le plugin dans la configuration de vitepress :
// .vitepress/config.mts
// ...
markdown: {
config: (md) => {
md.use(MarkdownIt3dPlugin)
},
}
// ...
// .vitepress/config.mts
// ...
markdown: {
config: (md) => {
md.use(MarkdownIt3dPlugin)
},
}
// ...
Code
Voici le code résumé
//.vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import Layout from './vue/components/Layout.vue'
export default {
...DefaultTheme,
Layout: Layout
}
//.vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import Layout from './vue/components/Layout.vue'
export default {
...DefaultTheme,
Layout: Layout
}