banner updated

This commit is contained in:
Alaguraj0361 2025-10-02 17:25:45 +05:30
parent 0124c57991
commit c8f24b2b12
5 changed files with 276 additions and 440 deletions

View File

@ -23,8 +23,8 @@ export default function Blog() {
<div key={i} className="col-lg-4 col-md-6 col-sm-12 news-block"> <div key={i} className="col-lg-4 col-md-6 col-sm-12 news-block">
<div <div
className="news-block-one wow fadeInUp animated" className="news-block-one wow fadeInUp animated"
data-wow-delay={`${i * 300}ms`} data-wow-delay={`${i * 20}ms`}
data-wow-duration="1500ms" data-wow-duration="300ms"
> >
<div className="inner-box"> <div className="inner-box">
<figure className="image-box"> <figure className="image-box">

View File

@ -1,237 +1,161 @@
'use client' "use client"
import React, { useState } from 'react'; import React, { useState } from "react"
import Link from "next/link"; import Link from "next/link"
import { Autoplay, Navigation, Pagination } from "swiper/modules"; import { useKeenSlider } from "keen-slider/react"
import { Swiper, SwiperSlide } from "swiper/react"; import "keen-slider/keen-slider.min.css"
import { motion, AnimatePresence } from "framer-motion";
import "swiper/css"; // Autoplay plugin
import "swiper/css/navigation"; function AutoplaySlider(slider) {
import "swiper/css/pagination"; let timeout
let mouseOver = false
const swiperOptions = { function clearNextTimeout() {
modules: [Autoplay, Pagination, Navigation], clearTimeout(timeout)
slidesPerView: 1,
spaceBetween: 0,
autoplay: {
delay: 2000,
disableOnInteraction: false,
pauseOnMouseEnter: true, // stops on hover for smooth UX
},
speed: 1500, // increase speed for smoother sliding
loop: true,
navigation: {
nextEl: '.h1n',
prevEl: '.h1p',
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
effect: "slide", // ensures smooth slide effect
};
const variants = {
topToBottom: {
initial: { y: '-100vh', opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: '100vh', opacity: 0 }
},
bottomToTop: {
initial: { y: '100vh', opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: '-100vh', opacity: 0 }
},
leftToRight: {
initial: { x: '-100vw', opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: '100vw', opacity: 0 }
},
rightToLeft: {
initial: { x: '100vw', opacity: 0 },
animate: {
x: 0,
opacity: 1,
transition: {
duration: 0.8, // adjust speed
ease: "easeInOut" // try "easeOut", "easeIn", or custom [0.4, 0, 0.2, 1]
}
},
exit: {
x: "-100vw",
opacity: 0,
transition: {
duration: 0.6,
ease: "easeInOut"
}
}
},
};
const revealVariants = {
hidden: {
scaleX: 0,
opacity: 0,
originX: 0, // same as transform-origin: 0% 50%
},
visible: {
scaleX: 1,
opacity: 1,
originX: 0,
transition: {
duration: 0.6,
ease: "easeInOut"
}
},
exit: {
scaleX: 0,
opacity: 0,
originX: 0,
transition: {
duration: 0.6,
ease: "easeInOut"
}
} }
}; function nextTimeout() {
clearTimeout(timeout)
const transition = { if (mouseOver) return
type: "tween", timeout = setTimeout(() => {
ease: [0.4, 0.0, 0.2, 1], slider.next()
duration: 1.2 }, 4000)
}; }
slider.on("created", () => {
slider.container.addEventListener("mouseover", () => {
mouseOver = true
clearNextTimeout()
})
slider.container.addEventListener("mouseout", () => {
mouseOver = false
nextTimeout()
})
nextTimeout()
})
slider.on("dragStarted", clearNextTimeout)
slider.on("animationEnded", nextTimeout)
slider.on("updated", nextTimeout)
}
export default function Banner() { export default function Banner() {
const [activeIndex, setActiveIndex] = useState(0); const [sliderLoaded, setSliderLoaded] = useState(false)
const [isAnimating, setIsAnimating] = useState(false);
const handleSlideChange = (swiper) => { const [sliderRef] = useKeenSlider(
setIsAnimating(true); {
setActiveIndex(swiper.realIndex || 0); loop: true,
setTimeout(() => setIsAnimating(false), 1200); renderMode: "performance",
}; slides: { perView: 1, spacing: 0 },
duration: 1000, // increase for smoother transition
easing: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2, // smooth cubic easing
created() {
setSliderLoaded(true)
},
},
[AutoplaySlider]
)
const slides = [
{
id: 0,
bg: "/assets/images/banner/desktopBanner/banner-4.png",
upper: "Begin your",
title: "Recovery",
desc: "• Rehab • Strength Training • Personalized Care",
btn: { text: "Visit Our Location", link: "/contact" },
align: "start",
},
{
id: 1,
bg: "/assets/images/banner/desktopBanner/banner-1.png",
upper: "Build Your Strength &",
title: "Endurance",
desc: "• Physiotherapy • Sports Therapy • Injury Prevention",
btn: { text: "Book Your Appointment", link: "tel:+647-722-3434" },
align: "center",
},
{
id: 2,
bg: "/assets/images/banner/desktopBanner/banner-3.webp",
upper: "Regain Your Strength",
title: "Heal",
desc: "• Pain Relief • Mobility • Wellness",
btn: { text: "Schedule a Massage", link: "/contact" },
align: "start",
},
{
id: 3,
bg: "/assets/images/banner/desktopBanner/banner-2.png",
upper: "Build your Core",
title: "Performance",
desc: "• Pain Relief • Active Care • Long-Term Result",
btn: { text: "Explore Our Service", link: "/etobicoke-treatment-service" },
align: "space-evenly",
},
]
return ( return (
<section className="banner-style-two p_relative"> <section className="banner-style-two p_relative">
<Swiper {...swiperOptions} <div
className="banner-carousel owl-theme owl-carousel owl-nav-none owl-dots-none" ref={sliderRef}
onSwiper={(swiper) => setActiveIndex(swiper.realIndex || 0)} className="keen-slider"
onSlideChange={handleSlideChange} style={{ opacity: sliderLoaded ? 1 : 0, transition: "opacity 0.5s ease" }}
> >
{slides.map((s) => (
<SwiperSlide> <div
<AnimatePresence mode="wait"> key={s.id}
{activeIndex === 0 && ( className="keen-slider__slide"
<motion.div key="slide-3" style={{
className="slide-item banner-slide" minHeight: "600px",
variants={revealVariants} width: "100%",
initial="initial" animate="animate" exit="exit" transition={transition}> backgroundImage: `url(${s.bg})`,
<div className="bg-layer" backgroundSize: "cover",
style={{ backgroundImage: 'url(/assets/images/banner/desktopBanner/banner-4.png)' }}> backgroundPosition: "center",
</div> display: "flex",
<div className="auto-container" style={{ height: "600px", display: "flex", alignItems: "end", justifyContent: "start", textAlign: "start" }}> alignItems: "end",
<div className="content-box custom-content-box"> justifyContent:
<span className="upper-text mb-2">Begin your</span> s.align === "start"
<h2 style={{ color: "#bc0000" }}>Recovery</h2> ? "flex-start"
<p className='text-white'> Rehab Strength Training Personalized Care </p> : s.align === "end"
<div className="btn-box mt-3"> ? "flex-end"
<Link href="/contact" className="theme-btn btn-one"> : s.align === "center"
<span>Vist Our Location</span> ? "center"
</Link> : s.align === "space-between"
</div> ? "space-between"
</div> : s.align === "space-evenly"
</div> ? "space-evenly"
</motion.div> : "center",
)} padding: "0 150px 40px",
</AnimatePresence> }}
</SwiperSlide> >
<div
<SwiperSlide> className="content-box custom-content-box"
<AnimatePresence mode="wait"> style={{
{activeIndex === 1 && ( color: "#bc0000",
<motion.div key="slide-0" textAlign:
className="slide-item banner-slide" s.align === "space-between" || s.align === "space-evenly"
//variants={variants.rightToLeft} ? "center"
variants={revealVariants} : s.align,
initial="initial" animate="animate" exit="exit" transition={transition}> padding: "450px 0px 220px 0px",
<div className="bg-layer bg-slide-0" }}
style={{ backgroundImage: 'url(/assets/images/banner/desktopBanner/banner-1.png)' }}> >
</div> <span className="upper-text mb-2" style={{ fontSize: "22px" }}>{s.upper}</span>
<div className="auto-container" style={{ height: "600px", display: "flex", alignItems: "end", justifyContent: "end", textAlign: "center" }}> <h1
<div className="content-box custom-content-box"> style={{
<span className="upper-text mb-2 ">Build Your Strength & </span> color: "#bc0000",
<h2 style={{ color: "#bc0000" }}>Endurance</h2> fontWeight: "bold",
<p className=' text-white'> Physiotherapy Sports Therapy Injury Prevention</p> fontSize: "64px",
<div className="btn-box mt-3"> }}
<Link href="tel:+647-722-3434" className="theme-btn btn-one"> >
<span>Book Your Appointment</span> {s.title}
</Link> </h1>
</div> <p style={{ color: "#fff" }}>{s.desc}</p>
</div> <div className="btn-box mt-3">
</div> <Link href={s.btn.link} className="theme-btn btn-one">
</motion.div> <span>{s.btn.text}</span>
)} </Link>
</AnimatePresence> </div>
</SwiperSlide> </div>
</div>
{/* ✅ existing 3rd slide remains as 3rd */} ))}
<SwiperSlide> </div>
<AnimatePresence mode="wait">
{activeIndex === 2 && (
<motion.div key="slide-2"
className="slide-item banner-slide"
//variants={variants.rightToLeft}
variants={revealVariants}
initial="initial" animate="animate" exit="exit" transition={transition}>
<div className="bg-layer"
style={{ backgroundImage: 'url(/assets/images/banner/desktopBanner/banner-3.webp)' }}>
</div>
<div className="auto-container" style={{ height: "600px", display: "flex", alignItems: "end", justifyContent: "start", textAlign: "start" }}>
<div className="content-box custom-content-box">
<span className="upper-text mb-2">Regain Your Strength</span>
<h2 style={{ color: "#bc0000" }}> Heal </h2>
<p className=' text-white'> Pain Relief mobility Wellness </p>
<div className="btn-box mt-3">
<Link href="/contact" className="theme-btn btn-one">
<span>Schedule a Massage</span>
</Link>
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</SwiperSlide>
<SwiperSlide>
<AnimatePresence mode="wait">
{activeIndex === 3 && (
<motion.div key="slide-1"
className="slide-item banner-slide"
//variants={variants.rightToLeft}
variants={revealVariants}
initial="initial" animate="animate" exit="exit" transition={transition}>
<div className="bg-layer"
style={{ backgroundImage: 'url(/assets/images/banner/desktopBanner/banner-2.png)' }}>
</div>
<div className="auto-container" style={{ height: "600px", display: "flex", alignItems: "end", justifyContent: "center", textAlign: "center" }}>
<div className="content-box custom-content-box">
<span className="upper-text mb-3">Build your Core </span>
<h2 style={{ color: "#bc0000" }}>Performance</h2>
<p className=' text-white'> Pain Relief Active Care Long-Term Result</p>
<div className="btn-box mt-3">
<Link href="/etobicoke-treatment-service" className="theme-btn btn-one">
<span>Explore Our Service</span>
</Link>
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</SwiperSlide>
</Swiper>
</section> </section>
); )
} }

View File

@ -1,220 +1,124 @@
'use client' "use client";
import React, { useState, useEffect } from 'react'; import React, { useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { Autoplay, Navigation, Pagination } from "swiper/modules"; import { useKeenSlider } from "keen-slider/react";
import { Swiper, SwiperSlide } from "swiper/react"; import "keen-slider/keen-slider.min.css";
import { motion, AnimatePresence } from "framer-motion";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
const swiperOptions = {
modules: [Autoplay, Pagination, Navigation],
slidesPerView: 1,
spaceBetween: 0,
autoplay: {
delay: 10000,
disableOnInteraction: false,
},
loop: true,
navigation: {
nextEl: '.h1n',
prevEl: '.h1p',
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
};
const variants = {
topToBottom: { initial: { y: '-100vh', opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: '100vh', opacity: 0 } },
bottomToTop: { initial: { y: '100vh', opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: '-100vh', opacity: 0 } },
leftToRight: { initial: { x: '-100vw', opacity: 0 }, animate: { x: 0, opacity: 1 }, exit: { x: '100vw', opacity: 0 } },
rightToLeft: { initial: { x: '100vw', opacity: 0 }, animate: { x: 0, opacity: 1 }, exit: { x: '-100vw', opacity: 0 } },
};
const transition = { type: "tween", ease: [0.4, 0.0, 0.2, 1], duration: 1.2 };
const revealVariants = {
hidden: {
scaleX: 0,
opacity: 0,
originX: 0, // same as transform-origin: 0% 50%
},
visible: {
scaleX: 1,
opacity: 1,
originX: 0,
transition: {
duration: 0.8,
ease: "easeInOut"
}
},
exit: {
scaleX: 0,
opacity: 0,
originX: 0,
transition: {
duration: 0.6,
ease: "easeInOut"
}
}
};
export default function MobileBanner() { export default function MobileBanner() {
const [activeIndex, setActiveIndex] = useState(0); const [isMobile, setIsMobile] = useState(false);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => { React.useEffect(() => {
const handleResize = () => setIsMobile(window.innerWidth <= 768); const handleResize = () => setIsMobile(window.innerWidth <= 768);
handleResize(); handleResize();
window.addEventListener('resize', handleResize); window.addEventListener("resize", handleResize);
return () => window.removeEventListener('resize', handleResize); return () => window.removeEventListener("resize", handleResize);
}, []); }, []);
const slides = [ const [sliderRef] = useKeenSlider({
{ loop: true,
id: 0, autoplay: {
variant: 'revealVariants', delay: 4000,
bgImage: '/assets/images/banner/mobile-banner/4.webp', pauseOnMouseEnter: true,
upperText: 'Begin your ', stopOnInteraction: false,
title: ' Recovery', },
// titleSpan: 'Wellness', slides: { perView: 1, spacing: 0 },
// titleEnd: 'Care', });
// subtitle: 'Waterfront Physio and Rehab Services.',
description: 'Discover holistic physiotherapy and rehab services designed to restore balance, ease pain, and support long-term recovery.',
buttonText: 'Book Your Appointment',
buttonLink: '/contact',
// contentStyle: 'with-background'
},
{
id: 1,
variant: 'revealVariants',
bgImage: '/assets/images/banner/mobile-banner/1.webp',
upperText: 'Build Your Strength & ',
title: 'Endurance',
// titleSpan: 'health',
// titleEnd: 'Forever',
subtitle: 'Expert Physiotherapy in Mississauga for You.',
description: 'Our expert physiotherapists help you restore movement, reduce pain, and live healthier with personalized care in Mississauga.',
buttonText: 'Book Your Appointment',
buttonLink: 'tel:+647-722-3434',
contentStyle: 'mobile-style'
},
{ const slides = [
id: 2, {
variant: 'revealVariants', id: 0,
bgImage: '/assets/images/banner/mobile-banner/3.webp', bgImage: "/assets/images/banner/mobile-banner/4.webp",
upperText: 'Regain your Flexiblity & ', upperText: "Begin your ",
title: 'Balance', title: "Recovery",
// titleSpan: 'Physio', description:
// titleEnd: 'Experts', "Discover holistic physiotherapy and rehab services designed to restore balance, ease pain, and support long-term recovery.",
// subtitle: 'Physiotherapy Etobicoke & Rehab Care.', buttonText: "Book Your Appointment",
description: 'Comprehensive physiotherapy and rehab services designed to restore your strength, mobility and long-term wellness.', buttonLink: "/contact",
buttonText: 'Explore Our Service', },
buttonLink: '/etobicoke-treatment-service', {
// contentStyle: 'with-background' id: 1,
}, bgImage: "/assets/images/banner/mobile-banner/1.webp",
{ upperText: "Build Your Strength & ",
id: 3, title: "Endurance",
variant: 'revealVariants', subtitle: "Expert Physiotherapy in Mississauga for You.",
bgImage: '/assets/images/banner/mobile-banner/2.webp', description:
upperText: 'Build your Core ', "Our expert physiotherapists help you restore movement, reduce pain, and live healthier with personalized care in Mississauga.",
title: 'Performance', buttonText: "Book Your Appointment",
// titleSpan: 'Massage', buttonLink: "tel:+647-722-3434",
// titleEnd: 'Therapy', },
// subtitle: 'Expert Hand Massage Techniques for Relief', {
description: 'Experience soothing massage techniques that release tension, promote circulation, and support your overall wellness.', id: 2,
buttonText: 'Schedule a Massage', bgImage: "/assets/images/banner/mobile-banner/3.webp",
buttonLink: '/contact', upperText: "Regain your Flexibility & ",
// contentStyle: 'with-background' title: "Balance",
}, description:
"Comprehensive physiotherapy and rehab services designed to restore your strength, mobility and long-term wellness.",
buttonText: "Explore Our Service",
buttonLink: "/etobicoke-treatment-service",
},
{
id: 3,
bgImage: "/assets/images/banner/mobile-banner/2.webp",
upperText: "Build your Core ",
title: "Performance",
description:
"Experience soothing massage techniques that release tension, promote circulation, and support your overall wellness.",
buttonText: "Schedule a Massage",
buttonLink: "/contact",
},
];
]; if (!isMobile) return null;
if (!isMobile) return null; return (
<section
return ( className="banner-style-two"
<section style={{ minHeight: "100vh", position: "relative" }}
className="banner-style-two p_relative" >
style={{ minHeight: '100vh', position: 'relative' }} <div ref={sliderRef} className="keen-slider">
> {slides.map((slide) => (
<Swiper <div
{...swiperOptions} key={slide.id}
className="banner-carousel" className="keen-slider__slide"
onSwiper={(swiper) => setActiveIndex(swiper.realIndex || 0)} style={{
onSlideChange={(swiper) => setActiveIndex(swiper.realIndex || 0)} minHeight: "100vh",
position: "relative",
backgroundImage: `url(${slide.bgImage})`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<div
className="auto-container"
style={{
position: "relative",
zIndex: 1,
minHeight: "100vh",
display: "flex",
justifyContent: "center",
alignItems: "center",
paddingTop: "200px",
color: "#fff",
textAlign: "center",
}}
> >
{slides.map((slide, index) => ( <div className="content-box custom-content-box">
<SwiperSlide key={slide.id}> <span className="upper-text mb-2">{slide.upperText}</span>
<AnimatePresence mode="wait"> <h2 style={{ fontSize: "32px", lineHeight: "42px", color:"#bc0000", fontWeight:"bold" }}>
{activeIndex === index && ( {slide.title}
<motion.div </h2>
key={`slide-${index}`} {/* {slide.subtitle && <p>{slide.subtitle}</p>} */}
className="slide-item" {/* <p>{slide.description}</p> */}
style={{ minHeight: '100vh', position: 'relative' }} <div className="btn-box mt-3">
variants={variants[slide.variant]} <Link href={slide.buttonLink} className="theme-btn btn-one-new">
initial="initial" <span>{slide.buttonText}</span>
animate="animate" </Link>
exit="exit" </div>
transition={transition} </div>
> </div>
{/* full-height background */} </div>
<div ))}
className="bg-layer" </div>
style={{ </section>
position: 'absolute', );
top: 0, }
left: 0,
width: '100%',
height: '100%',
backgroundImage: `url(${slide.bgImage})`,
backgroundSize: 'cover',
backgroundPosition: 'center'
}}
/>
{/* centered content */}
<div
className="auto-container"
style={{
position: 'relative',
zIndex: 1,
minHeight: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'end',
paddingBottom: "20px"
}}
>
{!slide.hideContent && (
<div
className={`content-box custom-content-box ${slide.contentStyle || ''}`}
>
<span className="upper-text mb-2 ">{slide.upperText}</span>
<h2 style={{ fontSize: "32px !important", lineHeight: "42px" }}>
{slide.title} {slide.titleSpan} {slide.titleEnd}
</h2>
<p>{slide.subtitle}</p>
<p>{slide.description}</p>
<div className="btn-box mt-3">
<Link href={slide.buttonLink} className="theme-btn btn-one-new">
<span>{slide.buttonText}</span>
</Link>
</div>
</div>
)}
</div>
</motion.div>
)}
</AnimatePresence>
</SwiperSlide>
))}
</Swiper>
</section>
);
};

7
package-lock.json generated
View File

@ -17,6 +17,7 @@
"imagemin-optipng": "^8.0.0", "imagemin-optipng": "^8.0.0",
"imagemin-webp": "^8.0.0", "imagemin-webp": "^8.0.0",
"isotope-layout": "^3.0.6", "isotope-layout": "^3.0.6",
"keen-slider": "^6.8.6",
"lightgallery": "^2.7.2", "lightgallery": "^2.7.2",
"next": "^14.0.4-canary.36", "next": "^14.0.4-canary.36",
"react": "18.2.0", "react": "18.2.0",
@ -3704,6 +3705,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/keen-slider": {
"version": "6.8.6",
"resolved": "https://registry.npmjs.org/keen-slider/-/keen-slider-6.8.6.tgz",
"integrity": "sha512-dcEQ7GDBpCjUQA8XZeWh3oBBLLmyn8aoeIQFGL/NTVkoEOsmlnXqA4QykUm/SncolAZYGsEk/PfUhLZ7mwMM2w==",
"license": "MIT"
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz",

View File

@ -22,6 +22,7 @@
"imagemin-optipng": "^8.0.0", "imagemin-optipng": "^8.0.0",
"imagemin-webp": "^8.0.0", "imagemin-webp": "^8.0.0",
"isotope-layout": "^3.0.6", "isotope-layout": "^3.0.6",
"keen-slider": "^6.8.6",
"lightgallery": "^2.7.2", "lightgallery": "^2.7.2",
"next": "^14.0.4-canary.36", "next": "^14.0.4-canary.36",
"react": "18.2.0", "react": "18.2.0",