develop #3
@@ -1,16 +1,3 @@
|
|||||||
// Entry point for the build script in your package.json
|
// Entry point for the build script in your package.json
|
||||||
import "@hotwired/turbo-rails"
|
import "@hotwired/turbo-rails"
|
||||||
import "./controllers"
|
import "./controllers"
|
||||||
import Counter from "./components/counter"
|
|
||||||
|
|
||||||
// Initialize counters when DOM is ready
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const counters = document.querySelectorAll('.counter')
|
|
||||||
counters.forEach(counter => new Counter(counter))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Re-initialize counters on Turbo page loads
|
|
||||||
document.addEventListener('turbo:load', () => {
|
|
||||||
const counters = document.querySelectorAll('.counter')
|
|
||||||
counters.forEach(counter => new Counter(counter))
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
export default class Counter {
|
|
||||||
constructor(element) {
|
|
||||||
this.element = element
|
|
||||||
this.target = parseFloat(element.dataset.target)
|
|
||||||
this.decimal = element.dataset.decimal === 'true'
|
|
||||||
this.duration = 2000
|
|
||||||
this.startTime = null
|
|
||||||
|
|
||||||
this.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const observer = new IntersectionObserver((entries) => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
this.animate()
|
|
||||||
observer.unobserve(this.element)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, { threshold: 0.5 })
|
|
||||||
|
|
||||||
observer.observe(this.element)
|
|
||||||
}
|
|
||||||
|
|
||||||
animate() {
|
|
||||||
const startValue = 0
|
|
||||||
const startTime = performance.now()
|
|
||||||
|
|
||||||
const updateCounter = (currentTime) => {
|
|
||||||
const elapsedTime = currentTime - startTime
|
|
||||||
const progress = Math.min(elapsedTime / this.duration, 1)
|
|
||||||
|
|
||||||
// Easing function for smooth animation
|
|
||||||
const easeOutQuart = 1 - Math.pow(1 - progress, 4)
|
|
||||||
|
|
||||||
let currentValue = startValue + (this.target - startValue) * easeOutQuart
|
|
||||||
|
|
||||||
if (this.decimal && this.target < 10) {
|
|
||||||
currentValue = currentValue.toFixed(1)
|
|
||||||
} else {
|
|
||||||
currentValue = Math.floor(currentValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.textContent = currentValue
|
|
||||||
|
|
||||||
if (progress < 1) {
|
|
||||||
requestAnimationFrame(updateCounter)
|
|
||||||
} else {
|
|
||||||
this.element.textContent = this.decimal && this.target < 10 ? this.target.toFixed(1) : this.target
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(updateCounter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize all counters
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const counters = document.querySelectorAll('.counter')
|
|
||||||
counters.forEach(counter => new Counter(counter))
|
|
||||||
})
|
|
||||||
@@ -1,2 +1,61 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static values = {
|
||||||
|
target: Number,
|
||||||
|
decimal: Boolean,
|
||||||
|
duration: { type: Number, default: 2000 }
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
this.animate()
|
||||||
|
this.observer.unobserve(this.element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, { threshold: 0.5 })
|
||||||
|
|
||||||
|
this.observer.observe(this.element)
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
if (this.observer) {
|
||||||
|
this.observer.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animate() {
|
||||||
|
const startValue = 0
|
||||||
|
const startTime = performance.now()
|
||||||
|
|
||||||
|
const updateCounter = (currentTime) => {
|
||||||
|
const elapsedTime = currentTime - startTime
|
||||||
|
const progress = Math.min(elapsedTime / this.durationValue, 1)
|
||||||
|
|
||||||
|
// Easing function for smooth animation
|
||||||
|
const easeOutQuart = 1 - Math.pow(1 - progress, 4)
|
||||||
|
|
||||||
|
let currentValue = startValue + (this.targetValue - startValue) * easeOutQuart
|
||||||
|
|
||||||
|
if (this.decimalValue && this.targetValue < 10) {
|
||||||
|
currentValue = currentValue.toFixed(1)
|
||||||
|
} else {
|
||||||
|
currentValue = Math.floor(currentValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.textContent = currentValue
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(updateCounter)
|
||||||
|
} else {
|
||||||
|
this.element.textContent = this.decimalValue && this.targetValue < 10
|
||||||
|
? this.targetValue.toFixed(1)
|
||||||
|
: this.targetValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(updateCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { Controller } from "@hotwired/stimulus"
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
connect() {
|
|
||||||
this.element.textContent = "Hello World!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
import { application } from "./application"
|
import { application } from "./application"
|
||||||
|
|
||||||
import HelloController from "./hello_controller"
|
|
||||||
import ShadcnTestController from "./shadcn_test_controller"
|
import ShadcnTestController from "./shadcn_test_controller"
|
||||||
|
import CounterController from "./counter_controller"
|
||||||
|
|
||||||
application.register("hello", HelloController)
|
|
||||||
application.register("shadcn-test", ShadcnTestController)
|
application.register("shadcn-test", ShadcnTestController)
|
||||||
|
application.register("counter", CounterController)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
|
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
|
||||||
<span class="counter" data-target="2473">0</span>
|
<span class="counter" data-controller="counter" data-counter-target-value="127">0</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
|
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
|
||||||
Événements organisés
|
Événements organisés
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
|
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
|
||||||
<span class="counter" data-target="15420">0</span>+
|
<span class="counter" data-controller="counter" data-counter-target-value="1433">0</span>+
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
|
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
|
||||||
Membres actifs
|
Membres actifs
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
|
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
|
||||||
<span class="counter" data-target="4.8" data-decimal="true">0</span>/5
|
<span class="counter" data-controller="counter" data-counter-target-value="4.4" data-counter-decimal-value="true">0</span>/5
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
|
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
|
||||||
Note moyenne des soirées
|
Note moyenne des soirées
|
||||||
@@ -84,28 +84,28 @@
|
|||||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-6 mt-12">
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-6 mt-12">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-3xl font-bold text-purple-300">
|
<div class="text-3xl font-bold text-purple-300">
|
||||||
<span class="counter" data-target="89">0</span>%
|
<span class="counter" data-controller="counter" data-counter-target-value="79">0</span>%
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Taux de remplissage</p>
|
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Taux de remplissage</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-3xl font-bold text-purple-300">
|
<div class="text-3xl font-bold text-purple-300">
|
||||||
<span class="counter" data-target="12">0</span>
|
<span class="counter" data-controller="counter" data-counter-target-value="12">0</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Arrondissements</p>
|
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Arrondissements</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-3xl font-bold text-purple-300">
|
<div class="text-3xl font-bold text-purple-300">
|
||||||
<span class="counter" data-target="156">0</span>
|
<span class="counter" data-controller="counter" data-counter-target-value="156">0</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Établissements partenaires</p>
|
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Établissements partenaires</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="text-3xl font-bold text-purple-300">
|
<div class="text-3xl font-bold text-purple-300">
|
||||||
<span class="counter" data-target="98">0</span>%
|
<span class="counter" data-controller="counter" data-counter-target-value="98">0</span>%
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Satisfaction client</p>
|
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Satisfaction client</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user