feat: Implement Animations for landing page
This commit is contained in:
+2
-2
@@ -13,9 +13,9 @@
|
|||||||
"@astrojs/sitemap": "^3.7.0",
|
"@astrojs/sitemap": "^3.7.0",
|
||||||
"@astrojs/vue": "^5.1.4",
|
"@astrojs/vue": "^5.1.4",
|
||||||
"@astrolib/seo": "1.0.0-beta.8",
|
"@astrolib/seo": "1.0.0-beta.8",
|
||||||
"astro": "^5.17.3",
|
"astro": "^5.18.0",
|
||||||
"astro-robots-txt": "^1.0.0",
|
"astro-robots-txt": "^1.0.0",
|
||||||
"gsap": "^3.14.2",
|
"gsap": "^3.14.2",
|
||||||
"vue": "^3.5.28"
|
"vue": "^3.5.29"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+293
-292
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ function getCurrentYear() {
|
|||||||
<span class="list-title">Explore</span>
|
<span class="list-title">Explore</span>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li><a href="/blog">Blog</a></li>
|
{/* <li><a href="/blog">Blog</a></li> */}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -96,3 +96,27 @@
|
|||||||
border: 2px solid var(--clr-ts-red-400);
|
border: 2px solid var(--clr-ts-red-400);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import { SplitText } from "gsap/SplitText";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
|
gsap.registerPlugin(SplitText);
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const footer = document.querySelector("footer");
|
||||||
|
|
||||||
|
if (footer) {
|
||||||
|
gsap.from(footer, {
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: footer,
|
||||||
|
start: "top 90%",
|
||||||
|
},
|
||||||
|
duration: 2,
|
||||||
|
opacity: 0,
|
||||||
|
yPercent: 10,
|
||||||
|
ease: "expo.out",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import SkipNavLink from "./SkipNavLink.astro";
|
|||||||
<Logo class="logo" />
|
<Logo class="logo" />
|
||||||
|
|
||||||
{/* Contact Link */}
|
{/* Contact Link */}
|
||||||
<a class="contact" href="mailto:office@tideshiftdigital.com">
|
<a href="mailto:office@tideshiftdigital.com">
|
||||||
<span class="heading-gradient">Contact us</span>
|
<span class="heading-gradient">Contact us</span>
|
||||||
<ArrowUpRight class="icon char" />
|
<ArrowUpRight class="icon char" />
|
||||||
</a>
|
</a>
|
||||||
@@ -42,7 +42,7 @@ import SkipNavLink from "./SkipNavLink.astro";
|
|||||||
grid-column: 1 / 3;
|
grid-column: 1 / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact {
|
a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
@@ -78,9 +78,9 @@ import SkipNavLink from "./SkipNavLink.astro";
|
|||||||
const header = document.querySelector("header");
|
const header = document.querySelector("header");
|
||||||
|
|
||||||
if (header) {
|
if (header) {
|
||||||
const timeline = gsap.timeline();
|
const tl = gsap.timeline();
|
||||||
|
|
||||||
timeline.from(header.children, {
|
tl.from(header.children, {
|
||||||
duration: 1,
|
duration: 1,
|
||||||
yPercent: -120,
|
yPercent: -120,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
|||||||
@@ -3,20 +3,21 @@ import AboutImage from "../../assets/img/about.jpg";
|
|||||||
import { Picture } from "astro:assets";
|
import { Picture } from "astro:assets";
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment>
|
<div class="about">
|
||||||
<div class="about" aria-label="About section">
|
<div class="heading" aria-label="About section">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="heading-gradient">Creative.</span>
|
<span class="heading-gradient">Creative.</span>
|
||||||
<span class="heading-gradient">Innovative.</span>
|
<span class="heading-gradient">Innovative.</span>
|
||||||
<span class="heading-gradient">Authentic.</span>
|
<span class="heading-gradient">Authentic.</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="text">
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Based in the beautiful city of Vienna, Austria, Tideshift aims
|
Based in the beautiful city of Vienna, Austria, Tideshift aims
|
||||||
to create long lasting partnerships built on sustainable tech
|
to create long lasting partnerships built on sustainable tech
|
||||||
and processes.
|
and processes.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Whether you're just starting out as a business or you're an
|
Whether you're just starting out as a business or you're an
|
||||||
enterprise, we will cater to your specific needs and provide
|
enterprise, we will cater to your specific needs and provide
|
||||||
@@ -39,38 +40,41 @@ import { Picture } from "astro:assets";
|
|||||||
source software.
|
source software.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.about {
|
.about {
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: var(--base-grid);
|
grid-template-columns: var(--base-grid);
|
||||||
max-width: var(--max-content-width);
|
max-width: var(--max-content-width);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
padding-block: 8rem 5.5rem;
|
padding-block: 8rem 5.5rem;
|
||||||
padding-inline: 5rem;
|
padding-inline: 5rem;
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
& h2 {
|
||||||
grid-column: 1 / 6;
|
grid-column: 1 / 6;
|
||||||
|
|
||||||
font-size: 4.75rem;
|
font-size: 4.75rem;
|
||||||
line-height: var(--leading-title);
|
line-height: var(--leading-title);
|
||||||
letter-spacing: var(--tracking-narrow);
|
letter-spacing: var(--tracking-narrow);
|
||||||
|
|
||||||
span {
|
span {
|
||||||
display: block;
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
& div {
|
||||||
grid-column: -1 / -6;
|
grid-column: -1 / -6;
|
||||||
|
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
line-height: var(--leading-paragraph);
|
line-height: var(--leading-paragraph);
|
||||||
|
|
||||||
* + * {
|
> p {
|
||||||
margin-top: 1.25rem;
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +106,103 @@ import { Picture } from "astro:assets";
|
|||||||
max-width: 43ch;
|
max-width: 43ch;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
line-height: var(--leading-paragraph);
|
line-height: var(--leading-paragraph);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import { SplitText } from "gsap/SplitText";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
|
gsap.registerPlugin(SplitText);
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const aboutSection = document.querySelector(".about");
|
||||||
|
|
||||||
|
if (aboutSection) {
|
||||||
|
const tl = gsap.timeline({
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: aboutSection,
|
||||||
|
start: "top 70%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.addLabel("heading", 0);
|
||||||
|
tl.addLabel("heading-text", 0.6);
|
||||||
|
tl.addLabel("image", 1);
|
||||||
|
tl.addLabel("image-text", 1.4);
|
||||||
|
|
||||||
|
new SplitText(aboutSection.querySelector("h2"), {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.chars,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: -120,
|
||||||
|
scale: 1.2,
|
||||||
|
stagger: 0.015,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"heading",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new SplitText(aboutSection.querySelectorAll(".heading p"), {
|
||||||
|
type: "lines, words",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "lines",
|
||||||
|
linesClass: "line",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.lines,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: 105,
|
||||||
|
stagger: 0.06,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"heading-text",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.from(
|
||||||
|
aboutSection.querySelector(".background"),
|
||||||
|
{
|
||||||
|
duration: 2,
|
||||||
|
opacity: 0,
|
||||||
|
ease: "expo.out",
|
||||||
|
},
|
||||||
|
"image",
|
||||||
|
);
|
||||||
|
|
||||||
|
new SplitText(aboutSection.querySelectorAll(".image > p"), {
|
||||||
|
type: "lines, words",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "lines",
|
||||||
|
linesClass: "line",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.lines,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: 105,
|
||||||
|
stagger: 0.06,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"image-text",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -92,3 +92,100 @@ import ArrowUpRight from "../icons/ArrowUpRight.astro";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import { SplitText } from "gsap/SplitText";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
|
gsap.registerPlugin(SplitText);
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const contactSection = document.querySelector(".contact");
|
||||||
|
|
||||||
|
if (contactSection) {
|
||||||
|
const tl = gsap.timeline({
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: contactSection,
|
||||||
|
start: "top 60%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.addLabel("image", 0.2);
|
||||||
|
tl.addLabel("heading", 0.5);
|
||||||
|
tl.addLabel("text", 0.6);
|
||||||
|
|
||||||
|
new SplitText(contactSection.querySelector("h2"), {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.chars,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: -120,
|
||||||
|
scale: 1.2,
|
||||||
|
stagger: 0.015,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"heading",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new SplitText(contactSection.querySelector("p"), {
|
||||||
|
type: "lines, words",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "lines",
|
||||||
|
linesClass: "line",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.lines,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: 105,
|
||||||
|
stagger: 0.06,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"text",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.from(
|
||||||
|
contactSection.querySelector(".image"),
|
||||||
|
{
|
||||||
|
duration: 2,
|
||||||
|
opacity: 0,
|
||||||
|
xPercent: -10,
|
||||||
|
ease: "expo.out",
|
||||||
|
},
|
||||||
|
"image",
|
||||||
|
);
|
||||||
|
|
||||||
|
new SplitText(contactSection.querySelector("a"), {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
gsap.from(self.chars, {
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: contactSection,
|
||||||
|
start: "top 50%",
|
||||||
|
},
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: -120,
|
||||||
|
scale: 1.2,
|
||||||
|
stagger: 0.015,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -125,23 +125,22 @@ import ArrowDown from "../icons/ArrowDown.astro";
|
|||||||
const heroSection = document.querySelector(".hero");
|
const heroSection = document.querySelector(".hero");
|
||||||
|
|
||||||
if (heroSection) {
|
if (heroSection) {
|
||||||
const timeline = gsap.timeline().pause();
|
const tl = gsap.timeline().pause();
|
||||||
|
|
||||||
timeline.from(heroSection.querySelector(".image"), {
|
tl.addLabel("heading", 0);
|
||||||
duration: 2,
|
tl.addLabel("image", 0.2);
|
||||||
opacity: 0,
|
tl.addLabel("text", 0.8);
|
||||||
// yPercent: 20,
|
tl.addLabel("arrow", 1.5);
|
||||||
ease: "expo.out",
|
|
||||||
});
|
|
||||||
|
|
||||||
timeline.from(
|
tl.from(
|
||||||
heroSection.querySelector(".down"),
|
heroSection.querySelector(".image"),
|
||||||
{
|
{
|
||||||
duration: 1,
|
duration: 2,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
xPercent: 10,
|
||||||
ease: "expo.out",
|
ease: "expo.out",
|
||||||
},
|
},
|
||||||
// ">-1",
|
"image",
|
||||||
);
|
);
|
||||||
|
|
||||||
new SplitText(heroSection.querySelector("h1"), {
|
new SplitText(heroSection.querySelector("h1"), {
|
||||||
@@ -150,7 +149,7 @@ import ArrowDown from "../icons/ArrowDown.astro";
|
|||||||
mask: "chars",
|
mask: "chars",
|
||||||
charsClass: "char",
|
charsClass: "char",
|
||||||
onSplit: (self) => {
|
onSplit: (self) => {
|
||||||
timeline.from(
|
tl.from(
|
||||||
self.chars,
|
self.chars,
|
||||||
{
|
{
|
||||||
duration: 1,
|
duration: 1,
|
||||||
@@ -158,8 +157,9 @@ import ArrowDown from "../icons/ArrowDown.astro";
|
|||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
stagger: 0.015,
|
stagger: 0.015,
|
||||||
ease: "expo.out",
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
},
|
},
|
||||||
">-2.8",
|
"heading",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -170,19 +170,29 @@ import ArrowDown from "../icons/ArrowDown.astro";
|
|||||||
mask: "lines",
|
mask: "lines",
|
||||||
linesClass: "line",
|
linesClass: "line",
|
||||||
onSplit: (self) => {
|
onSplit: (self) => {
|
||||||
timeline
|
tl.from(
|
||||||
.from(
|
self.lines,
|
||||||
self.lines,
|
{
|
||||||
{
|
duration: 0.9,
|
||||||
duration: 0.9,
|
yPercent: 105,
|
||||||
yPercent: 105,
|
stagger: 0.06,
|
||||||
stagger: 0.06,
|
ease: "expo.out",
|
||||||
ease: "expo.out",
|
onComplete: () => self.revert(),
|
||||||
},
|
},
|
||||||
">-1",
|
"text",
|
||||||
)
|
).play();
|
||||||
.play();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tl.from(
|
||||||
|
heroSection.querySelector(".down"),
|
||||||
|
{
|
||||||
|
duration: 1,
|
||||||
|
opacity: 0,
|
||||||
|
translateY: "-100%",
|
||||||
|
ease: "bounce.out",
|
||||||
|
},
|
||||||
|
"arrow",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const services: Array<ComponentProps<typeof ServicesCard>> = [
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul class="cards">
|
||||||
{services.map((service) => <ServicesCard {...service} />)}
|
{services.map((service) => <ServicesCard {...service} />)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,7 +86,7 @@ const services: Array<ComponentProps<typeof ServicesCard>> = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
.cards {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
@@ -98,3 +98,80 @@ const services: Array<ComponentProps<typeof ServicesCard>> = [
|
|||||||
margin-top: 7rem;
|
margin-top: 7rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import { SplitText } from "gsap/SplitText";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
|
gsap.registerPlugin(SplitText);
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const servicesSection = document.querySelector(".services");
|
||||||
|
|
||||||
|
if (servicesSection) {
|
||||||
|
const tl = gsap.timeline({
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: servicesSection,
|
||||||
|
start: "top 60%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.addLabel("heading", 0);
|
||||||
|
tl.addLabel("text", 0.3);
|
||||||
|
tl.addLabel("cards", 0.8);
|
||||||
|
|
||||||
|
new SplitText(servicesSection.querySelector("h2"), {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.chars,
|
||||||
|
{
|
||||||
|
duration: 1,
|
||||||
|
yPercent: -120,
|
||||||
|
scale: 1.2,
|
||||||
|
stagger: 0.015,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"heading",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new SplitText(servicesSection.querySelector("p"), {
|
||||||
|
type: "lines, words",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "lines",
|
||||||
|
linesClass: "line",
|
||||||
|
onSplit: (self) => {
|
||||||
|
tl.from(
|
||||||
|
self.lines,
|
||||||
|
{
|
||||||
|
duration: 1.5,
|
||||||
|
yPercent: 105,
|
||||||
|
stagger: 0.06,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"text",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.from(
|
||||||
|
servicesSection.querySelectorAll(".cards > *"),
|
||||||
|
{
|
||||||
|
duration: 1,
|
||||||
|
opacity: 0,
|
||||||
|
yPercent: 5,
|
||||||
|
stagger: 0.15,
|
||||||
|
ease: "expo.out",
|
||||||
|
},
|
||||||
|
"cards",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
const values = [
|
const values = [
|
||||||
{
|
{
|
||||||
title: "Sustainability",
|
title: "Sustainability",
|
||||||
text: "Choosing Tideshift as your digital partner is a sustainable way to build your next project and keep it running. We will build your vision on open tech and support you in gaining digital sovereignty.",
|
text: "Choosing Tideshift as your digital partner is a future-proof way to build your next project and keep it running. We will build your vision on open tech and sustainable infrastructure to maximize control and minimize resource usage.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Collaboration",
|
title: "Collaboration",
|
||||||
@@ -10,7 +10,7 @@ const values = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Privacy",
|
title: "Privacy",
|
||||||
text: "We strongly believe that individuals and businesses should be in control of their data and stay aware of what is happening with their creative work. That's why we focus on using technology and services that protect your data instead of selling it for profit.",
|
text: "We strongly believe that individuals and businesses should be in control of their data and stay aware of what is happening with their creative work. That's why we focus on using technology and infrastructure that protects your data instead of selling it for profit.",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -101,3 +101,114 @@ function formatIndex(index: number) {
|
|||||||
line-height: var(--leading-tightest);
|
line-height: var(--leading-tightest);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import { SplitText } from "gsap/SplitText";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
|
gsap.registerPlugin(SplitText);
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const valuesSection = document.querySelector(".values");
|
||||||
|
|
||||||
|
if (valuesSection) {
|
||||||
|
const heading = valuesSection.querySelector("h2");
|
||||||
|
|
||||||
|
new SplitText(heading, {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
gsap.from(self.chars, {
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: heading,
|
||||||
|
start: "top 70%",
|
||||||
|
},
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: -120,
|
||||||
|
scale: 1.2,
|
||||||
|
stagger: 0.015,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = valuesSection.querySelectorAll("li");
|
||||||
|
|
||||||
|
items.forEach((item) => {
|
||||||
|
const itemTl = gsap.timeline({
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: item,
|
||||||
|
start: "top 70%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
itemTl.addLabel("number", 0);
|
||||||
|
itemTl.addLabel("heading", 0.2);
|
||||||
|
itemTl.addLabel("text", 0.5);
|
||||||
|
|
||||||
|
new SplitText(item.querySelector("span"), {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
itemTl.from(
|
||||||
|
self.chars,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: -120,
|
||||||
|
stagger: 0.25,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"number",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new SplitText(item.querySelector("h3"), {
|
||||||
|
type: "words, chars",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "chars",
|
||||||
|
charsClass: "char",
|
||||||
|
onSplit: (self) => {
|
||||||
|
itemTl.from(
|
||||||
|
self.chars,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: -120,
|
||||||
|
stagger: 0.015,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"heading",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new SplitText(item.querySelector("p"), {
|
||||||
|
type: "lines, words",
|
||||||
|
autoSplit: true,
|
||||||
|
mask: "lines",
|
||||||
|
linesClass: "line",
|
||||||
|
onSplit: (self) => {
|
||||||
|
itemTl.from(
|
||||||
|
self.lines,
|
||||||
|
{
|
||||||
|
duration: 0.9,
|
||||||
|
yPercent: 105,
|
||||||
|
stagger: 0.06,
|
||||||
|
ease: "expo.out",
|
||||||
|
onComplete: () => self.revert(),
|
||||||
|
},
|
||||||
|
"text",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user