swiper and clean
This commit is contained in:
18
src/hooks/useAnimations.js
Normal file
18
src/hooks/useAnimations.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
export default function useAnimations(containerRef) {
|
||||
const { pathname } = useLocation()
|
||||
useEffect(() => {
|
||||
const el = containerRef.current
|
||||
if (!el) return
|
||||
|
||||
el.querySelectorAll('[data-ani]').forEach(el => {
|
||||
el.classList.add(el.dataset.ani)
|
||||
})
|
||||
|
||||
el.querySelectorAll('[data-ani-delay]').forEach(el => {
|
||||
el.style.animationDelay = el.dataset.aniDelay
|
||||
})
|
||||
}, [containerRef, pathname])
|
||||
}
|
||||
36
src/hooks/useBackgroundAttrs.js
Normal file
36
src/hooks/useBackgroundAttrs.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
export default function useBackgroundAttrs(containerRef) {
|
||||
const { pathname } = useLocation()
|
||||
useEffect(() => {
|
||||
const el = containerRef.current
|
||||
if (!el) return
|
||||
|
||||
// data-bg-src → background-image
|
||||
el.querySelectorAll('[data-bg-src]').forEach(el => {
|
||||
el.style.backgroundImage = 'url(' + el.dataset.bgSrc + ')'
|
||||
el.removeAttribute('data-bg-src')
|
||||
el.classList.add('background-image')
|
||||
})
|
||||
|
||||
// data-bg-color → background-color
|
||||
el.querySelectorAll('[data-bg-color]').forEach(el => {
|
||||
el.style.backgroundColor = el.dataset.bgColor
|
||||
el.removeAttribute('data-bg-color')
|
||||
})
|
||||
|
||||
// data-border → CSS var
|
||||
el.querySelectorAll('[data-border]').forEach(el => {
|
||||
el.style.setProperty('--th-border-color', el.dataset.border)
|
||||
})
|
||||
|
||||
// data-mask-src → mask-image
|
||||
el.querySelectorAll('[data-mask-src]').forEach(el => {
|
||||
el.style.maskImage = 'url(' + el.dataset.maskSrc + ')'
|
||||
el.style.webkitMaskImage = 'url(' + el.dataset.maskSrc + ')'
|
||||
el.classList.add('bg-mask')
|
||||
el.removeAttribute('data-mask-src')
|
||||
})
|
||||
}, [containerRef, pathname])
|
||||
}
|
||||
133
src/hooks/useSwiper.js
Normal file
133
src/hooks/useSwiper.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import Swiper from 'swiper'
|
||||
import { Navigation, Pagination, Autoplay, EffectFade } from 'swiper/modules'
|
||||
|
||||
export default function useSwiper(containerRef) {
|
||||
const { pathname } = useLocation()
|
||||
const instances = useRef([])
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current
|
||||
if (!container) return
|
||||
|
||||
// Destroy previous instances
|
||||
instances.current.forEach(s => s.destroy(true, true))
|
||||
instances.current = []
|
||||
|
||||
// Init all .th-slider elements
|
||||
container.querySelectorAll('.th-slider').forEach(el => {
|
||||
try {
|
||||
const userOptions = JSON.parse(el.dataset.sliderOptions || '{}')
|
||||
|
||||
// Find pagination element — inside slider or sibling .slider-controller
|
||||
let paginationEl = el.querySelector('.slider-pagination')
|
||||
if (!paginationEl) {
|
||||
const controller = el.closest('.th-slider')?.parentElement?.querySelector('.slider-controller')
|
||||
if (controller) paginationEl = controller.querySelector('.slider-pagination')
|
||||
}
|
||||
|
||||
const hasNumberPagi = el.querySelector('.slider-pagination.pagi-number')
|
||||
const paginationType = userOptions.paginationType || 'bullets'
|
||||
|
||||
const swiper = new Swiper(el, {
|
||||
modules: [Navigation, Pagination, Autoplay, EffectFade],
|
||||
slidesPerView: userOptions.slidesPerView || 1,
|
||||
spaceBetween: userOptions.spaceBetween !== undefined ? userOptions.spaceBetween : 24,
|
||||
loop: userOptions.loop !== false,
|
||||
speed: userOptions.speed || 1000,
|
||||
autoplay: userOptions.autoplay || { delay: 6000, disableOnInteraction: false },
|
||||
effect: userOptions.effect || 'slide',
|
||||
centeredSlides: userOptions.centeredSlides || false,
|
||||
slidesPerGroup: userOptions.slidesPerGroup || 1,
|
||||
breakpoints: userOptions.breakpoints || {},
|
||||
navigation: {
|
||||
nextEl: el.querySelector('.slider-next'),
|
||||
prevEl: el.querySelector('.slider-prev'),
|
||||
},
|
||||
pagination: paginationEl ? {
|
||||
el: paginationEl,
|
||||
type: paginationType,
|
||||
clickable: true,
|
||||
renderBullet: hasNumberPagi ? function (index, className) {
|
||||
const num = index + 1
|
||||
return '<span class="' + className + ' number">' + (num < 10 ? '0' + num : num) + '</span>'
|
||||
} : undefined,
|
||||
formatFractionCurrent: function (n) { return n < 10 ? '0' + n : n },
|
||||
formatFractionTotal: function (n) { return n < 10 ? '0' + n : n },
|
||||
} : false,
|
||||
|
||||
on: {
|
||||
slideChange: function () {
|
||||
setTimeout(function () {
|
||||
if (swiper.params.mousewheel) swiper.params.mousewheel.releaseOnEdges = false
|
||||
}, 500)
|
||||
},
|
||||
reachEnd: function () {
|
||||
setTimeout(function () {
|
||||
if (swiper.params.mousewheel) swiper.params.mousewheel.releaseOnEdges = true
|
||||
}, 750)
|
||||
},
|
||||
...(userOptions.on || {}),
|
||||
},
|
||||
})
|
||||
|
||||
instances.current.push(swiper)
|
||||
} catch (e) {
|
||||
console.warn('useSwiper: failed to init', el.id || el.className, e)
|
||||
}
|
||||
})
|
||||
|
||||
// External controls: [data-slider-prev] and [data-slider-next]
|
||||
const handleClick = (e) => {
|
||||
const btn = e.target.closest('[data-slider-prev], [data-slider-next]')
|
||||
if (!btn) return
|
||||
const selector = btn.dataset.sliderPrev || btn.dataset.sliderNext
|
||||
const sliderEl = document.querySelector(selector)
|
||||
if (!sliderEl || !sliderEl.swiper) return
|
||||
if (btn.dataset.sliderPrev) sliderEl.swiper.slidePrev()
|
||||
else sliderEl.swiper.slideNext()
|
||||
}
|
||||
container.addEventListener('click', handleClick)
|
||||
|
||||
// Slider tabs: [data-slider-tab]
|
||||
const tabContainers = container.querySelectorAll('[data-slider-tab]')
|
||||
tabContainers.forEach(tc => {
|
||||
const tabButtons = tc.querySelectorAll('.tab-btn')
|
||||
const sliderSelector = tc.dataset.sliderTab
|
||||
const sliderEl = document.querySelector(sliderSelector)
|
||||
if (!sliderEl || !sliderEl.swiper || !tabButtons.length) return
|
||||
|
||||
const swiper = sliderEl.swiper
|
||||
|
||||
const setActive = (btn) => {
|
||||
tabButtons.forEach(b => b.classList.remove('active'))
|
||||
btn.classList.add('active')
|
||||
}
|
||||
|
||||
tabButtons.forEach((btn, i) => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
setActive(btn)
|
||||
swiper.slideTo(i)
|
||||
})
|
||||
})
|
||||
|
||||
swiper.on('slideChange', () => {
|
||||
const idx = swiper.realIndex
|
||||
if (tabButtons[idx]) setActive(tabButtons[idx])
|
||||
})
|
||||
|
||||
// Set initial active
|
||||
if (tabButtons[swiper.activeIndex]) {
|
||||
tabButtons[swiper.activeIndex].classList.add('active')
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
instances.current.forEach(s => s.destroy(true, true))
|
||||
instances.current = []
|
||||
container.removeEventListener('click', handleClick)
|
||||
}
|
||||
}, [containerRef, pathname])
|
||||
}
|
||||
Reference in New Issue
Block a user