Section épurée sur fond blanc avec gros texte (fourni en HTML encodé, automatiquement décodé) et une grande roue en SVG qui tourne une seule fois pendant ~5 secondes au chargement. Design minimal, responsive, et respect de prefers-reduced-motion. Plug-and-play, aucune dépendance.
<!-- ========== Apple-Style Ferris Wheel (Auto 5s, Decode HTML) ==========
USAGE :
- Mettez votre texte HTML ENCODÉ dans data-encoded (ou dans un <pre> enfant).
- La grande roue tourne automatiquement une seule fois (~5 s) au chargement.
======================================================================= -->
<div class="sw-apple-hero-wheel" data-encoded="<strong>Bienvenue !</strong>"></div>
<style>
.sw-apple-hero-wheel{
position: relative;
background:#fff;
border-radius: 20px;
padding: clamp(16px,4vw,40px);
min-height: 36vh;
overflow: visible;
}
.sw-hero-wrap{
display: grid;
align-items: center;
gap: clamp(16px,4vw,40px);
grid-template-columns: 1fr minmax(180px, clamp(200px, 24vw, 360px));
}
@media (max-width: 720px){
.sw-hero-wrap{ grid-template-columns: 1fr; }
}
.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);
line-height: 1.05;
letter-spacing: .2px;
color:#111;
text-align:left;
white-space: pre-wrap;
}
@media (max-width: 720px){
.sw-text{ text-align:center; }
}
/* --- Wheel look --- */
.sw-wheel{
width: 100%;
height: auto;
display: block;
}
.sw-wheel *{
vector-effect: non-scaling-stroke;
}
.wheel-rim{ stroke:#111; stroke-width:2.5; fill:none; opacity:.9; }
.wheel-spoke{ stroke:#111; stroke-width:2; stroke-linecap:round; opacity:.7; }
.wheel-hub{ fill:#111; }
.wheel-cabin{ fill:#111; opacity:.8; rx:3; ry:3; }
/* --- Animation (runs once ~5s) --- */
.wheel-rotor{
transform-box: fill-box;
transform-origin: 50% 50%;
animation: none;
}
.wheel-rotor.play{
animation: sw-rotate 5s cubic-bezier(.2,.7,.2,1) 1 both;
}
@keyframes sw-rotate{
from{ transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Accessibilité : réduire l’animation si demandé */
@media (prefers-reduced-motion: reduce){
.wheel-rotor{ animation: none !important; }
}
</style>
<script>
(function(){
// Decode "<strong>Bonjour</strong>" -> "<strong>Bonjour</strong>"
function decodeHTML(encoded){
const ta = document.createElement('textarea');
ta.innerHTML = encoded;
return ta.value;
}
function renderHero(host){
// 1) Décoder le texte
let encoded = host.getAttribute('data-encoded') || '';
if(!encoded){
const pre = host.querySelector('pre');
encoded = pre ? pre.innerHTML.trim() : (host.textContent || '').trim();
}
host.innerHTML = `
<div class="sw-hero-wrap">
<div class="sw-col">
<span class="sw-text">${decodeHTML(encoded)}</span>
</div>
<div class="sw-col">
<svg class="sw-wheel" viewBox="-120 -120 240 240" aria-hidden="true"></svg>
</div>
</div>
`;
// 2) Construire la grande roue en SVG
const svg = host.querySelector('.sw-wheel');
const rotor = document.createElementNS('http://www.w3.org/2000/svg','g');
rotor.setAttribute('class','wheel-rotor');
svg.appendChild(rotor);
// Rim
const rim = document.createElementNS(svg.namespaceURI,'circle');
rim.setAttribute('class','wheel-rim');
rim.setAttribute('cx','0'); rim.setAttribute('cy','0'); rim.setAttribute('r','100');
rotor.appendChild(rim);
// Spokes (12)
for(let i=0; i<12; i++){
const a = (i * Math.PI/6); // 30°
const x = Math.cos(a) * 92;
const y = Math.sin(a) * 92;
const spoke = document.createElementNS(svg.namespaceURI,'line');
spoke.setAttribute('class','wheel-spoke');
spoke.setAttribute('x1','0'); spoke.setAttribute('y1','0');
spoke.setAttribute('x2', x.toFixed(2)); spoke.setAttribute('y2', y.toFixed(2));
rotor.appendChild(spoke);
}
// Cabins (12) sur le pourtour
for(let i=0; i<12; i++){
const a = (i * Math.PI/6);
const R = 100; // rayon de la roue
const w = 18, h = 12;
const cx = Math.cos(a) * R;
const cy = Math.sin(a) * R;
const cabin = document.createElementNS(svg.namespaceURI,'rect');
cabin.setAttribute('class','wheel-cabin');
cabin.setAttribute('x', (cx - w/2).toFixed(2));
cabin.setAttribute('y', (cy - h/2).toFixed(2));
cabin.setAttribute('width', w);
cabin.setAttribute('height', h);
rotor.appendChild(cabin);
}
// Hub
const hub = document.createElementNS(svg.namespaceURI,'circle');
hub.setAttribute('class','wheel-hub');
hub.setAttribute('cx','0'); hub.setAttribute('cy','0'); hub.setAttribute('r','6.5');
rotor.appendChild(hub);
return rotor;
}
// Lance l’animation une seule fois (~5 s) au chargement
function autoplayOnce(host, rotor){
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
if (host.dataset.played === '1') return;
host.dataset.played = '1';
// Démarre l’animation
rotor.classList.add('play');
// Retire la classe à la fin pour éviter tout restart involontaire via reflow
rotor.addEventListener('animationend', ()=> {
rotor.classList.remove('play');
}, {once:true});
}
function init(){
document.querySelectorAll('.sw-apple-hero-wheel').forEach(host=>{
const rotor = renderHero(host);
autoplayOnce(host, rotor);
});
}
if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init);
}else{
init();
}
})();
</script>