This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
:ui="{ container: '!p-0' }"
|
||||
>
|
||||
<template #body>
|
||||
<div class="benefits">
|
||||
<div ref="benefitsRef" class="benefits">
|
||||
<div
|
||||
v-for="benefit in benefits"
|
||||
:key="benefit.title"
|
||||
@@ -30,6 +30,41 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import gsap from 'gsap'
|
||||
import ScrollTrigger from 'gsap/ScrollTrigger'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger)
|
||||
|
||||
const benefitsRef = ref()
|
||||
let benefitsCtx
|
||||
|
||||
onMounted(() => {
|
||||
desktopAnimation()
|
||||
})
|
||||
|
||||
function desktopAnimation() {
|
||||
benefitsCtx = gsap.context((self) => {
|
||||
const boxes = self.selector('.benefit')
|
||||
|
||||
const tl = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: benefitsRef.value,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
})
|
||||
|
||||
tl.from(boxes, {
|
||||
y: 50,
|
||||
opacity: 0,
|
||||
duration: 0.8,
|
||||
ease: 'power3.out',
|
||||
stagger: 0.25,
|
||||
})
|
||||
}, benefitsRef.value)
|
||||
}
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
icon: '⏱️',
|
||||
@@ -62,6 +97,10 @@ const benefits = [
|
||||
description: 'Более 10 лет на рынке консьерж-услуг',
|
||||
},
|
||||
]
|
||||
|
||||
onUnmounted(() => {
|
||||
benefitsCtx?.revert()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -6,25 +6,22 @@
|
||||
:ui="{ container: '!p-0' }"
|
||||
>
|
||||
<template #body>
|
||||
<div class="how-works">
|
||||
<div ref="howWorksRef" class="how-works">
|
||||
<UCard
|
||||
v-for="item in items"
|
||||
:key="item.title"
|
||||
class="how-work"
|
||||
:ui="{ body: 'flex flex-col gap-4 h-[350px] !pb-15' }"
|
||||
:ui="cardUi"
|
||||
>
|
||||
<div class="how-work__number">
|
||||
{{ item.number }}
|
||||
</div>
|
||||
|
||||
<div class="how-work__icon">
|
||||
{{ item.icon }}
|
||||
</div>
|
||||
|
||||
<div class="how-work__title">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
|
||||
<div class="how-work__description">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
@@ -35,6 +32,50 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import gsap from 'gsap'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const isMobile = useMediaQuery('(max-width: 1280px)')
|
||||
const cardUi = computed(() => {
|
||||
return isMobile.value
|
||||
? { body: 'flex flex-col gap-2 h-[270px] !pb-5' }
|
||||
: { body: 'flex flex-col gap-4 h-[350px] !pb-15' }
|
||||
})
|
||||
|
||||
const howWorksRef = ref()
|
||||
let ctx
|
||||
|
||||
onMounted(() => {
|
||||
desktopAnimation()
|
||||
})
|
||||
|
||||
function desktopAnimation() {
|
||||
ctx = gsap.context((self) => {
|
||||
const boxes = self.selector('.how-work')
|
||||
|
||||
const tl = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: howWorksRef.value,
|
||||
start: 'top 50%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
})
|
||||
|
||||
tl.from(boxes, {
|
||||
x: -80,
|
||||
opacity: 0,
|
||||
duration: 0.8,
|
||||
ease: 'power3.out',
|
||||
stagger: 0.25,
|
||||
})
|
||||
}, howWorksRef.value)
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
ctx.revert()
|
||||
})
|
||||
|
||||
const items = [
|
||||
{
|
||||
icon: '💬',
|
||||
@@ -72,7 +113,7 @@ const items = [
|
||||
|
||||
@include mobile {
|
||||
gap: 10px;
|
||||
grid-template-columns: repeat(2, minmax(150px, 350px));
|
||||
grid-template-columns: repeat(1, minmax(150px, 350px));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<UPageSection
|
||||
id="services"
|
||||
title="Что мы предлагаем"
|
||||
description="Полный спектр консьерж-услуг для работы с недвижимостью и автомобилями"
|
||||
:ui="{ container: '!p-0' }"
|
||||
>
|
||||
<template #body>
|
||||
<div class="services">
|
||||
<div ref="servicesRef" class="services">
|
||||
<NuxtLink
|
||||
v-for="service in services"
|
||||
:key="service.name"
|
||||
@@ -41,6 +42,52 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import gsap from 'gsap'
|
||||
import ScrollTrigger from 'gsap/ScrollTrigger'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger)
|
||||
|
||||
const servicesRef = ref()
|
||||
let servicesCtx
|
||||
|
||||
onMounted(() => {
|
||||
servicesAnimation()
|
||||
})
|
||||
|
||||
function servicesAnimation() {
|
||||
servicesCtx = gsap.context((self) => {
|
||||
const cards = self.selector('.service')
|
||||
|
||||
cards.forEach((card) => {
|
||||
gsap.from(card, {
|
||||
y: 80,
|
||||
rotateY: 15,
|
||||
opacity: 0,
|
||||
duration: 1,
|
||||
ease: 'power4.out',
|
||||
scrollTrigger: {
|
||||
trigger: card,
|
||||
start: 'top 70%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
cards.forEach((card) => {
|
||||
const hover = gsap.to(card, {
|
||||
scale: 1.01,
|
||||
duration: 0.3,
|
||||
paused: true,
|
||||
ease: 'power2.out',
|
||||
})
|
||||
|
||||
card.addEventListener('mouseenter', () => hover.play())
|
||||
card.addEventListener('mouseleave', () => hover.reverse())
|
||||
})
|
||||
}, servicesRef.value)
|
||||
}
|
||||
|
||||
const services = [
|
||||
{
|
||||
name: 'nedvizhimost',
|
||||
@@ -87,6 +134,10 @@ const services = [
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
onUnmounted(() => {
|
||||
servicesCtx?.revert()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -109,11 +160,6 @@ const services = [
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
|
||||
transition: 0.15s ease-out;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user