système de spirales/lianes

This commit is contained in:
ewen 2026-05-10 20:37:58 +02:00
parent 0cbb10cd90
commit 650cd6eab2
8 changed files with 896 additions and 0 deletions

View file

@ -27,6 +27,7 @@ body {
padding-bottom: 0; padding-bottom: 0;
margin-top: 0px; margin-top: 0px;
position: relative; position: relative;
z-index: 1;
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
align-items: start; align-items: start;
@ -79,6 +80,8 @@ body {
align-items: baseline; align-items: baseline;
justify-content: space-between; justify-content: space-between;
max-width: 550px; max-width: 550px;
position: relative;
z-index: 1;
} }
.index-header .now { .index-header .now {
@ -393,6 +396,226 @@ body {
opacity: 1; opacity: 1;
} }
/* === VINE ASCII ANIMATION === */
#vine-container {
position: fixed;
top: 0;
right: 0;
width: calc(100vw - 640px);
height: 100vh;
z-index: 0;
overflow: hidden;
}
#vine-container:hover {
cursor: crosshair;
}
.vine-char {
cursor: pointer;
}
.vine-char:hover {
color: #111 !important;
}
.vine-flower {
cursor: grab;
}
.vine-flower:active {
cursor: grabbing;
}
.vine-flower:hover {
filter: brightness(1.4);
}
@media (max-width: 1000px) {
#vine-container {
display: none;
}
}
@keyframes flowerFall {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.95;
}
100% {
transform: translateY(100px) rotate(60deg);
opacity: 0;
}
}
@keyframes leafFall {
0% {
transform: translateY(0) rotate(0deg) scale(1);
opacity: 0.6;
}
100% {
transform: translateY(80px) rotate(90deg) scale(0.6);
opacity: 0;
}
}
@keyframes flowerPulse {
0%, 100% {
transform: scale(1);
opacity: 0.9;
}
50% {
transform: scale(1.15);
opacity: 1;
}
}
.falling-flower {
animation: flowerFall 2.5s ease-in forwards;
}
.falling-leaf {
animation: leafFall 2s ease-in forwards;
}
.vine-flower {
animation: flowerPulse 2.5s ease-in-out infinite;
}
#vine-help-btn {
position: absolute;
top: 10px;
right: 10px;
width: 22px;
height: 22px;
border: 1px solid #111;
background: transparent;
font-family: 'IM Fell English', serif;
font-size: 14px;
line-height: 1;
color: #111;
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: background 0.2s, color 0.2s;
}
#vine-help-btn:hover {
background: #111;
color: #fff;
}
#vine-flower-counter {
position: absolute;
top: 36px;
right: 10px;
font-family: 'IM Fell English', serif;
font-size: 13px;
color: hsl(180, 60%, 70%);
text-align: center;
width: 22px;
line-height: 1;
z-index: 10;
pointer-events: none;
}
#vine-help-panel {
position: absolute;
top: 40px;
right: 10px;
width: 260px;
background: #fff;
border: 1px solid #111;
padding: 0.8rem 1rem;
z-index: 10;
font-family: 'IM Fell English', serif;
font-size: 14px;
line-height: 1.5;
color: #111;
cursor: default;
}
#vine-help-panel .close {
position: absolute;
top: 2px;
right: 6px;
font-size: 18px;
cursor: pointer;
line-height: 1;
padding: 0;
background: none;
border: none;
color: #111;
font-family: 'IM Fell English', serif;
}
#vine-help-panel .close:hover {
opacity: 0.6;
}
#vine-help-panel h3 {
margin: 0 0 0.6rem 0;
font-size: 15px;
font-weight: normal;
border-bottom: 1px solid #eee;
padding-bottom: 0.3rem;
}
#vine-help-panel ul {
margin: 0;
padding: 0;
list-style: none;
}
#vine-help-panel li {
margin-bottom: 0.4rem;
padding-left: 0.8rem;
position: relative;
}
#vine-help-panel li::before {
content: '—';
position: absolute;
left: 0;
opacity: 0.4;
}
#vine-help-panel li kbd {
font-family: 'Courier New', monospace;
font-size: 11px;
border: 1px solid #ddd;
padding: 0 3px;
background: #f9f9f9;
margin-right: 2px;
}
/* === SWIRL (page d'accueil) === */
#swirl-container {
position: fixed;
top: 0;
right: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 0;
overflow: hidden;
}
.swirl-char {
display: inline-block;
will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
#vine-container,
#swirl-container {
display: none;
}
}
/* === DETAILS / SUMMARY === */ /* === DETAILS / SUMMARY === */
summary { summary {
cursor: pointer; cursor: pointer;

124
assets/swirl.js Normal file
View file

@ -0,0 +1,124 @@
(function () {
const CHARS = ['~', '-', '_', '·', '°', '*', 'o', '+', '×', '=', '≈', '∿', '◦'];
function pick(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
class Swirl {
constructor(container) {
this.container = container;
this.cx = container.offsetWidth / 2 || window.innerWidth / 2;
this.cy = container.offsetHeight / 2 || window.innerHeight / 2;
this.elements = [];
this.frameCount = 0;
this.angle = 0;
this.radius = 2;
this.hue = Math.random() * 360;
this.hueSpeed = 0.25;
this.angleStep = 0.18;
this.radiusStep = 0.55;
this.globalRotation = 0;
this.loop = this.loop.bind(this);
requestAnimationFrame(this.loop);
window.addEventListener('resize', () => {
this.cx = container.offsetWidth / 2 || window.innerWidth / 2;
this.cy = container.offsetHeight / 2 || window.innerHeight / 2;
});
}
createChar(x, y, hue, scale) {
const color = `hsl(${hue}, 60%, 68%)`;
const el = document.createElement('span');
el.textContent = pick(CHARS);
el.classList.add('swirl-char');
el.style.cssText =
`position:absolute;` +
`left:${x}px;top:${y}px;` +
`font-family:"Courier New",Courier,monospace;` +
`font-size:15px;line-height:1;` +
`color:${color};` +
`pointer-events:none;user-select:none;` +
`opacity:0.85;`;
el.style.transform = `scale(${scale}) rotate(${Math.random() * 360}deg)`;
el.style.display = 'inline-block';
this.container.appendChild(el);
this.elements.push({
el,
born: this.frameCount,
maxAge: 900 + Math.floor(Math.random() * 400),
});
}
ageElements() {
if (this.frameCount % 12 !== 0) return;
const now = this.frameCount;
let pruneCount = 0;
const maxPrune = 12;
for (let i = 0; i < this.elements.length && pruneCount < maxPrune; i++) {
const item = this.elements[i];
const age = now - item.born;
if (age >= item.maxAge) {
item.el.remove();
this.elements.splice(i, 1);
i--;
pruneCount++;
} else if (age > item.maxAge - 150) {
const fade = 1 - (age - (item.maxAge - 150)) / 150;
item.el.style.opacity = Math.max(0, fade * 0.85);
}
}
}
update() {
this.frameCount++;
// Rotation globale du tourbillon
this.globalRotation += 0.002;
// Évolution de la couleur
this.hue = (this.hue + this.hueSpeed) % 360;
// Ajouter un nouveau caractère à la spirale
if (this.frameCount % 2 === 0) {
this.angle += this.angleStep;
// L'espacement augmente avec le rayon (spirale qui s'écarte)
const dynamicStep = this.radiusStep + (this.radius / 100) * 0.18;
this.radius += dynamicStep;
const effectiveAngle = this.angle + this.globalRotation;
const x = this.cx + Math.cos(effectiveAngle) * this.radius;
const y = this.cy + Math.sin(effectiveAngle) * this.radius * 0.6;
// Scale qui grandit avec le rayon
const scale = 0.6 + (this.radius / 600) * 0.6;
this.createChar(x, y, this.hue, scale);
}
// Quand le rayon dépasse l'écran, on recommence plus petit
const maxRadius = Math.max(this.cx, this.cy) + 80;
if (this.radius > maxRadius) {
this.radius = 8 + Math.random() * 12;
this.hue = (this.hue + 60 + Math.random() * 120) % 360;
}
this.ageElements();
}
loop() {
this.update();
requestAnimationFrame(this.loop);
}
}
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('swirl-container');
if (container) {
new Swirl(container);
}
});
})();

539
assets/vine.js Normal file
View file

@ -0,0 +1,539 @@
(function () {
const CHARS = {
down: ['|', '¦', '¡', 'l', 'I', '1'],
downRight: ['\\', '╲'],
downLeft: ['/', ''],
leaf: ['.', 'o', '*', "'", '"', '·', '°', '`', '¨', '+'],
flower: ['✿', '❀', '✾', '❁', '⚘'],
debris: ['°', '·', '¨', '`', ','],
};
function pick(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function charForAngle(angle) {
const deg = angle * 180 / Math.PI;
if (deg >= 60 && deg <= 120) return pick(CHARS.down);
if (deg < 60) return pick(CHARS.downRight);
return pick(CHARS.downLeft);
}
class Vine {
constructor(container) {
this.container = container;
this.width = container.offsetWidth || window.innerWidth;
this.height = container.offsetHeight || window.innerHeight;
this.tips = [];
this.elements = [];
this.flowers = [];
this.frameCount = 0;
this.minStep = 24;
this.flowerCount = 0;
this.startVine();
this.startVine();
this.startVine();
this.startVine();
this.createHelpUI();
this.loop = this.loop.bind(this);
requestAnimationFrame(this.loop);
window.addEventListener('resize', () => {
this.width = container.offsetWidth || window.innerWidth;
this.height = container.offsetHeight || window.innerHeight;
});
// Interactions
this.container.addEventListener('click', (e) => this.handleClick(e));
this.container.addEventListener('dblclick', (e) => this.handleDoubleClick(e));
this.container.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.handleRightClick(e);
});
}
startVine() {
if (this.tips.length >= 12) return;
this.tips.push({
x: 40 + Math.random() * Math.max(0, this.width - 80),
y: -5,
angle: Math.PI / 2 + (Math.random() - 0.5) * 0.25,
hue: Math.random() * 360,
hueSpeed: (Math.random() - 0.5) * 0.3,
generation: 0,
age: 0,
alive: true,
nextBranch: 140 + Math.random() * 140,
speed: 0.7 + Math.random() * 0.3,
distSinceLastChar: 0,
});
}
createHelpUI() {
const btn = document.createElement('button');
btn.id = 'vine-help-btn';
btn.textContent = '?';
this.container.appendChild(btn);
const panel = document.createElement('div');
panel.id = 'vine-help-panel';
panel.style.display = 'none';
const close = document.createElement('button');
close.className = 'close';
close.innerHTML = '&times;';
close.setAttribute('aria-label', 'fermer');
panel.appendChild(close);
const title = document.createElement('h3');
title.innerHTML = this.animText('jardinage');
panel.appendChild(title);
const items = [
{ k: 'clic', a: 'sur tige', d: 'coupe la liane' },
{ k: 'double-clic', a: 'sur tige', d: 'plante une fleur' },
{ k: 'clic droit', a: 'sur tige', d: 'plante une fleur' },
{ k: 'clic', a: 'sur fleur', d: "l'attrape" },
{ k: 'clic', a: 'sur fond', d: 'dévie la racine' },
];
const ul = document.createElement('ul');
items.forEach((it) => {
const li = document.createElement('li');
li.innerHTML =
'<kbd>' + it.k + '</kbd> ' +
it.a + ' <span style="opacity:0.5">→</span> ' + it.d;
ul.appendChild(li);
});
panel.appendChild(ul);
this.container.appendChild(panel);
const counter = document.createElement('div');
counter.id = 'vine-flower-counter';
counter.textContent = '0';
this.counterEl = counter;
this.container.appendChild(counter);
btn.addEventListener('click', (e) => {
e.stopPropagation();
panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
});
close.addEventListener('click', (e) => {
e.stopPropagation();
panel.style.display = 'none';
});
panel.addEventListener('click', (e) => e.stopPropagation());
}
animText(text) {
return text.split('').map((c, i) => {
if (c === ' ') return ' ';
const delayColor = (i * 0.3).toFixed(1);
const delayLevite = (Math.random() * 4).toFixed(2);
const duree = (3 + Math.random() * 3).toFixed(2);
return `<span style="display:inline-block;animation:colorshift 12s ease-in-out infinite,levite ${duree}s ease-in-out infinite;animation-delay:-${delayColor}s,-${delayLevite}s">${c}</span>`;
}).join('');
}
// ===== INTERACTIONS =====
handleClick(e) {
const target = e.target;
// 1. Clic sur une fleur vivante -> attraper
if (target.classList.contains('vine-flower') && !target.classList.contains('falling-flower')) {
this.catchFlower(target);
return;
}
// 2. Clic sur caractère de tige (pas feuille) -> couper
if (target.classList.contains('vine-char') && !target.classList.contains('vine-leaf')) {
this.cutVine(parseFloat(target.style.left), parseFloat(target.style.top));
return;
}
// 3. Clic sur fond vide -> dévier la racine la plus proche
if (target === this.container) {
this.redirectVine(e.offsetX, e.offsetY);
}
}
handleDoubleClick(e) {
const target = e.target;
// Double-clic sur caractère de tige -> planter une fleur
if (target.classList.contains('vine-char')) {
const x = parseFloat(target.style.left);
const y = parseFloat(target.style.top);
this.plantFlower(x, y);
}
}
handleRightClick(e) {
const target = e.target;
// Clic droit sur caractère de tige -> planter une fleur
if (target.classList.contains('vine-char')) {
const x = parseFloat(target.style.left);
const y = parseFloat(target.style.top);
this.plantFlower(x, y);
}
}
findClosestTip(x, y, onlyRoots = false) {
let closest = null;
let closestDist = Infinity;
for (const tip of this.tips) {
if (onlyRoots && tip.generation > 0) continue;
const dx = tip.x - x;
const dy = tip.y - y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < closestDist) {
closestDist = dist;
closest = tip;
}
}
return closest;
}
cutVine(clickX, clickY) {
const tip = this.findClosestTip(clickX, clickY);
if (!tip) return;
// Effet de débris visuels
for (let i = 0; i < 4; i++) {
this.createDebris(clickX + (Math.random() - 0.5) * 20, clickY + (Math.random() - 0.5) * 20, tip.hue);
}
// Créer deux nouvelles lianes qui divergent
const hue = tip.hue;
const hueSpeed = tip.hueSpeed;
this.tips.push({
x: clickX,
y: clickY,
angle: Math.PI / 2 - 0.3 - Math.random() * 0.4,
hue,
hueSpeed: hueSpeed * (0.8 + Math.random() * 0.4),
generation: 0,
age: 0,
alive: true,
nextBranch: 60 + Math.random() * 80,
speed: 0.9 + Math.random() * 0.4,
distSinceLastChar: 0,
});
this.tips.push({
x: clickX,
y: clickY,
angle: Math.PI / 2 + 0.3 + Math.random() * 0.4,
hue,
hueSpeed: hueSpeed * (0.8 + Math.random() * 0.4),
generation: 0,
age: 0,
alive: true,
nextBranch: 60 + Math.random() * 80,
speed: 0.9 + Math.random() * 0.4,
distSinceLastChar: 0,
});
// Tuer l'ancienne liane
tip.alive = false;
}
plantFlower(x, y) {
const tip = this.findClosestTip(x, y);
const hue = tip ? tip.hue : Math.random() * 360;
this.createFlower(x, y, hue);
}
catchFlower(el) {
const idx = this.flowers.findIndex((f) => f.el === el);
if (idx === -1) return;
const flower = this.flowers[idx];
if (flower.dying) return;
flower.dying = true;
flower.el.classList.remove('vine-flower');
flower.el.classList.add('falling-flower');
flower.el.style.animationDuration = '0.6s';
flower.el.style.animationTimingFunction = 'ease-out';
setTimeout(() => {
if (flower.el.parentNode) flower.el.remove();
}, 700);
this.flowers.splice(idx, 1);
this.flowerCount++;
if (this.counterEl) {
this.counterEl.textContent = this.flowerCount;
}
}
redirectVine(clickX, clickY) {
const root = this.findClosestTip(clickX, clickY, true);
if (!root) return;
const dx = clickX - root.x;
const dy = clickY - root.y;
let targetAngle = Math.atan2(Math.abs(dy), dx);
targetAngle = Math.max(0.3, Math.min(Math.PI - 0.3, targetAngle));
// Transition douce : on s'approche progressivement de l'angle cible
const diff = targetAngle - root.angle;
root.angle += diff * 0.4;
root.speed = Math.min(root.speed * 1.3, 2.5);
}
createDebris(x, y, hue) {
const color = this.getColor(hue, y, false);
const el = document.createElement('span');
el.textContent = pick(CHARS.debris);
el.style.cssText =
`position:absolute;left:${x}px;top:${y}px;` +
`font-family:"Courier New",Courier,monospace;` +
`font-size:12px;line-height:1;color:${color};` +
`pointer-events:none;user-select:none;` +
`opacity:0.8;`;
el.style.display = 'inline-block';
this.container.appendChild(el);
this.elements.push({
el,
born: this.frameCount,
maxAge: 60 + Math.floor(Math.random() * 40),
});
}
// ===== LOGIQUE EXISTANTE =====
getColor(hue, y, isFlower) {
const drifted = (hue + y * 0.04 + Math.random() * 6) % 360;
const sat = isFlower ? 68 : 58;
const lit = isFlower ? 74 : 68;
return `hsl(${drifted}, ${sat}%, ${lit}%)`;
}
createChar(x, y, angle, hue, isLeaf) {
const color = this.getColor(hue, y, false);
const el = document.createElement('span');
el.textContent = isLeaf ? pick(CHARS.leaf) : charForAngle(angle);
el.classList.add('vine-char');
if (isLeaf) el.classList.add('vine-leaf');
el.style.cssText =
`position:absolute;left:${x}px;top:${y}px;` +
`font-family:"Courier New",Courier,monospace;` +
`font-size:17px;line-height:1;color:${color};` +
`text-shadow:0 0 3px currentColor;` +
`pointer-events:auto;user-select:none;` +
`opacity:${isLeaf ? 0.55 : 0.85};`;
const scale = 0.9 + Math.random() * 0.12;
const rot = (Math.random() - 0.5) * 5;
el.style.transform = `scale(${scale}) rotate(${rot}deg)`;
el.style.display = 'inline-block';
this.container.appendChild(el);
const lifeRatio = Math.min(1, y / Math.max(1, this.height));
const maxAge = Math.floor(1800 - lifeRatio * 1000 + Math.random() * 300);
this.elements.push({
el,
born: this.frameCount,
maxAge,
});
}
createFlower(x, y, hue) {
if (this.flowers.length >= 8) return;
const color = this.getColor(hue, y, true);
const el = document.createElement('span');
el.textContent = pick(CHARS.flower);
el.classList.add('vine-flower');
el.style.cssText =
`position:absolute;left:${x}px;top:${y}px;` +
`font-family:"Courier New",Courier,monospace;` +
`font-size:18px;line-height:1;color:${color};` +
`text-shadow:0 0 5px currentColor;` +
`pointer-events:auto;user-select:none;` +
`opacity:0.9;display:inline-block;`;
this.container.appendChild(el);
this.flowers.push({
el,
x, y,
life: 30 + Math.random() * 40,
dying: false,
});
}
updateFlowers() {
for (let i = this.flowers.length - 1; i >= 0; i--) {
const f = this.flowers[i];
if (f.dying) continue;
f.life -= 1;
if (f.life <= 0) {
f.dying = true;
f.el.classList.remove('vine-flower');
f.el.classList.add('falling-flower');
setTimeout(() => {
if (f.el.parentNode) f.el.remove();
}, 2600);
this.flowers.splice(i, 1);
}
}
}
ageElements() {
if (this.frameCount % 10 !== 0) return;
const now = this.frameCount;
let pruneCount = 0;
const maxPrune = 18;
for (let i = 0; i < this.elements.length && pruneCount < maxPrune; i++) {
const item = this.elements[i];
const age = now - item.born;
if (age >= item.maxAge) {
if (item.el.classList.contains('vine-leaf') && !item.falling) {
item.falling = true;
item.el.classList.add('falling-leaf');
setTimeout(() => {
if (item.el.parentNode) item.el.remove();
}, 2100);
} else {
item.el.remove();
}
this.elements.splice(i, 1);
i--;
pruneCount++;
} else if (age > item.maxAge - 120 && !item.falling) {
const fade = 1 - (age - (item.maxAge - 120)) / 120;
item.el.style.opacity = Math.max(0, fade * 0.85);
}
}
}
update() {
this.frameCount++;
if (this.frameCount % 3 !== 0) return;
this.updateFlowers();
this.ageElements();
const newTips = [];
for (let i = 0; i < this.tips.length; i++) {
const tip = this.tips[i];
tip.age++;
tip.hue = (tip.hue + tip.hueSpeed) % 360;
if (tip.hue < 0) tip.hue += 360;
tip.angle += (Math.random() - 0.5) * 0.025;
if (tip.angle < 0.3) tip.angle = 0.3;
if (tip.angle > Math.PI - 0.3) tip.angle = Math.PI - 0.3;
const dx = Math.cos(tip.angle);
const dy = Math.sin(tip.angle);
const step = tip.speed * 1.6;
tip.x += dx * step;
tip.y += dy * step;
tip.distSinceLastChar += step;
if (tip.distSinceLastChar >= this.minStep) {
tip.distSinceLastChar = 0;
this.createChar(tip.x, tip.y, tip.angle, tip.hue, false);
}
if (tip.generation > 0 && Math.random() < 0.015) {
const lx = tip.x + (Math.random() - 0.5) * 14;
const ly = tip.y + (Math.random() - 0.5) * 14;
this.createChar(lx, ly, tip.angle, tip.hue, true);
}
if (
tip.age >= tip.nextBranch &&
tip.generation < 2
) {
tip.nextBranch = tip.age + 140 + Math.random() * 160;
const side = Math.random() > 0.5 ? 1 : -1;
const spread = 0.35 + Math.random() * 0.55;
newTips.push({
x: tip.x,
y: tip.y,
angle: tip.angle + side * spread,
hue: tip.hue,
hueSpeed: tip.hueSpeed,
generation: tip.generation + 1,
age: 0,
alive: true,
nextBranch: 120 + Math.random() * 140,
speed: tip.speed * (0.75 + Math.random() * 0.15),
distSinceLastChar: 0,
});
this.createFlower(
tip.x + (Math.random() - 0.5) * 8,
tip.y + (Math.random() - 0.5) * 8,
tip.hue
);
}
if (tip.generation >= 1 && Math.random() < 0.008) {
this.createFlower(
tip.x + (Math.random() - 0.5) * 6,
tip.y + (Math.random() - 0.5) * 6,
tip.hue
);
}
// Branches : meurent quand sortent
if (tip.generation > 0) {
if (
tip.y > this.height + 15 ||
tip.x < -20 ||
tip.x > this.width + 20
) {
tip.alive = false;
}
}
// Racines : respawn immédiat quand touchent le bas
else if (tip.y > this.height + 15) {
tip.y = -5;
tip.x = 40 + Math.random() * Math.max(0, this.width - 80);
tip.angle = Math.PI / 2 + (Math.random() - 0.5) * 0.25;
tip.hue = Math.random() * 360;
tip.hueSpeed = (Math.random() - 0.5) * 0.3;
tip.age = 0;
tip.nextBranch = 140 + Math.random() * 140;
tip.distSinceLastChar = 0;
// Chance de faire apparaître une nouvelle racine (max 6 racines)
if (this.tips.filter((t) => t.generation === 0).length < 6 && Math.random() < 0.35) {
this.startVine();
}
}
}
this.tips = this.tips.filter((t) => t.alive).concat(newTips);
// Respawn de secours si jamais il y a trop peu de tips
if (this.tips.filter((t) => t.generation === 0).length < 3 && Math.random() < 0.5) {
this.startVine();
}
}
loop() {
this.update();
requestAnimationFrame(this.loop);
}
}
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('vine-container');
if (container) {
new Vine(container);
}
});
})();

