312 lines
No EOL
17 KiB
HTML
312 lines
No EOL
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Générateur de NFB (Non-Fungible Boogers)</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
font-family: 'Inter', sans-serif;
|
|
background-color: #f0f4f8; /* Couleur de fond plus douce */
|
|
}
|
|
.nfb-card {
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
overflow: hidden; /* Pour que le SVG ne dépasse pas avec des bordures arrondies */
|
|
}
|
|
.nfb-visual {
|
|
min-height: 250px; /* Hauteur minimale pour la visualisation */
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #e2e8f0; /* Fond pour la zone visuelle */
|
|
}
|
|
.btn-generate {
|
|
background-color: #3b82f6; /* Bleu Tailwind */
|
|
color: white;
|
|
padding: 12px 24px;
|
|
border-radius: 8px;
|
|
font-weight: 600;
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
.btn-generate:hover {
|
|
background-color: #2563eb;
|
|
}
|
|
.attribute-list dt {
|
|
font-weight: 600;
|
|
color: #4a5568; /* Gris plus foncé pour les titres d'attributs */
|
|
}
|
|
.attribute-list dd {
|
|
color: #718096; /* Gris pour les valeurs d'attributs */
|
|
margin-bottom: 8px;
|
|
}
|
|
/* Style pour la rareté */
|
|
.rarity-tag {
|
|
display: inline-block;
|
|
padding: 4px 12px;
|
|
border-radius: 16px;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
}
|
|
.rarity-COMMUNE { background-color: #d1d5db; color: #374151; } /* Gris */
|
|
.rarity-RARE { background-color: #60a5fa; color: #eff6ff; } /* Bleu */
|
|
.rarity-ÉPIQUE { background-color: #a78bfa; color: #f5f3ff; } /* Violet */
|
|
.rarity-LÉGENDAIRE { background-color: #facc15; color: #78350f; } /* Jaune */
|
|
.rarity-MYTHIQUE { background-color: #f472b6; color: #831843; } /* Rose/Orange pour un côté spécial */
|
|
|
|
</style>
|
|
</head>
|
|
<body class="flex flex-col items-center justify-center min-h-screen p-4 sm:p-6">
|
|
|
|
<div class="w-full max-w-2xl mx-auto">
|
|
<header class="text-center mb-8">
|
|
<h1 class="text-3xl sm:text-4xl font-bold text-gray-800">Générateur de NFB 👃</h1>
|
|
<p class="text-gray-600 mt-2">Créez votre propre "Non-Fungible Booger" unique et découvrez ses attributs !</p>
|
|
</header>
|
|
|
|
<div class="nfb-card">
|
|
<div id="nfbVisualContainer" class="nfb-visual p-6">
|
|
<!-- La visualisation du NFB apparaîtra ici -->
|
|
<span class="text-gray-500">Votre NFB apparaîtra ici...</span>
|
|
</div>
|
|
|
|
<div class="p-6 sm:p-8">
|
|
<div id="nfbDetails" class="mb-6">
|
|
<p class="text-gray-500 text-center">Cliquez sur le bouton pour générer un NFB.</p>
|
|
<!-- Les détails du NFB apparaîtront ici -->
|
|
</div>
|
|
|
|
<button id="generateButton" class="btn-generate w-full">
|
|
✨ Générer un nouveau NFB ✨
|
|
</button>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="text-center mt-8 text-sm text-gray-500">
|
|
<p><strong>Note :</strong> Ceci est un simulateur pour le plaisir. La création de véritables NFT nécessite des étapes supplémentaires comme le déploiement sur une blockchain.</p>
|
|
<p>Inspiré par une idée... mémorable !</p>
|
|
<div class="flex justify-center">
|
|
<iframe width="450" height="300" src="https://www.youtube.com/embed/8CLoNOEWN4g" title="François Pérusse - Grosse Morve + C'est Belle Une Fille" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
// Options pour la génération des NFB
|
|
const nomsPoetiquesPrefix = ["Le Joyau", "L'Énigmatique", "Le Classique", "Le Scintillant", "Le Robuste", "Le Discret", "L'Audacieux", "Le Visqueux", "Le Séché"];
|
|
const nomsPoetiquesSuffix = ["du Sinus Profond", "de l'Éternuement Matinal", "du Cornet Gauche", "de l'Allergie Printanière", "de la Fosse Nasale Oubliée", "de Minuit", "Post-Grippal"];
|
|
|
|
const couleurs = [
|
|
{ nom: "vert épinard profond", valeur_css: "#2f855A", ajustement_texte: "#f0fff4" }, // Texte clair
|
|
{ nom: "jaune moutarde à l'ancienne", valeur_css: "#D69E2E", ajustement_texte: "#fffaf0" }, // Texte clair
|
|
{ nom: "transparent cristallin", valeur_css: "rgba(237, 242, 247, 0.7)", ajustement_texte: "#2d3748" }, // Texte foncé (car fond clair/transparent)
|
|
{ nom: "brun terreux nuancé", valeur_css: "#874A33", ajustement_texte: "#fffaf0" }, // Texte clair
|
|
{ nom: "orange fluo (édition limitée)", valeur_css: "#FF7849", ajustement_texte: "#fff5f0" }, // Texte clair
|
|
{ nom: "arc-en-ciel psychédélique (mythique)", valeur_css: "linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet)", ajustement_texte: "#ffffff" }, // Texte blanc pour contraste
|
|
{ nom: "noir d'encre mystérieux", valeur_css: "#1A202C", ajustement_texte: "#e2e8f0" } // Texte clair
|
|
];
|
|
const tailles = ["microscopique (pour les puristes)", "discrète mais présente", "taille respectable", "franchement impressionnante", "qui-bloque-la-narine (collector)"];
|
|
const textures = ["sèche et délicatement friable", "humide et agréablement collante", "élastique et rebondie", "pétillante au toucher (secret de fabrication)", "cristallisée finement", "aérée comme une meringue"];
|
|
const formesArtistiques = ["sphérique parfaite", "amorphe contemporaine", "stalactite élégante", "comète avec traînée subtile", "nuage abstrait", "fractale complexe"];
|
|
const accessoiresRares = [null, null, null, null, null, null, null, null, "Pépite d'or (trace)", "Mini-diamant (éclat discret)", "Fossil d'acarien préhistorique", "Pollen de fleur exotique (rare)", "ADN de célébrité (non vérifié)"];
|
|
|
|
const nfbVisualContainer = document.getElementById('nfbVisualContainer');
|
|
const nfbDetailsContainer = document.getElementById('nfbDetails');
|
|
const generateButton = document.getElementById('generateButton');
|
|
|
|
// Fonction pour obtenir un élément aléatoire d'un tableau
|
|
function getRandomElement(arr) {
|
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
}
|
|
|
|
// Fonction pour générer un NFB
|
|
function genererNFB() {
|
|
const nomGenere = `${getRandomElement(nomsPoetiquesPrefix)} ${getRandomElement(nomsPoetiquesSuffix)} CN-${Math.floor(Math.random() * 8999) + 1000}`;
|
|
const couleurChoisie = getRandomElement(couleurs);
|
|
const accessoire = getRandomElement(accessoiresRares);
|
|
const tailleChoisie = getRandomElement(tailles);
|
|
|
|
let nfb = {
|
|
nom_oeuvre: nomGenere,
|
|
couleur_dominante: couleurChoisie,
|
|
taille_percue: tailleChoisie,
|
|
texture_au_toucher_virtuel: getRandomElement(textures),
|
|
forme_artistique_observee: getRandomElement(formesArtistiques),
|
|
niveau_de_brillance_speculaire: `${Math.floor(Math.random() * 100) + 1} cd/m² (estimé)`,
|
|
accessoire_exclusif_potentiel: accessoire,
|
|
ph_approximatif_theorique: (Math.random() * (8.0 - 5.0) + 5.0).toFixed(1),
|
|
coefficient_de_collectionnite: `${Math.floor(Math.random() * 991) + 10} points`
|
|
};
|
|
|
|
// Logique de rareté améliorée
|
|
let pointsRarete = 0;
|
|
if (nfb.couleur_dominante.nom.includes("(mythique)")) pointsRarete += 50;
|
|
if (nfb.couleur_dominante.nom.includes("(édition limitée)")) pointsRarete += 15;
|
|
if (nfb.accessoire_exclusif_potentiel) {
|
|
pointsRarete += 30;
|
|
if (nfb.accessoire_exclusif_potentiel.includes("Pépite d'or") || nfb.accessoire_exclusif_potentiel.includes("Mini-diamant")) pointsRarete += 20;
|
|
if (nfb.accessoire_exclusif_potentiel.includes("ADN de célébrité")) pointsRarete += 35; // Très rare !
|
|
}
|
|
if (nfb.taille_percue.includes("(collector)")) pointsRarete += 25;
|
|
if (nfb.forme_artistique_observee.includes("fractale complexe")) pointsRarete += 10;
|
|
if (parseFloat(nfb.ph_approximatif_theorique) < 5.5 || parseFloat(nfb.ph_approximatif_theorique) > 7.5) pointsRarete += 5; // pH extrême = plus rare
|
|
|
|
let rarete = "COMMUNE";
|
|
if (pointsRarete >= 90) rarete = "MYTHIQUE"; // ex: arc-en-ciel + or/diamant + collector + ADN célébrité
|
|
else if (pointsRarete >= 65) rarete = "LÉGENDAIRE"; // ex: arc-en-ciel + accessoire or/diamant OU ADN célébrité + collector
|
|
else if (pointsRarete >= 40) rarete = "ÉPIQUE"; // ex: accessoire notable OU taille collector + couleur limitée
|
|
else if (pointsRarete >= 15) rarete = "RARE"; // ex: couleur limitée OU forme fractale + pH extrême
|
|
|
|
nfb.rarete_estimee_par_l_IA = rarete;
|
|
return nfb;
|
|
}
|
|
|
|
// Fonction pour afficher le NFB
|
|
function afficherNFB(nfb) {
|
|
// Affichage des détails textuels
|
|
let detailsHtml = `<h2 class="text-2xl font-bold text-gray-800 mb-4">${nfb.nom_oeuvre}</h2>`;
|
|
detailsHtml += `<div class="mb-3"><span class="rarity-tag rarity-${nfb.rarete_estimee_par_l_IA.replace('É', 'E')}">${nfb.rarete_estimee_par_l_IA}</span></div>`; // .replace pour classe CSS
|
|
detailsHtml += '<dl class="attribute-list grid grid-cols-1 sm:grid-cols-2 gap-x-4">';
|
|
|
|
const attributeTranslations = {
|
|
couleur_dominante: "Couleur dominante",
|
|
taille_percue: "Taille perçue",
|
|
texture_au_toucher_virtuel: "Texture (virtuelle)",
|
|
forme_artistique_observee: "Forme artistique",
|
|
niveau_de_brillance_speculaire: "Brillance spéculaire",
|
|
accessoire_exclusif_potentiel: "Accessoire exclusif",
|
|
ph_approximatif_theorique: "pH approximatif",
|
|
coefficient_de_collectionnite: "Coefficient de collectionnite"
|
|
};
|
|
|
|
for (const key in nfb) {
|
|
if (key !== "nom_oeuvre" && key !== "rarete_estimee_par_l_IA") {
|
|
const keyText = attributeTranslations[key] || key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
let value = nfb[key];
|
|
if (key === "couleur_dominante") {
|
|
value = nfb[key].nom;
|
|
} else if (key === "accessoire_exclusif_potentiel" && !value) {
|
|
value = "Aucun";
|
|
}
|
|
detailsHtml += `<div class="mb-2"><dt>${keyText}</dt><dd>${value}</dd></div>`;
|
|
}
|
|
}
|
|
detailsHtml += '</dl>';
|
|
nfbDetailsContainer.innerHTML = detailsHtml;
|
|
|
|
// Génération du visuel SVG
|
|
nfbVisualContainer.innerHTML = genererVisuelSVG(nfb);
|
|
}
|
|
|
|
// Fonction pour générer un visuel SVG simple pour le NFB
|
|
function genererVisuelSVG(nfb) {
|
|
const svgSize = 200; // Taille du canvas SVG
|
|
let fillStyle = nfb.couleur_dominante.valeur_css;
|
|
|
|
// Déterminer la taille relative pour le SVG
|
|
let scaleFactor = 0.5; // Taille de base
|
|
if (nfb.taille_percue.includes("microscopique")) scaleFactor = 0.3;
|
|
else if (nfb.taille_percue.includes("discrète")) scaleFactor = 0.4;
|
|
else if (nfb.taille_percue.includes("respectable")) scaleFactor = 0.6;
|
|
else if (nfb.taille_percue.includes("impressionnante")) scaleFactor = 0.8;
|
|
else if (nfb.taille_percue.includes("collector")) scaleFactor = 1.0;
|
|
|
|
const radius = (svgSize / 2) * scaleFactor;
|
|
const centerX = svgSize / 2;
|
|
const centerY = svgSize / 2;
|
|
|
|
// Créer un chemin de "blob" aléatoire
|
|
let pathData = "M ";
|
|
const points = 8 + Math.floor(Math.random() * 5); // Nombre de points pour le blob
|
|
const angleStep = (Math.PI * 2) / points;
|
|
|
|
for (let i = 0; i < points; i++) {
|
|
const angle = angleStep * i;
|
|
// Variation aléatoire du rayon pour un aspect organique
|
|
const currentRadius = radius * (0.8 + Math.random() * 0.4);
|
|
const x = centerX + Math.cos(angle) * currentRadius;
|
|
const y = centerY + Math.sin(angle) * currentRadius;
|
|
pathData += `${x.toFixed(2)},${y.toFixed(2)} `;
|
|
if (i === 0) {
|
|
pathData += "C "; // Start smooth curve commands after first point
|
|
} else if (i < points -1 ){
|
|
// Add control points for Bezier curves for a smoother blob
|
|
const prevAngle = angleStep * (i - 0.5);
|
|
const controlRadius = radius * (0.7 + Math.random() * 0.5);
|
|
const cx = centerX + Math.cos(prevAngle) * controlRadius;
|
|
const cy = centerY + Math.sin(prevAngle) * controlRadius;
|
|
pathData += `${cx.toFixed(2)},${cy.toFixed(2)} `;
|
|
}
|
|
}
|
|
pathData += "Z"; // Close the path
|
|
|
|
let svgContent = `<svg width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" xmlns="http://www.w3.org/2000/svg">`;
|
|
|
|
// Gestion des gradients pour les couleurs spéciales
|
|
if (nfb.couleur_dominante.nom.includes("arc-en-ciel")) {
|
|
svgContent += `
|
|
<defs>
|
|
<linearGradient id="rainbowGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" style="stop-color:red;" />
|
|
<stop offset="16%" style="stop-color:orange;" />
|
|
<stop offset="33%" style="stop-color:yellow;" />
|
|
<stop offset="50%" style="stop-color:green;" />
|
|
<stop offset="66%" style="stop-color:blue;" />
|
|
<stop offset="83%" style="stop-color:indigo;" />
|
|
<stop offset="100%" style="stop-color:violet;" />
|
|
</linearGradient>
|
|
</defs>`;
|
|
fillStyle = "url(#rainbowGradient)";
|
|
}
|
|
|
|
svgContent += `<path d="${pathData}" fill="${fillStyle}" stroke="${nfb.couleur_dominante.ajustement_texte || '#4A5568'}" stroke-width="2"/>`;
|
|
|
|
// Ajout d'un accessoire visuel simple
|
|
if (nfb.accessoire_exclusif_potentiel) {
|
|
let accesoryFill = "gold";
|
|
let accesoryRadius = 5 * scaleFactor;
|
|
if (nfb.accessoire_exclusif_potentiel.includes("diamant")) {
|
|
accesoryFill = "lightblue";
|
|
accesoryRadius = 4 * scaleFactor;
|
|
// Petite étoile pour le diamant
|
|
svgContent += `<polygon points="${centerX},${centerY - accesoryRadius*2} ${centerX + accesoryRadius*0.5},${centerY - accesoryRadius*0.5} ${centerX + accesoryRadius*2},${centerY} ${centerX + accesoryRadius*0.5},${centerY + accesoryRadius*0.5} ${centerX},${centerY + accesoryRadius*2} ${centerX - accesoryRadius*0.5},${centerY + accesoryRadius*0.5} ${centerX - accesoryRadius*2},${centerY} ${centerX - accesoryRadius*0.5},${centerY - accesoryRadius*0.5}" fill="${accesoryFill}" opacity="0.9"/>`;
|
|
|
|
} else if (nfb.accessoire_exclusif_potentiel.includes("Pépite d'or")) {
|
|
svgContent += `<circle cx="${centerX + radius * 0.3 * (Math.random() - 0.5)}" cy="${centerY + radius * 0.3 * (Math.random() - 0.5)}" r="${accesoryRadius}" fill="${accesoryFill}" opacity="0.8"/>`;
|
|
} else if (nfb.accessoire_exclusif_potentiel.includes("Pollen")) {
|
|
// Petits points jaunes pour le pollen
|
|
for(let i=0; i<3; i++) {
|
|
svgContent += `<circle cx="${centerX + radius * 0.4 * (Math.random() - 0.5)}" cy="${centerY + radius * 0.4 * (Math.random() - 0.5)}" r="${2*scaleFactor}" fill="yellow" opacity="0.7"/>`;
|
|
}
|
|
}
|
|
}
|
|
// Effet de brillance si texture pétillante ou brillance élevée
|
|
if (nfb.texture_au_toucher_virtuel.includes("pétillante") || parseInt(nfb.niveau_de_brillance_speculaire) > 75) {
|
|
svgContent += `<circle cx="${centerX - radius * 0.3}" cy="${centerY - radius * 0.3}" r="${radius * 0.15}" fill="white" opacity="0.5"/>`;
|
|
}
|
|
|
|
|
|
svgContent += `</svg>`;
|
|
return svgContent;
|
|
}
|
|
|
|
// Écouteur d'événement pour le bouton
|
|
generateButton.addEventListener('click', () => {
|
|
const nouveauNFB = genererNFB();
|
|
afficherNFB(nouveauNFB);
|
|
});
|
|
|
|
// Générer un premier NFB au chargement de la page pour démonstration
|
|
window.onload = () => {
|
|
const premierNFB = genererNFB();
|
|
afficherNFB(premierNFB);
|
|
};
|
|
</script>
|
|
</body>
|
|
</html> |