Refonte accueil : hero typo statique, axes icônes, menu italic, page numérique
- Hero : 5 lignes typographiques alternées (bold/light/accent/caps/italic), citations et axes dans un bloc discret dépliable - Icônes axes : Ğ1 custom, balance (éco don), graphe (WoT), marteau (décision), pictos plus lumineux (glow) - Menu : Autonomie en italique + grand, Événement majuscule - Page /autonomie renommée /numerique avec redirect 301 - Sceau hexagramme 益 Yì dans le layout, BookSection dans /modele-eco - Fonts Syne + Space Grotesk, dark theme éclairci - Popup GrateWizard agrandie (480×860) - Actions AxisBlock : primary côte à côte, secondary séparé dessous Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
export interface TypewriterSentence {
|
||||
text: string
|
||||
style?: 'title' | 'citation' | 'text'
|
||||
stays?: boolean
|
||||
separator?: boolean
|
||||
}
|
||||
|
||||
interface SequenceOptions {
|
||||
fadeMs?: number
|
||||
holdMs?: number
|
||||
gapMs?: number
|
||||
}
|
||||
|
||||
export function useTypewriter(sentences: TypewriterSentence[], options: SequenceOptions = {}) {
|
||||
const {
|
||||
fadeMs = 1000,
|
||||
holdMs = 2800,
|
||||
gapMs = 300,
|
||||
} = options
|
||||
|
||||
const currentText = ref('')
|
||||
const currentStyle = ref<string>('title')
|
||||
const isVisible = ref(false)
|
||||
const lockedSentences = ref<TypewriterSentence[]>([])
|
||||
const isComplete = ref(false)
|
||||
|
||||
let currentIdx = -1
|
||||
let timer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function clearTimer() {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
|
||||
function next() {
|
||||
currentIdx++
|
||||
if (currentIdx >= sentences.length) {
|
||||
currentText.value = ''
|
||||
isComplete.value = true
|
||||
return
|
||||
}
|
||||
|
||||
const sentence = sentences[currentIdx]
|
||||
|
||||
if (sentence.separator) {
|
||||
lockedSentences.value = [...lockedSentences.value, { text: '', separator: true }]
|
||||
}
|
||||
|
||||
// Set text while invisible
|
||||
currentText.value = sentence.text
|
||||
currentStyle.value = sentence.style || 'title'
|
||||
|
||||
// Fade in on next frame
|
||||
requestAnimationFrame(() => {
|
||||
isVisible.value = true
|
||||
})
|
||||
|
||||
// After fade-in + hold → fade out
|
||||
timer = setTimeout(() => {
|
||||
isVisible.value = false
|
||||
|
||||
// After fade-out completes → lock if stays, then next
|
||||
timer = setTimeout(() => {
|
||||
if (sentence.stays) {
|
||||
lockedSentences.value = [...lockedSentences.value, { ...sentence }]
|
||||
}
|
||||
timer = setTimeout(next, gapMs)
|
||||
}, fadeMs)
|
||||
}, fadeMs + holdMs)
|
||||
}
|
||||
|
||||
function start() {
|
||||
next()
|
||||
}
|
||||
|
||||
function skipToEnd() {
|
||||
clearTimer()
|
||||
isVisible.value = false
|
||||
currentText.value = ''
|
||||
|
||||
const locked: TypewriterSentence[] = []
|
||||
for (const sentence of sentences) {
|
||||
if (sentence.separator) {
|
||||
locked.push({ text: '', separator: true })
|
||||
}
|
||||
if (sentence.stays) {
|
||||
locked.push({ ...sentence })
|
||||
}
|
||||
}
|
||||
|
||||
lockedSentences.value = locked
|
||||
currentIdx = sentences.length - 1
|
||||
isComplete.value = true
|
||||
}
|
||||
|
||||
onUnmounted(clearTimer)
|
||||
|
||||
return {
|
||||
currentText: readonly(currentText),
|
||||
currentStyle: readonly(currentStyle),
|
||||
isVisible,
|
||||
lockedSentences: readonly(lockedSentences),
|
||||
isComplete: readonly(isComplete),
|
||||
start,
|
||||
skipToEnd,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user