Ce snippet prend un texte HTML encodé (ex. <strong>…</strong>), le décode puis affiche une explosion de confettis autour du texte. Plug-and-play, sans librairie externe, relançable au clic.
Utilisez l’attribut data-encoded (ou un <pre> enfant) pour fournir le contenu encodé.
<!-- ============== Apple-Style Confetti Text (Auto, 5s, Decode HTML) ==============
USAGE :
- Placez votre HTML ENCODÉ dans data-encoded (ou à défaut dans un <pre> enfant).
- L’animation démarre seule au chargement, dure ~5 s, puis s’arrête (une seule fois).
=============================================================================== -->
<div class="sw-apple-hero" data-encoded="<strong>Bravo !</strong>"></div>
<style>
.sw-apple-hero{
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 32vh; /* zone aérée, fond blanc */
padding: clamp(16px, 4vw, 40px);
background: #fff;
border-radius: 20px;
overflow: visible;
isolation: isolate; /* confettis au-dessus sans affecter le reste */
}
.sw-apple-hero .sw-text{
font-family: -apple-system, system-ui, "SF Pro Display", Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
font-weight: 700;
font-size: clamp(28px, 7vw, 72px); /* grand, responsive */
line-height: 1.05;
letter-spacing: .2px;
color: #111; /* contraste sobre */
text-align: center;
white-space: pre-wrap;
}
/* Confetti */
.sw-piece{
position: absolute;
width: var(--w,6px);
height: var(--h,10px);
background: var(--c,#777);
left: var(--x,0px);
top: var(--y,0px);
transform: translate(0,0) rotate(0deg);
opacity: 1;
border-radius: var(--r,3px);
pointer-events: none;
will-change: transform, opacity, filter;
animation:
sw-fall var(--dur,1400ms) cubic-bezier(.2,.7,.2,1) forwards,
sw-twirl var(--dur,1400ms) linear forwards;
}
@keyframes sw-fall{
to{
transform: translate(var(--dx,0px), var(--dy,120px)) rotate(var(--rot,180deg));
opacity: 0;
}
}
@keyframes sw-twirl{
to{ filter: hue-rotate(120deg) saturate(1.05); }
}
/* Respecte l’accessibilité : pas d’animation si l’utilisateur la réduit */
@media (prefers-reduced-motion: reduce){
.sw-piece{ animation: none !important; opacity: 0 !important; }
}
</style>
<script>
(function(){
// Décode <strong>Bravo</strong> -> <strong>Bravo</strong>
function decodeHTML(encoded){
const ta = document.createElement('textarea');
ta.innerHTML = encoded;
return ta.value;
}
// Rendu du texte décodé dans .sw-apple-hero
function renderDecoded(el){
let encoded = el.getAttribute('data-encoded') || '';
if(!encoded){
const pre = el.querySelector('pre');
encoded = pre ? pre.innerHTML.trim() : (el.textContent || '').trim();
}
el.innerHTML = `<span class="sw-text">${decodeHTML(encoded)}</span>`;
}
// Émet un petit lot de confettis autour du centre du texte
function emitConfetti(host, qty){
const text = host.querySelector('.sw-text');
if(!text) return;
const hRect = host.getBoundingClientRect();
const tRect = text.getBoundingClientRect();
const cx = (tRect.left - hRect.left) + tRect.width/2;
const cy = (tRect.top - hRect.top) + tRect.height*0.45;
const palette = [
'#111111', '#2E2E2E', '#5E5E5E', // neutres élégants
'#007AFF', '#34C759', '#FF9500', // accents iOS
'#FF2D55', '#AF52DE' // touches vives
];
for(let i=0;i<qty;i++){
const p = document.createElement('i');
p.className = 'sw-piece';
// tailles / formes
const w = 4 + Math.random()*7; // 4–11px
const h = 7 + Math.random()*11; // 7–18px
const rounded = Math.random() < 0.35 ? (Math.min(w,h)/2)+'px' : '3px';
// angle + portée douce
const angle = Math.random() * Math.PI * 2;
const power = 40 + Math.random()*110; // 40–150px
const dx = Math.cos(angle) * power;
const dy = Math.sin(angle) * power + (30 + Math.random()*70);
// rotation / durée
const rot = (Math.random()*540 - 270).toFixed(1) + 'deg';
const dur = (1000 + Math.random()*900) | 0; // 1.0–1.9s
p.style.setProperty('--x', cx + 'px');
p.style.setProperty('--y', cy + 'px');
p.style.setProperty('--w', w + 'px');
p.style.setProperty('--h', h + 'px');
p.style.setProperty('--r', rounded);
p.style.setProperty('--dx', dx + 'px');
p.style.setProperty('--dy', dy + 'px');
p.style.setProperty('--rot', rot);
p.style.setProperty('--dur', dur + 'ms');
p.style.setProperty('--c', palette[(Math.random()*palette.length)|0]);
host.appendChild(p);
p.addEventListener('animationend', ()=> p.remove(), {once:true});
}
}
// Lecture automatique : ~5 s puis stop (une seule fois)
function autoplayOnce(host){
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
if (host.dataset.played === '1') return;
host.dataset.played = '1';
const start = performance.now();
const duration = 5000; // 5 s
const tickEvery = 120; // ms entre lots
let lastTick = start - tickEvery;
function frame(now){
if (now - lastTick >= tickEvery){
lastTick = now;
// quantité proportionnelle à la largeur du texte (mais bornée)
const text = host.querySelector('.sw-text');
const w = text ? text.getBoundingClientRect().width : 320;
const qty = Math.min(18, Math.max(8, Math.round(w / 80)));
emitConfetti(host, qty);
}
if (now - start < duration){
requestAnimationFrame(frame);
}
}
requestAnimationFrame(frame);
}
function init(){
document.querySelectorAll('.sw-apple-hero').forEach(host=>{
renderDecoded(host);
autoplayOnce(host);
});
}
if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init);
}else{
init();
}
})();
</script>
</script>