View file

@ -40,6 +40,8 @@
<img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu"> <img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu">
</div> </div>
</div> </div>
<div id="vine-container"></div>
<script src="../../../assets/main.js"></script> <script src="../../../assets/main.js"></script>
<script src="../../../assets/vine.js"></script>
</body> </body>
</html> </html>

View file

@ -47,6 +47,8 @@
<img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu"> <img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu">
</div> </div>
</div> </div>
<div id="vine-container"></div>
<script src="../../../assets/main.js"></script> <script src="../../../assets/main.js"></script>
<script src="../../../assets/vine.js"></script>
</body> </body>
</html> </html>

View file

@ -40,6 +40,8 @@
<img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu"> <img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu">
</div> </div>
</div> </div>
<div id="vine-container"></div>
<script src="../../../assets/main.js"></script> <script src="../../../assets/main.js"></script>
<script src="../../../assets/vine.js"></script>
</body> </body>
</html> </html>

View file

@ -59,6 +59,8 @@
<img class="thanks" src="../../../assets/draws/thxforreading.png" alt="merci d'avoir lu"> <img class="thanks" src="../../../assets/draws/thxforreading.png" alt="merci d'avoir lu">
</div> </div>
</div> </div>
<div id="vine-container"></div>
<script src="../../../assets/main.js"></script> <script src="../../../assets/main.js"></script>
<script src="../../../assets/vine.js"></script>
</body> </body>
</html> </html>

View file

@ -41,6 +41,8 @@
</div> </div>
<img src="assets/draws/ewen.png" class="perso" alt=""> <img src="assets/draws/ewen.png" class="perso" alt="">
</div> </div>
<div id="swirl-container"></div>
<script src="assets/main.js"></script> <script src="assets/main.js"></script>
<script src="assets/swirl.js"></script>
</body> </body>
</html> </html>