Compare commits
No commits in common. "master" and "main" have entirely different histories.
1
.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
.DS_Store
|
||||
9
LICENSE
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 ewen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
5
README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# eweng.space
|
||||
|
||||
site personnel de blog, medias, art
|
||||
|
||||
pour moi et moi seulement
|
||||
BIN
assets/.DS_Store
vendored
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 472 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 938 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 6.5 MiB |
|
Before Width: | Height: | Size: 200 KiB |
394
assets/main.js
|
|
@ -1,394 +0,0 @@
|
|||
/* === ANIMATION TITRE-NAV (toutes les pages) === */
|
||||
function animerTitreNav() {
|
||||
document.querySelectorAll('.fenetre-nom').forEach(el => {
|
||||
el.innerHTML = el.textContent.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('');
|
||||
});
|
||||
}
|
||||
|
||||
/* === /NOW (index.html) === */
|
||||
function afficherNow() {
|
||||
const phrasesNow = [
|
||||
"/now : faut que je fasses les courses.",
|
||||
"/now : les arbres vibrent !",
|
||||
"/now : les mains, les jambes, avancent..",
|
||||
"/now : lave toi le visage.",
|
||||
"/now : il pleut, et c'est très bien comme ça.",
|
||||
"/now : pas mal la page d'accueil !",
|
||||
"/now : i love my computer",
|
||||
"/now : demain je serais en cours, j'espère",
|
||||
"/now : je resterais auprès de toi pour toujours",
|
||||
"/now : je serais toi j'irais prendre l'air",
|
||||
"/now : je dois vraiment aller dormir la"
|
||||
];
|
||||
|
||||
const spanNow = document.querySelector('.now');
|
||||
if (spanNow) {
|
||||
const indexAleatoire = Math.floor(Math.random() * phrasesNow.length);
|
||||
spanNow.textContent = phrasesNow[indexAleatoire];
|
||||
}
|
||||
}
|
||||
|
||||
/* === GRILLE ALBUMS (reviews/index.html) === */
|
||||
function toSlug(titre) {
|
||||
return titre
|
||||
.toLowerCase()
|
||||
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[^a-z0-9-]/g, '');
|
||||
}
|
||||
|
||||
function chargerAlbums() {
|
||||
fetch('data/albums.json')
|
||||
.then(r => r.json())
|
||||
.then(albums => {
|
||||
const grille = document.getElementById('grille');
|
||||
|
||||
albums.forEach(album => {
|
||||
const carte = document.createElement('div');
|
||||
carte.className = 'carte';
|
||||
|
||||
const media = album.cover
|
||||
? `<img src="${album.cover}" alt="${album.titre}" onerror="this.outerHTML='<div class=\\'placeholder\\'>${album.titre}</div>'">`
|
||||
: `<div class="placeholder">${album.titre}</div>`;
|
||||
|
||||
const fav = album.favoris ? '<span class="badge-fav">♥</span>' : '';
|
||||
|
||||
carte.innerHTML = `
|
||||
${fav}
|
||||
${media}
|
||||
<div class="nom">
|
||||
${album.titre}<br>
|
||||
<span class="artiste">${album.artiste}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
carte.addEventListener('mouseenter', () => {
|
||||
grille.classList.add('hovering');
|
||||
carte.classList.add('actif');
|
||||
});
|
||||
carte.addEventListener('mouseleave', () => {
|
||||
grille.classList.remove('hovering');
|
||||
carte.classList.remove('actif');
|
||||
});
|
||||
|
||||
carte.addEventListener('click', () => {
|
||||
window.location.href = `albums/${toSlug(album.titre)}.html`;
|
||||
});
|
||||
|
||||
grille.appendChild(carte);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
document.getElementById('grille').textContent = 'impossible de charger les albums.';
|
||||
});
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
DEPECHES — VERSION ENRICHIE
|
||||
═══════════════════════════════════════════════════ */
|
||||
|
||||
/* ── helpers texte ───────────────────────────────── */
|
||||
function escapeHtml(str) {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function parseMarkdown(text) {
|
||||
// gras
|
||||
text = text.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||
// italique
|
||||
text = text.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
||||
// barré
|
||||
text = text.replace(/~~([^~]+)~~/g, '<del>$1</del>');
|
||||
// code inline
|
||||
text = text.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
|
||||
return text;
|
||||
}
|
||||
|
||||
function parseHashtags(text) {
|
||||
return text.replace(/(^|\s)(#[\wÀ-ÿ]+)/g, (m, before, tag) => {
|
||||
const q = encodeURIComponent(tag);
|
||||
return `${before}<a href="https://www.google.com/search?q=${q}" target="_blank" class="hashtag">${tag}</a>`;
|
||||
});
|
||||
}
|
||||
|
||||
function parseMentions(text) {
|
||||
return text.replace(/(^|\s)(@[\w.]+)/g, (m, before, mention) => {
|
||||
return `${before}<span class="mention">${mention}</span>`;
|
||||
});
|
||||
}
|
||||
|
||||
function timeAgo(dateStr) {
|
||||
const m = dateStr.match(/(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2})/);
|
||||
if (!m) return dateStr;
|
||||
const [, d, mo, y, H, Mi] = m;
|
||||
const date = new Date(`${y}-${mo}-${d}T${H}:${Mi}:00`);
|
||||
const sec = Math.floor((Date.now() - date) / 1000);
|
||||
if (sec < 60) return "à l'instant";
|
||||
if (sec < 3600) return `il y a ${Math.floor(sec / 60)} min`;
|
||||
if (sec < 86400)return `il y a ${Math.floor(sec / 3600)} h`;
|
||||
if (sec < 604800)return `il y a ${Math.floor(sec / 86400)} j`;
|
||||
if (sec < 2592000)return `il y a ${Math.floor(sec / 604800)} sem`;
|
||||
return dateStr;
|
||||
}
|
||||
|
||||
/* ── split texte / urls ──────────────────────────── */
|
||||
function splitTextAndUrls(text) {
|
||||
const re = /(https?:\/\/[^\s]+)/g;
|
||||
const parts = [];
|
||||
let last = 0, m;
|
||||
while ((m = re.exec(text)) !== null) {
|
||||
if (m.index > last) parts.push({ type: 'text', value: text.slice(last, m.index) });
|
||||
parts.push({ type: 'url', value: m[1] });
|
||||
last = m.index + m[0].length;
|
||||
}
|
||||
if (last < text.length) parts.push({ type: 'text', value: text.slice(last) });
|
||||
return parts;
|
||||
}
|
||||
|
||||
/* ── embeds ──────────────────────────────────────── */
|
||||
function styledLink(url) {
|
||||
try {
|
||||
const domain = new URL(url).hostname.replace(/^www\./, '');
|
||||
return `<a href="${escapeHtml(url)}" target="_blank" class="depeche-link"><span class="link-domain">${escapeHtml(domain)}</span><span class="link-url">${escapeHtml(url)}</span></a>`;
|
||||
} catch {
|
||||
return `<a href="${escapeHtml(url)}" target="_blank" class="depeche-link">${escapeHtml(url)}</a>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveEmbed(url) {
|
||||
/* vidéo 16:9 */
|
||||
const yt = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
|
||||
if (yt) return `<div class="embed-container video"><iframe src="https://www.youtube.com/embed/${yt[1]}" frameborder="0" allowfullscreen loading="lazy"></iframe></div>`;
|
||||
|
||||
const vm = url.match(/vimeo\.com\/(\d+)/);
|
||||
if (vm) return `<div class="embed-container video"><iframe src="https://player.vimeo.com/video/${vm[1]}" frameborder="0" allowfullscreen loading="lazy"></iframe></div>`;
|
||||
|
||||
const dm = url.match(/(?:dailymotion\.com\/video\/|dai\.ly\/)([a-zA-Z0-9]+)/);
|
||||
if (dm) return `<div class="embed-container video"><iframe src="https://www.dailymotion.com/embed/video/${dm[1]}" frameborder="0" allowfullscreen loading="lazy"></iframe></div>`;
|
||||
|
||||
const twClip = url.match(/(?:clips\.twitch\.tv\/|twitch\.tv\/[^/]+\/clip\/)([a-zA-Z0-9_-]+)/);
|
||||
if (twClip) return `<div class="embed-container video"><iframe src="https://clips.twitch.tv/embed?clip=${twClip[1]}&parent=${location.hostname}" frameborder="0" allowfullscreen loading="lazy"></iframe></div>`;
|
||||
|
||||
const twVid = url.match(/twitch\.tv\/videos\/(\d+)/);
|
||||
if (twVid) return `<div class="embed-container video"><iframe src="https://player.twitch.tv/?video=${twVid[1]}&parent=${location.hostname}" frameborder="0" allowfullscreen loading="lazy"></iframe></div>`;
|
||||
|
||||
const coub = url.match(/coub\.com\/view\/([a-zA-Z0-9]+)/);
|
||||
if (coub) return `<div class="embed-container video"><iframe src="https://coub.com/embed/${coub[1]}" frameborder="0" allowfullscreen loading="lazy"></iframe></div>`;
|
||||
|
||||
/* musique */
|
||||
const sp = url.match(/open\.spotify\.com\/(track|album|playlist|episode|show)\/([a-zA-Z0-9]+)/);
|
||||
if (sp) return `<div class="embed-container music"><iframe src="https://open.spotify.com/embed/${sp[1]}/${sp[2]}" frameborder="0" allowtransparency="true" allow="encrypted-media" loading="lazy"></iframe></div>`;
|
||||
|
||||
const dz = url.match(/deezer\.com\/[a-z]{2}\/(track|album|playlist)\/(\d+)/);
|
||||
if (dz) return `<div class="embed-container music"><iframe src="https://widget.deezer.com/widget/dark/${dz[1]}/${dz[2]}" frameborder="0" allowtransparency="true" allow="encrypted-media; clipboard-write" loading="lazy"></iframe></div>`;
|
||||
|
||||
const am = url.match(/music\.apple\.com\/[a-z]{2}\/(album|song|playlist)\/[^/]+\/(\d+)/);
|
||||
if (am) return `<div class="embed-container music"><iframe src="https://embed.music.apple.com/us/${am[1]}/${am[2]}" frameborder="0" allow="encrypted-media" loading="lazy"></iframe></div>`;
|
||||
|
||||
if (url.includes('mixcloud.com')) {
|
||||
return `<div class="embed-container music"><iframe src="https://www.mixcloud.com/widget/iframe/?feed=${encodeURIComponent(url)}&hide_cover=1&light=1" frameborder="0" loading="lazy"></iframe></div>`;
|
||||
}
|
||||
|
||||
const aud = url.match(/audiomack\.com\/([^/]+)\/(song|album|playlist)\/([^/]+)/);
|
||||
if (aud) return `<div class="embed-container music"><iframe src="https://audiomack.com/embed/${aud[1]}/${aud[2]}/${aud[3]}" scrolling="no" frameborder="0" loading="lazy"></iframe></div>`;
|
||||
|
||||
/* oembed */
|
||||
if (url.includes('soundcloud.com')) {
|
||||
try {
|
||||
const r = await fetch(`https://soundcloud.com/oembed?format=json&url=${encodeURIComponent(url)}&maxwidth=500`);
|
||||
const d = await r.json();
|
||||
return `<div class="embed-container">${d.html}</div>`;
|
||||
} catch { /* fallthrough */ }
|
||||
}
|
||||
|
||||
if (url.includes('bandcamp.com')) {
|
||||
try {
|
||||
const r = await fetch(`https://bandcamp.com/api/embed/1/oembed?url=${encodeURIComponent(url)}&format=json`);
|
||||
if (!r.ok) throw new Error('ko');
|
||||
const d = await r.json();
|
||||
return `<div class="embed-container">${d.html}</div>`;
|
||||
} catch { /* fallthrough */ }
|
||||
}
|
||||
|
||||
if (url.includes('tiktok.com')) {
|
||||
try {
|
||||
const r = await fetch(`https://www.tiktok.com/oembed?url=${encodeURIComponent(url)}`);
|
||||
const d = await r.json();
|
||||
return `<div class="embed-container">${d.html}</div>`;
|
||||
} catch { /* fallthrough */ }
|
||||
}
|
||||
|
||||
if (url.includes('reddit.com')) {
|
||||
try {
|
||||
const r = await fetch(`https://www.reddit.com/oembed?url=${encodeURIComponent(url)}`);
|
||||
const d = await r.json();
|
||||
return `<div class="embed-container">${d.html}</div>`;
|
||||
} catch { /* fallthrough */ }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ── parser principal ────────────────────────────── */
|
||||
async function parseTexte(texte) {
|
||||
const parts = splitTextAndUrls(texte);
|
||||
const resolved = await Promise.all(parts.map(async part => {
|
||||
if (part.type === 'text') {
|
||||
let html = escapeHtml(part.value);
|
||||
html = parseMarkdown(html);
|
||||
html = parseHashtags(html);
|
||||
html = parseMentions(html);
|
||||
html = html.replace(/\n/g, '<br>');
|
||||
return html;
|
||||
}
|
||||
const embed = await resolveEmbed(part.value);
|
||||
return embed || styledLink(part.value);
|
||||
}));
|
||||
return resolved.join('');
|
||||
}
|
||||
|
||||
/* ── rendu du feed ───────────────────────────────── */
|
||||
async function chargerDepeches() {
|
||||
const feed = document.getElementById('feed');
|
||||
|
||||
try {
|
||||
const r = await fetch('/api/depeches');
|
||||
const depeches = await r.json();
|
||||
feed.innerHTML = '';
|
||||
|
||||
if (depeches.length === 0) {
|
||||
feed.innerHTML = '<p class="chargement">aucune dépêche pour l\'instant.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
for (const d of depeches) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'depeche';
|
||||
el.id = d.id;
|
||||
|
||||
const texteHtml = await parseTexte(d.texte);
|
||||
const img = d.image
|
||||
? `<img src="${d.image}" class="depeche-image" alt="" loading="lazy">`
|
||||
: '';
|
||||
|
||||
const permalink = `${location.origin}${location.pathname}#${d.id}`;
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="depeche-header">
|
||||
<span class="depeche-date" title="${d.date}">ewen — ${timeAgo(d.date)}</span>
|
||||
<div class="depeche-actions">
|
||||
<button class="depeche-action" data-action="copy" title="copier le lien">#</button>
|
||||
<button class="depeche-action" data-action="share" title="partager">↗</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="depeche-texte">${texteHtml}</div>
|
||||
${img}
|
||||
`;
|
||||
|
||||
/* actions */
|
||||
el.querySelector('[data-action="copy"]').addEventListener('click', function () {
|
||||
navigator.clipboard.writeText(permalink).then(() => {
|
||||
this.textContent = '✓';
|
||||
setTimeout(() => this.textContent = '#', 1200);
|
||||
});
|
||||
});
|
||||
|
||||
el.querySelector('[data-action="share"]').addEventListener('click', function () {
|
||||
if (navigator.share) {
|
||||
navigator.share({ title: 'dépêche', url: permalink });
|
||||
} else {
|
||||
navigator.clipboard.writeText(permalink).then(() => {
|
||||
this.textContent = '✓';
|
||||
setTimeout(() => this.textContent = '↗', 1200);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
feed.appendChild(el);
|
||||
}
|
||||
|
||||
/* scroll vers l'ancre si présente */
|
||||
if (location.hash) {
|
||||
const target = document.querySelector(location.hash);
|
||||
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
} catch {
|
||||
feed.innerHTML = '<p class="chargement">impossible de charger les dépêches.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
LIGHTBOX
|
||||
═══════════════════════════════════════════════════ */
|
||||
function initLightbox() {
|
||||
const contenu = document.querySelector('.fenetre-contenu');
|
||||
if (!contenu) return;
|
||||
|
||||
contenu.addEventListener('click', e => {
|
||||
const img = e.target.closest('img:not(.thanks)');
|
||||
if (!img || img.closest('.depeche-link') || img.closest('.embed-container')) return;
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'lightbox';
|
||||
|
||||
const fond = document.createElement('div');
|
||||
fond.className = 'lightbox-fond';
|
||||
overlay.appendChild(fond);
|
||||
|
||||
const clone = document.createElement('img');
|
||||
clone.className = 'lightbox-image';
|
||||
clone.src = img.src;
|
||||
clone.alt = img.alt;
|
||||
overlay.appendChild(clone);
|
||||
|
||||
const fermer = document.createElement('button');
|
||||
fermer.className = 'lightbox-fermer';
|
||||
fermer.innerHTML = '×';
|
||||
overlay.appendChild(fermer);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
overlay.addEventListener('click', e => {
|
||||
if (e.target === fond || e.target === fermer || e.target === overlay) {
|
||||
overlay.remove();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function onKey(e) {
|
||||
if (e.key === 'Escape') {
|
||||
overlay.remove();
|
||||
document.removeEventListener('keydown', onKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
INIT
|
||||
═══════════════════════════════════════════════════ */
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
animerTitreNav();
|
||||
|
||||
if (document.querySelector('.now')) {
|
||||
afficherNow();
|
||||
}
|
||||
|
||||
if (document.getElementById('grille')) {
|
||||
chargerAlbums();
|
||||
}
|
||||
|
||||
if (document.getElementById('feed')) {
|
||||
chargerDepeches();
|
||||
}
|
||||
|
||||
initLightbox();
|
||||
});
|
||||
BIN
assets/pictures/.DS_Store
vendored
|
Before Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 972 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
|
@ -1,808 +0,0 @@
|
|||
/* === BASE === */
|
||||
body {
|
||||
font-family: 'IM Fell English', serif;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* === ANIMATIONS === */
|
||||
@keyframes colorshift {
|
||||
0% { color: hsl(0, 60%, 70%); }
|
||||
25% { color: hsl(90, 60%, 70%); }
|
||||
50% { color: hsl(180, 60%, 70%); }
|
||||
75% { color: hsl(270, 60%, 70%); }
|
||||
100% { color: hsl(360, 60%, 70%); }
|
||||
}
|
||||
|
||||
@keyframes levite {
|
||||
0% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-5px); }
|
||||
100% { transform: translateY(0px); }
|
||||
}
|
||||
|
||||
/* === LAYOUT === */
|
||||
.fenetre {
|
||||
max-width: 600px;
|
||||
border: 1px solid black;
|
||||
padding: 0.3rem 1.5rem 0 1.5rem;
|
||||
padding-bottom: 0;
|
||||
margin-top: 0px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: start;
|
||||
gap: 1rem;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.fenetre--large { max-width: 950px; }
|
||||
.fenetre--medium { max-width: 900px; }
|
||||
.fenetre--small { max-width: 550px; }
|
||||
|
||||
.fenetre-nom {
|
||||
margin-bottom: 1px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.fenetre-contenu {
|
||||
padding: 0 1rem 1.5rem 1rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* === NAVIGATION === */
|
||||
.retour {
|
||||
font-family: 'IM Fell English', serif;
|
||||
font-size: 20px;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
margin-bottom: 1px;
|
||||
margin-right: 0.4rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.retour:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* === COMPONENTS === */
|
||||
.now {
|
||||
font-size: 0.75rem;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
padding-top: 0.6rem;
|
||||
margin-top: 0.8rem;
|
||||
line-height: 1.6;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.index-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
max-width: 550px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.index-header .now {
|
||||
margin-right: -3rem;
|
||||
}
|
||||
|
||||
.now-titre {
|
||||
font-style: normal;
|
||||
color: #999;
|
||||
font-size: 0.7rem;
|
||||
display: block;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.soustitre {
|
||||
font-size: medium;
|
||||
font-style: italic;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.thanks {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
/* === REVIEWS: GRILLE === */
|
||||
.grille {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.carte {
|
||||
width: 150px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, opacity 0.25s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.carte img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border: 1px solid #111;
|
||||
}
|
||||
|
||||
.carte .placeholder {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: #eee;
|
||||
border: 1px solid #111;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-style: italic;
|
||||
color: #999;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.carte .nom {
|
||||
margin-top: 0.3rem;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.carte .nom .artiste {
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.grille.hovering .carte {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.grille.hovering .carte.actif {
|
||||
opacity: 1;
|
||||
transform: scale(1.07);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.badge-fav {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
font-size: 0.7rem;
|
||||
background: #fff;
|
||||
border: 1px solid #111;
|
||||
padding: 1px 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* === REVIEWS: ALBUM PAGE === */
|
||||
.col-cover {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.cover {
|
||||
height: 280px;
|
||||
width: auto;
|
||||
display: block;
|
||||
border: 1px solid #111;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.cover-legende {
|
||||
font-size: 0.75rem;
|
||||
font-style: italic;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meta-ligne {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.8rem;
|
||||
font-size: 0.85rem;
|
||||
color: #555;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 0.6rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.note {
|
||||
color: #111;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.genre-tag {
|
||||
border: 1px solid #bbb;
|
||||
padding: 1px 5px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.review p {
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.review p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-albums {
|
||||
margin-top: 1.2rem;
|
||||
padding-top: 0.6rem;
|
||||
border-top: 1px solid #ddd;
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nav-albums a:hover {
|
||||
border-bottom-style: dashed;
|
||||
}
|
||||
|
||||
/* === DEPECHES: FEED === */
|
||||
.feed {
|
||||
margin-top: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.depeche {
|
||||
border-top: 1px solid #ddd;
|
||||
padding: 1rem 0;
|
||||
scroll-margin-top: 1rem;
|
||||
animation: fadeInUp 0.45s ease-out both;
|
||||
}
|
||||
|
||||
.depeche:last-child {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.depeche-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.depeche-date {
|
||||
font-size: 0.75rem;
|
||||
font-style: italic;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.depeche-actions {
|
||||
display: flex;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.depeche-action {
|
||||
font-family: 'IM Fell English', serif;
|
||||
font-size: 0.8rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #bbb;
|
||||
cursor: pointer;
|
||||
padding: 0 0.2rem;
|
||||
line-height: 1;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.depeche-action:hover {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.depeche-texte {
|
||||
line-height: 1.6;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.depeche-texte strong { font-weight: bold; }
|
||||
.depeche-texte em { font-style: italic; }
|
||||
.depeche-texte del { text-decoration: line-through; opacity: 0.6; }
|
||||
|
||||
.inline-code {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8em;
|
||||
background: #f4f4f4;
|
||||
padding: 0.1em 0.35em;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 2px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.hashtag {
|
||||
color: hsl(180, 50%, 40%);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted hsl(180, 50%, 60%);
|
||||
}
|
||||
|
||||
.hashtag:hover {
|
||||
color: hsl(180, 60%, 25%);
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.mention {
|
||||
color: hsl(270, 40%, 50%);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.depeche-link {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.35rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
border-bottom: 1px dotted #bbb;
|
||||
max-width: 100%;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.depeche-link:hover {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #111;
|
||||
}
|
||||
|
||||
.link-domain {
|
||||
font-size: 0.65rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
background: #111;
|
||||
color: #fff;
|
||||
padding: 0.05em 0.4em;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.link-url {
|
||||
font-size: 0.85rem;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 300px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.embed-container {
|
||||
margin-top: 0.8rem;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border: 1px solid #ddd;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.embed-container iframe {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.embed-container.video {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.embed-container.video iframe {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.embed-container.music iframe {
|
||||
height: 152px;
|
||||
}
|
||||
|
||||
.depeche-image {
|
||||
margin-top: 0.8rem;
|
||||
max-height: 400px;
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
border: 1px solid #ddd;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chargement {
|
||||
font-style: italic;
|
||||
color: #aaa;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.rss-link {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
color: #aaa;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.rss-link:hover {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
/* === PERSO IMAGES === */
|
||||
.fenetre img.perso {
|
||||
height: 400px;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.fenetre img.perso--offset {
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
/* === RESPONSIVE === */
|
||||
@media (max-width: 600px) {
|
||||
body {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.fenetre {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.fenetre img.perso,
|
||||
.fenetre img.cover,
|
||||
.thanks {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.col-cover {
|
||||
position: static;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.index-header {
|
||||
flex-direction: column;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.now {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* === LIGHTBOX === */
|
||||
.lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lightbox-fond {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
cursor: zoom-out;
|
||||
}
|
||||
|
||||
.lightbox-image {
|
||||
position: relative;
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
object-fit: contain;
|
||||
z-index: 1;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.lightbox-fermer {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1.5rem;
|
||||
z-index: 2;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 2.5rem;
|
||||
cursor: pointer;
|
||||
font-family: 'IM Fell English', serif;
|
||||
line-height: 1;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.lightbox-fermer:hover {
|
||||
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 === */
|
||||
summary {
|
||||
cursor: pointer;
|
||||
list-style: "+ ";
|
||||
}
|
||||
|
||||
details[open] > summary {
|
||||
list-style: "- ";
|
||||
}
|
||||
|
||||
summary:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
/* === PAGE BOUTONS == */
|
||||
.section-boutons {
|
||||
margin-top: 1.2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.section-boutons h2 {
|
||||
font-size: 1rem;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
color: #999;
|
||||
margin-bottom: 0.6rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
.rangee-boutons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
}
|
||||
.rangee-boutons img {
|
||||
image-rendering: pixelated;
|
||||
display: block;
|
||||
}
|
||||
.mon-bouton {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #ddd;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.mon-bouton p {
|
||||
margin-bottom: 0.6rem;
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
}
|
||||
code {
|
||||
font-family: monospace;
|
||||
font-size: 0.75rem;
|
||||
background: #f5f5f5;
|
||||
padding: 0.4rem 0.6rem;
|
||||
display: block;
|
||||
border: 1px solid #ddd;
|
||||
overflow-x: auto;
|
||||
color: #333;
|
||||
}
|
||||
124
assets/swirl.js
|
|
@ -1,124 +0,0 @@
|
|||
(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
|
|
@ -1,539 +0,0 @@
|
|||
(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 = '×';
|
||||
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);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="site personnel d'Ewen" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://eweng.space" />
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>"
|
||||
/>
|
||||
|
||||
<link rel="stylesheet" href="../../assets/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="../../index.html">←</a>
|
||||
<span class="fenetre-nom">eweng.space ☆</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>entrées</h1>
|
||||
<p class="soustitre">
|
||||
chaques entrées contiennent des thèmes spécifiques, bonne
|
||||
lecture !
|
||||
</p>
|
||||
<details>
|
||||
<summary>mai 2026</summary>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="may2026/reutiliser.html">réutiliser</a
|
||||
><span class="soustitre"> 06 mai - 2026</span>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>mars 2026</summary>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="march2026/retransmettre.html"
|
||||
>retransmettre</a
|
||||
><span class="soustitre"> 30 mars - 2026</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="march2026/multipotentiel.html"
|
||||
>multipotentiel</a
|
||||
><span class="soustitre"> 25 mars - 2026</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="march2026/bonjour.html">bonjour</a
|
||||
><span class="soustitre"> 21 mars - 2026</span>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../../assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="site personnel d'Ewen">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://eweng.space">
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
|
||||
<link rel="stylesheet" href="../../../assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="../index.html">←</a>
|
||||
<span class="fenetre-nom">eweng.space ☆</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>bonjour</h1>
|
||||
<p class="soustitre">21 mars - 2026</p>
|
||||
<p>bonjour, bienvenue, c'est la première entrée de mon blog. je suis particulièrement heureux d'avoir réouvert un blog, mon dernier remontais à 2023 (sur le même nom de domaine d'ailleurs !) et en vrai bah ça me manquais.</p>
|
||||
<p>pour la petite histoire, je me suis rendu que je payais un nom de domaine (celui la) que j'utilisais plus quand j'ai vu passer un -15 balles sur mon compte en banque, enfait j'avais zappé que j'avais payé annuellement ce nom de domaine, ducoup bah... foutu pour foutu.. autant en faire quelque chose de cool hein !</p>
|
||||
<p>ducoup je me suis dit que ce site tournerais autour de moi, mais c'est aussi une opportunité de travailler une image.</p>
|
||||
<p>dernièrement je me suis mit au dessin, et je me suis dit que ca serait super de faire un site autour de mes dessins, pouvoir les intégrer dedans etc..</p>
|
||||
<p>bon, bref, j'espère que je fais pas ça pour rien, je dis pas que je pense que la pertinence du site se résume à son nombre de visiteurs ou de gens qui tombent dessus mais.. faut que je me dise que je fais ça pour moi, parceque j'ai envie de partager des choses sympa, de m'améliorer humainement, de juste kiffer quoi.</p>
|
||||
<p>amusez vous bien ! merci de m'avoir lu, et hésitez pas à me contacter sur la page contact (qui n'est pas encore faite ATM -> @azuneer sur twitter) si vous avez envie de papoter ou whatever.</p>
|
||||
<p>bye ! ☆</p>
|
||||
<img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu">
|
||||
</div>
|
||||
</div>
|
||||
<div id="vine-container"></div>
|
||||
<script src="../../../assets/main.js"></script>
|
||||
<script src="../../../assets/vine.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="site personnel d'Ewen">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://eweng.space">
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
|
||||
<link rel="stylesheet" href="../../../assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="../index.html">←</a>
|
||||
<span class="fenetre-nom">eweng.space ☆</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>multipotentiel</h1>
|
||||
<p class="soustitre">25 mars - 2026</p>
|
||||
<p>quand j'ai commencé à apprendre la 3d avec blender (autour de janvier dernier), je me sentais pas mal submergé et enfait j'arrivais à rien faire, je voulais le faire mais mon cerveau entrait dans un état d'anxiété assez violent pour je ne sais quelle raison</p>
|
||||
<p>et enfait cet évènement m'a poussé à réfléchir sur pourquoi je ressentais ça quand je faisais de l'art ou autre discipline créative, et je me suis rendu compte qu'avec du recul, ça m'arrivait avec presque toute nouvelle discipline que je touchais</p>
|
||||
<p>le vrai problème c'est que j'ai envie d'apprendre à faire tellement de choses, j'ai envie de maitrîser beaucoup trops de notions différentes, je veux faire de la musique, du dessin, de la 3D, apprendre des instruments, la peinture, le hardware engineering, les maths etc etc etc..</p>
|
||||
<p>mais à chaque fois que je me lance dans un truc, après quelques jours à apprendre ce truc en question, j'arrive plus, je bloque complètement mentalement</p>
|
||||
<p>et je crois que c'est en partie dû à mon système de récompense interne qui est complètement fucked up, mais aussi parceque je recherche toujours une certaine reconnaissance, que ce sois quand je poste ce que je fais sur twitter ou quand j'envoie ce que je fais a gab, à chaque fois, systématiquement, tout passe par cette forme de validation ingrate et nuisible</p>
|
||||
<h2>pourquoi "multipotentiel" ?</h2>
|
||||
<p>j'ai profondément réfléchi à ce sujet, et enfait, j'en suis arrivé à deux conclusions : </p>
|
||||
<ol>
|
||||
<li>cette situation est profondément triste</li>
|
||||
<li>je suis peut être "multipotentiel"</li>
|
||||
</ol>
|
||||
<p>déjà, oui je trouve ça triste, parceque j'ai envie de faire des trucs de mes mains, et en être fier, et pas à devoir constamment le partager à tord et à travers. il faut que je trouve un autre remède à cette envie, le fait de partager son art n'est pas une mauvaise chose en tant que telle, loin de là même.</p>
|
||||
<p>deuxièmement, je pense être multipotentiel, j'ai le potentiel de devenir le meilleur de ce que je souhaite être à une discipline, mais en même temps j'ai envie de l'être dans toutes ces disciplines en meme temps ! et ça marche pas ! dumbass</p>
|
||||
<p>ça reste logique, ça fais sens, ça fais mal, mais c'est pas grave, le long process d'acceptation est une looongue route mais j'y verrais le bout, j'en suis sur :)</p>
|
||||
<img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu">
|
||||
</div>
|
||||
</div>
|
||||
<div id="vine-container"></div>
|
||||
<script src="../../../assets/main.js"></script>
|
||||
<script src="../../../assets/vine.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="site personnel d'Ewen">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://eweng.space">
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
|
||||
<link rel="stylesheet" href="../../../assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="../index.html">←</a>
|
||||
<span class="fenetre-nom">eweng.space ☆</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>retransmettre</h1>
|
||||
<p class="soustitre">30 mars - 2026</p>
|
||||
<p>quand j'écris ce texte, je suis en cours, ouai, en cours. j'écoute <i>Drinking Age</i> de Cameron Winter, et j'avais envie de parler d'un exercice qui me frustre, mais qui est indispensable.</p>
|
||||
<p>j'y pense aussi mais, je devrais faire une sorte de section "dépêche" sur le site, comme ca je pourrais juste envoyer depuis mon tel une ou deux phrases et ca se formatte tout beau tout propre quelque part sur le site.</p>
|
||||
<p>mais c'est pas le putain de sujet ?</p>
|
||||
<p>je disais donc, l'exercice en question, c'est celui de retransmettre ce que je ressens en mots et en phrases.</p>
|
||||
<p>vu que dernièrement, j'ai réussi à faire la section review du site et à finaliser ma première review (AZ) dont je suis très fier, bah enfait je me suis rendu compte en l'écrivant que c'est tellement compliqué de sortir ce qui nous fais vibrer en mots.</p>
|
||||
<p>c'est malgré tout un exercice génial car il nous permets après dans la vie de tout les jours d'arriver à <i>pinpoint</i> plus facilement ce qui ne vas pas ou ce qui vas.</p>
|
||||
<p>to be written</p>
|
||||
<img class="thanks" src="/assets/draws/thxforreading.png" alt="merci d'avoir lu">
|
||||
</div>
|
||||
</div>
|
||||
<div id="vine-container"></div>
|
||||
<script src="../../../assets/main.js"></script>
|
||||
<script src="../../../assets/vine.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="site personnel d'Ewen">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://eweng.space">
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
|
||||
<link rel="stylesheet" href="../../../assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="../index.html">←</a>
|
||||
<span class="fenetre-nom">eweng.space ☆</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>réutiliser</h1>
|
||||
<p class="soustitre">06 mai - 2026</p>
|
||||
<p>comme vous aurez pu le voir sur le site, je me suis dernièrement mit à faire du "retro computing" ou comme je dirais, en anglais qui n'existe pas, du "repurposing".</p>
|
||||
<h2>début</h2>
|
||||
<p>ça à commencé quand je cherchais sur leboncoin, des vieux Macs, j'avais pas d'idée précises de ce que je voulais enfait, et je me rends compte en écrivant ces lignes que c'était pas la première fois que je faisais ça enfait.. </p>
|
||||
<p>je crois que tout à commencé réellement quand je me suis acheté mon Thinkpad X200 (image en dessous), au lycée en première. je l'avais prit parceque j'étais complètement matrixé par R. Stallman à l'époque et le "free software", je voulais donc un Thinkpad qui soit "<a href="https://libreboot.org">librebooté</a>", c'est à dire un Thinkpad qui ne venait pas avec le bios propriétaire du PC. (je vais pas rentrer dans les détails mais en gros le librebooting permets d'enlever <a href="https://en.wikipedia.org/wiki/Intel_Management_Engine#Assertions_that_ME_is_a_backdoor"> Intel Management Engine </a> qui est réputé, possiblement à tort, pour avoir de grosses backdoor au niveau matériel)</p>
|
||||
<img class="depeche-image" src="../../../assets/pictures/portfolio-serveur.webp" alt="Thinkpad X200 sur une étagère">
|
||||
<p class="soustitre">Thinkpad X200 librebooté</p>
|
||||
<p>j'étais super content de ce PC, il est vraiment bien fichu et mon esprit gangréné par le "libre" était vraiment satisfait cette pièce à cette époque, j'avais mon petit rice d'Arch sous DWM (Luke Smith m'avait vraiment baisé le cerveau et je m'en suis rendu compte y'a genre 2 ans de ça).</p>
|
||||
<img class="depeche-image" src="../../../assets/pictures/dwm.png" alt="DWM">
|
||||
<p class="soustitre">DWM, le WM des bons gros barbus !</p>
|
||||
<p>il m'a accompagné pendant toutes mes années lycées, jusqu'au moment où je suis rentré à l'IUT de Châtellerault et où j'ai du passer sur du hardware un petit peu plus conséquent parceque le Core 2 Duo était pas suffisant pour ce que j'avais besoin de faire à cette période de ma vie. Et aujourd'hui, je me sers du Thinkpad en tant que homelab, où j'y fou pleins de services à la pelle et il tiens vraiment bien la charge, je suis vraiment fier de ce que j'en ai fait.</p>
|
||||
<p>m'enfin bref, tout ça pour dire que ma quête de repurposing à commencée ici, c'est à partir de ce moment que inconsciemment, je me suis dit que les vieux appareils éléctroniques (principalement les ordinateurs) ont le mérite de leur donner une nouvelle vie, un nouveau sens, une nouvelle opportunité. </p>
|
||||
<h2>la quête des Macs</h2>
|
||||
<p>sur la section <a href="../../../depeches.html">dêpèches</a> de mon site, j'ai récemment raconté en plusieurs posts mes aventures avec un <a href="https://en.wikipedia.org/wiki/Power_Mac_G5"> PowerMac G5</a> que j'ai trouvé sur leboncoin. il venait d'un mec qui l'avait stocké dans son garage pendant 10 ans, parceque il appartenait à sa mère et qu'il ne marchais plus, je vous laisse remonter le fil de ma page de dépêches si vous voulez vous faire une idée du processus qu'a été de tenter de le remettre à la vie, sans succès pour l'instant parceque mon fournisseur de pièces neuves me GHOST littéralement, mais c'est pas le sujet..</p>
|
||||
<img class="depeche-image" src="../../../assets/pictures/G5.jpg" alt="PowerMac G5 vue de côté avec panneau ouvert">
|
||||
<p class="soustitre">Le PowerMac G5</p>
|
||||
<p>bon, comme je disais, j'ai pas réussi à le faire fonctionner à cause d'un gros soucis de carte mère, ducoup j'ai rabattu toute ma frustration sur un autre vieux mac, cette fois-ci, un magnifique MacBook Pro Pre-Unibody Early 2008, une merveilleuse pièce d'ingénieurie à mon sens. je le trouve mangifique, j'ai le modèle 15 pouces, à la base j'essayais de trouver le modèle 17 pouces mais il est assez rare et j'en ai pas trouvé.. surtout au prix où j'ai eu mon mac ! 10 balles. le mec à qui je l'ai acheté ne savais pas si il marchais mais wow il était en super bon état, du moins sur les photos, il avait l'air presque neuf ! et pour 10 euros je pouvais pas laisser passer ma chance, clairement.</p>
|
||||
<p>ducoup, je le reçois, et la c'est une belle claque déjà parceque il à l'air comme neuf, y'a juste la batterie qui à gonflée, mais sinon c'est hallucinant à quel point il est en bon état visuel. j'avais pas le chargeur pour l'essayer, donc je ne pouvais que spéculer sur le fait qu'il marchais ou pas. j'avais peur parceque, sur ces modèles spécifiques, ils ont eu de gros soucis de billes de soudure au niveau de la carte graphique, et une pèlletée de ces modèles de MBP sont allés à la benne parceque le GPU marchait juste plus et que c'est une purge à remplacer (le reballing, le fait de refaire les billes de soudures sur la carte mère, c'est vraiment un process compliqué, et j'avais VRAIMENT pas envie de m'embêter avec ça).</p>
|
||||
<img class="depeche-image" src="../../../assets/pictures/mbpearly2008bigsur.webp" alt="Un Macbook Pro Pre-Unibody Early 2008 qui tourne sous Big Sur">
|
||||
<p class="soustitre">Un Macbook Pro Pre-Unibody Early 2008 qui tourne sous Big Sur</p>
|
||||
<p>quelques jours plus tard je reçois le chargeur, puis là, je suis abassourdi, il marche !! parfaitement bien !! aucun soucis de GPU, le clavier est niquel, l'écran aussi, le rétro-éclairage du clavier également... fin bref j'ai vraiment eu énormément de chance pour le coup. enfait je me suis renseigné auprès du gars et le MBP était à un Darty, ils l'utilisaient plus parceque il devait se faire trop vieux je pense..</p>
|
||||
<img class="depeche-image" src="../../../assets/pictures/mbpearly2008.jpeg" alt="MacBook Pro Early 2008 dans la nuit">
|
||||
<p class="soustitre">regardez moi ce beau gosse ! </p>
|
||||
<p>depuis, j'ai installé un SSD dans la bête, puis remit Snow Leopard dessus, pour le kiff, pour revoir le skeumorphisme de cet OS, et c'est vraiment une super claque, tout avait tellement de vie à cette époque ! ça me rappelle beaucoup de souvenirs et vraiment j'adore juste l'utiliser pour écrire sur mon blog, écouter de la musique, jouer à des vieux jeux ou même faire de la musique dessus ! il tiens vraiment bien cette petite charge que je lui impose, je vais pas commencer à le faire souffrir non plus.. il a que 2Go de RAM en DDR2 !</p>
|
||||
<p>mais ouai sinon pour en revenir au G5.. bah j'attends désespéremment que le mec me réponde mais bon.. il reste couché sous mon bureau à côté de mon PC de bureau principal quoi..</p>
|
||||
<h2>qu'en tirer comme conclusion ?</h2>
|
||||
<p>bah c'est simple, je pense pas qu'il faille jeter tout et n'importe pas à la benne sous prétexte que "ça ne marche plus" ou que "c'est trop vieux", il faut revendre ou donner ! je sais qu'au Etats-Unis y'a des magasins qui récupère toute sorte de matériel éléctronique un peu vieux ou légèrement déffectueux, qu'ils réparent et qu'il revendent à des prix bas, bah ça je trouve que c'est vraiment une initiative géniale et qui devrait être plus présente en France, ce qui n'est pas le cas pour le moment car les entreprises préfèrent jeter en grande majorité, et ça je trouve ça naze, vraiment.</p>
|
||||
<img class="thanks" src="../../../assets/draws/thxforreading.png" alt="merci d'avoir lu">
|
||||
</div>
|
||||
</div>
|
||||
<div id="vine-container"></div>
|
||||
<script src="../../../assets/main.js"></script>
|
||||
<script src="../../../assets/vine.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
61
boutons.html
|
|
@ -1,61 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>boutons — eweng.space</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English:ital@0;1&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<a href="index.html" class="retour">←</a><span class="fenetre-nom" id="titre-nav">eweng.space ☆</span>
|
||||
</div>
|
||||
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>boutons</h1>
|
||||
<p>des amis et des trucs que j'aime bien.</p>
|
||||
|
||||
<!-- ── amis ── -->
|
||||
<div class="section-boutons">
|
||||
<h2>amis</h2>
|
||||
<div class="rangee-boutons">
|
||||
<a href="https://gabriel.reisen" target="_blank">
|
||||
<img src="https://gabriel.reisen/button2.gif" width="88" height="31" alt="gabriel.reisen">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── sites que j'aime ── -->
|
||||
<div class="section-boutons">
|
||||
<h2>sites et boutons que j'aime</h2>
|
||||
<div class="rangee-boutons">
|
||||
<img src="assets/buttons/button1.gif" width="88" height="31" alt="">
|
||||
<a href="https://neocities.org" target="_blank">
|
||||
<img src="assets/buttons/button2.gif" width="88" height="31" alt="">
|
||||
</a>
|
||||
<a href="https://debian.org" target="_blank">
|
||||
<img src="assets/buttons/button4.gif" width="88" height="31" alt="">
|
||||
</a>
|
||||
<img src="assets/buttons/button5.gif" width="88" height="31" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── mon bouton ── -->
|
||||
<div class="mon-bouton">
|
||||
<p>si vous voulez ajouter mon bouton sur mon site / if you want to add my button on your website : </p>
|
||||
<img src="assets/buttons/button3.gif" width="88" height="31" alt="eweng.space" style="image-rendering:pixelated; margin-bottom:0.6rem;">
|
||||
<code><a href="https://eweng.space"><img src="https://eweng.space/assets/buttons/button3.gif" width="88" height="31" alt="eweng.space"></a></code>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>dépêches — eweng.space</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English:ital@0;1&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<a href="index.html" class="retour">←</a><span class="fenetre-nom" id="titre-nav">eweng.space ☆</span>
|
||||
</div>
|
||||
|
||||
<div class="fenetre">
|
||||
<a href="/rss/depeches.xml" class="rss-link">rss</a>
|
||||
<div class="fenetre-contenu">
|
||||
<h1>dépêches</h1>
|
||||
|
||||
<div class="feed" id="feed">
|
||||
<p class="chargement">chargement…</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
48
index.html
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="site personnel d'Ewen">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://eweng.space">
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="index-header">
|
||||
<span class="fenetre-nom" id="titre-nav">eweng.space ☆</span>
|
||||
<span class="now">/now : faut que je fasses les courses.</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--small">
|
||||
<div class="fenetre-contenu">
|
||||
<p>bienvenue sur mon blog personnel, j'y artage mes états d'âme, mes pensées, mes musiques, mon art préféré, mes sentiments... bonne visite!</p>
|
||||
<ul>
|
||||
<li><a href="blog/entries/index.html">blog</a></li>
|
||||
<li><a href="reviews/index.html">reviews</a></li>
|
||||
<li><a href="depeches.html">dépêches</a></li>
|
||||
<li><a href="qui.html">qui</a></li>
|
||||
</ul>
|
||||
<p>ce site est entièrement fait à la main, par moi, par ce que je suis et ce que je représente dans le monde, avec mes envies, mes convictions, mes peines et ma sueur.</p>
|
||||
<p><strong><u>!! en construction !!</u></strong></p>
|
||||
<p><a href="boutons.html">boutons</a> de mes amis ou des trucs que j'aime bien dans la vie : </p>
|
||||
<a href="https://gabriel.reisen"><img src="https://gabriel.reisen/button2.gif" width="88" height="31" alt="gab"></a> <img src="assets/buttons/button1.gif" width="88" height="31" alt=""> <img src="assets/buttons/button2.gif" width="88" height="31" alt=""> <img src="assets/buttons/button3.gif" width="88" height="31" alt=""> <img src="assets/buttons/button4.gif" width="88" height="31" alt=""> <a href="https://www.mabsland.com/Adoption.html"><img src="assets/buttons/panda.gif" width="88" height="31" alt=""></a>
|
||||
<img style="position:absolute; bottom:1.5rem; right:4rem;" src="https://count.getloli.com/@ewengspace?name=ewengspace&theme=rule34&padding=3&offset=0&align=top&scale=1&pixelated=1&darkmode=0" alt="ewengspace" />
|
||||
</div>
|
||||
<img src="assets/draws/ewen.png" class="perso" alt="">
|
||||
</div>
|
||||
<div id="swirl-container"></div>
|
||||
<script src="assets/main.js"></script>
|
||||
<script src="assets/swirl.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
61
qui.html
|
|
@ -1,61 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="site personnel d'Ewen">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="eweng.space">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://eweng.space">
|
||||
|
||||
<title>eweng.space</title>
|
||||
|
||||
<!-- Font depuis Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="../../index.html">←</a>
|
||||
<span class="fenetre-nom">eweng.space ☆</span>
|
||||
</div>
|
||||
<div class="fenetre fenetre--large">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>qui suis-je</h1>
|
||||
<p>salut! si vous êtes sur ce site, c'est soit que vous me connaissez et que par la force des choses vous êtes tombés sur ce site à cause de moi et de mon larping, soit que vous êtes un complet inconnu et que vous ne me connaissez pas et inversement.</p>
|
||||
<p>dans tout les cas, je vous souhaite la bienvenue, et je voulais me présenter un peu, parceque on est sur un blog et que c'est important de définir un contexte!</p>
|
||||
<p>je m'appelle Ewen, j'ai actuellement 20 ans (bientôt 21, le 12 mai) et j'essaie de vivre ma vie et de pas trop penser aux choses négatives qui s'y passe, ce blog fais partie de cette démarche active.</p>
|
||||
<p>au moment où j'écris ça, je suis en bts sio (sisr) et je kiffe assez bien ce que je fais, je compte très sûrement devenir adminsys dans le futur!</p>
|
||||
<h2>ce que j'aime dans la vie : </h2>
|
||||
<ul>
|
||||
<li>la musique</li>
|
||||
<li>le dessin</li>
|
||||
<li>les visual novels</li>
|
||||
<li>les jeux vidéos (ils font de moins en moins partie de ma vie mais ils sont là dans mon coeur)</li>
|
||||
<li>undertale/deltarune</li>
|
||||
<li>l'informatique (au sens large du terme, oui c'est flou)</li>
|
||||
<li>le logiciel libre</li>
|
||||
<li>le café</li>
|
||||
<li>Lou</li>
|
||||
<li>mes chats</li>
|
||||
<li>mon appartement</li>
|
||||
<li>mes vinyles</li>
|
||||
</ul>
|
||||
<p>j'aime aussi beaucoup faire de la musique, je devrais déposer mes liens quelque part...</p>
|
||||
<p>enfin bref, voilà c'est à peu près tout, oui c'est un peu vague mais ça suffiras je pense, merci d'avoir lu et bonne visite du site !</p>
|
||||
</div>
|
||||
<figure style="margin: 0; text-align: center;">
|
||||
<img src="assets/draws/rpg.png" class="perso perso--offset" alt="">
|
||||
<figcaption style="font-size: 0.75em; color: #555; margin-top: 0.3rem; font-style: italic;">
|
||||
moi en perso rpg que j'ai dessiné, basé sur <br>l'UI de <a href="https://iro.ragnarokonline.com/">Ragnarok Online</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<script src="assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>eweng.space</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English:ital@0;1&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../../assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<a href="../index.html" class="retour">←</a><span class="fenetre-nom" id="titre-nav">eweng.space ☆</span>
|
||||
</div>
|
||||
|
||||
<div class="fenetre fenetre--medium">
|
||||
<div class="fenetre-contenu">
|
||||
|
||||
<h1>AZ</h1>
|
||||
<p style="color:#555; margin-bottom:0.8rem;">23wa · 2025</p>
|
||||
|
||||
<div class="meta-ligne">
|
||||
<span class="note">9/10</span>
|
||||
<span>★★★★★★★★★☆</span>
|
||||
<span class="genre-tag">Epic Collage</span>
|
||||
<span class="genre-tag">Art Pop</span>
|
||||
</div>
|
||||
|
||||
<div class="review">
|
||||
<h1>lerooooooooyyy jenkins</h1>
|
||||
<p>quand je m'étais prit RORSCHACH dans la face en 2024, l'album précédent AZ de 23wa, j'avais ressenti une telle envie de fouiner sa discographie que j'ai tout écouté, que ce sois sur SoundCloud sous son pseudonyme "djmaleblanc" ou ses remix ambient sous "23062003".</p>
|
||||
<p>y'a un truc dans sa manière de produire que j'arrive pas a cerner, l'utilisation majeure de samples peu communs de musiques japonaises, d'extraits, de voix, de textures variées et de resampling est évidente, mais y'a une telle puissance d'âme qui s'en dégage, quelque chose de tellement personnel et indescriptible propre à sa musique qui me foudroie en plein coeur à chaque nouvelle sortie ou découverte.</p>
|
||||
<p>alors c'était évident que j'attendais impatiemment la sortie d'<strong>AZ</strong> quand le rollout à commencé fin juin 2025 il me semble, avec une <a href="https://x.com/amur23w/status/1939292773511012456?s=20">vidéo</a> au style assez écclectique qui était enfait un extrait du premier morceau de l'album <i>Atto, Alto</i>.</p>
|
||||
<p>l'album est sorti un petit moins d'un mois après il me semble, mais avant de l'écouter je voulais attendre de partir en vacances sur l'île de Ré avec ma copine pour l'écouter ensemble sur une plage tranquille où y'avait peu de monde voire personne. juste moi, celle que j'aime de tout mon coeur, et un album qui, je ne savais trop peu à cette époque, aller me marquer énormément.</p>
|
||||
<p>tranquillement installé sur une serviette de plage, sous un magnifique soleil, on se mets à écouter l'album avec Lou, et puis wow.. c'est la claque. alors pourquoi c'est la claque ?</p>
|
||||
<p>c'est la claque parceque j'y retrouve tout ce que j'aime chez cet artiste, de l'expérimentation, mais beaucoup d'âme et de sincérité. les 4 premières musiques de l'album sont très belles, puissantes. elles parlent d'acceptation de soi, de la routine, de son corps, au travers d'un hymne omniprésent scandé par 23wa et ce qu'on pourrait penser être plusieurs personnes à la fois à la fin du morceau <i>Atto, Alto</i> : </p>
|
||||
<p><strong>AZ, AZ, AZ, AZ !!!!</strong></p>
|
||||
<p>comme à l'unisson, sans savoir ce que ça veut réellement dire. j'ai un petit peu spéculé d'ailleurs sur ce que ça pourrait potentiellement dire mais c'est en grande majorité des théories un peu débile (cf. il aurait tout fait de A à Z dans cet album, s/o gab).</p>
|
||||
<p>c'est lors du son <i>World (Absolute)</i> que je me suis vraiment vu partir un peu loin, c'est l'un de mes morceaux préféré de cet album, si ce n'est mon préféré. la guitare, le doux flottement constant, la voix d'Ethan, les sifflements, la fin du son qui introduit une basse/pad douce mais puissante sur la longueur, les divers bruits stridents d'une radio ou d'un poste qui marche mal qui se supperposent en même temps sur tout le reste, wow.. j'ai eu beaucoup de frissons sur celle là et j'y reviens vraiment souvent. </p>
|
||||
<p>le vrai point fort de cet album c'est qu'il est traversé par des moments de calme, de douceur, mais aussi de moments de révolte comme sur <i>MOMOMO (feat. Botsu aka N.G.S)</i>, reprennant le chant "<i>la jeunesse emmerde le front nationnal</i>" de la chanson mythique des <i>Berurier Noirs</i>, <i>Porcherie</i>. ou encore d'autres moment plus dansants comme sur <i>giga (feat. SEBii)</i>.</p>
|
||||
<p>ce sentiment que je décrivais comme indescriptible est perceptible sur les 3 dernières musiques de l'album, où Ethan se livre sur ses expériences de vie, ses émotions les plus sincères.</p>
|
||||
<p>l'album se conclut sur <i>toi</i>, une chanson qui reviens sur ces dites expériences, sûrement amoureuses, passées, où il parle d'un <strong>toi</strong> inconnu, qu'il a aimé, sous toutes ses formes. laissant derrière lui tout ses efforts, ses convictions au travers de cette présumée relation.</p>
|
||||
<p>cette musique se termine sur une voix mythique d'un personnage d'internet, le fameux <i>leroy jenkins</i> puissant qui symboliserait le laisser aller pur, le rentre dans le tas, le lâcher prise total, sur quoi ? sur la vie ? sur ses relations ? sur sa perception du monde qui nous entoure ? je ne sais pas, mais le message est là, et il est puissant.</p>
|
||||
<p>et puis je me revois la, sur la plage, allongé sur le sable chaud, entrain de finir l'album sous un temps gris, avec des nuages, et je me dis à moi même que j'ai écouté à quelque chose de sacrément puissant et marquant, dont je vais me souvenir toute ma vie comme l'une des oeuvres les plus sincères auquelles j'ai pu écouter de toute ma vie.</p>
|
||||
<p>alors merci pour tout 23wa pour ce bout de toi, j'ai hâte de te voir en concert un jour, ce serait certainement l'un des plus beaux jours de ma vie.</p>
|
||||
<br>
|
||||
<img src="/assets/draws/thxforreading.png" class="thanks">
|
||||
<p style="color:#555; margin-bottom:0.8rem; font-size: small;">merci d'avoir lu cette review, c'est la toute première que je fais, alors oui c'est maladroit et certainement pas objectif, mais j'ai envie de dire que ce n'est pas le but de ce blog et de cette review. en espérant que ça vous ait plu !</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="nav-albums">
|
||||
<a href="u.html">U →</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-cover">
|
||||
<figure>
|
||||
<img src="/assets/covers/az.jpg" alt="AZ - 23wa" class="cover"
|
||||
onerror="this.style.display='none'">
|
||||
<figcaption class="cover-legende">AZ, 2025</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../../assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
[
|
||||
{
|
||||
"id": "1",
|
||||
"titre": "AZ",
|
||||
"artiste": "23wa",
|
||||
"annee": 2025,
|
||||
"cover": "../../assets/covers/az.jpg",
|
||||
"note": 9,
|
||||
"genre": ["Epic Collage", "Art Pop"],
|
||||
"favoris": true
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"titre": "U",
|
||||
"artiste": "Underscores",
|
||||
"annee": 2026,
|
||||
"cover": "../../assets/covers/u.jpg",
|
||||
"note": 9,
|
||||
"genre": ["Electropop", "Glitch Pop"],
|
||||
"favoris": true
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"titre": "Untrue",
|
||||
"artiste": "Burial",
|
||||
"annee": 2007,
|
||||
"cover": "../../assets/covers/untrue.jpg",
|
||||
"note": 10,
|
||||
"genre": ["UK Garage", "Dubstep", "Ambient"],
|
||||
"favoris": false
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"titre": "C'est triste (P1)",
|
||||
"artiste": "Yuri Online",
|
||||
"annee": 2021,
|
||||
"cover": "../../assets/covers/cesttristep1.jpg",
|
||||
"note": 8,
|
||||
"genre": ["Cloud Rap", "Pluggnb"],
|
||||
"favoris": true
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"titre": "♡",
|
||||
"artiste": "Jane Remover",
|
||||
"annee": 2025,
|
||||
"cover": "../../assets/covers/heartep.jpg",
|
||||
"note": 10,
|
||||
"genre": ["Alternative R&R", "Glitch Pop"],
|
||||
"favoris": true
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
"titre": "Barefoot",
|
||||
"artiste": "Gen",
|
||||
"annee": 2025,
|
||||
"cover": "../../assets/covers/barefoot.jpg",
|
||||
"note": 7.5,
|
||||
"genre": ["French Hip Hop", "Cloud Rap"],
|
||||
"favoris": true
|
||||
}
|
||||
]
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>reviews — eweng.space</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☆</text></svg>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IM+Fell+English:ital@0;1&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../assets/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<a href="../index.html" class="retour">←</a><span class="fenetre-nom" id="titre-nav">eweng.space ☆</span>
|
||||
</div>
|
||||
|
||||
<div class="fenetre">
|
||||
<div class="fenetre-contenu">
|
||||
<h1>reviews</h1>
|
||||
<p>ce que j'écoute, ce que j'en pense.</p>
|
||||
|
||||
<div class="grille" id="grille"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||