mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-11 04:22:19 +00:00
phone cmp
This commit is contained in:
@@ -97,15 +97,15 @@
|
||||
id="open-source"
|
||||
use:scroll
|
||||
on:aw-scroll={({ detail }) => {
|
||||
const { scrollPercentage } = detail;
|
||||
scrollHandler(scrollPercentage);
|
||||
const { percentage } = detail;
|
||||
scrollHandler(percentage);
|
||||
}}
|
||||
on:aw-resize={({ detail }) => {
|
||||
scrollHandler.reset();
|
||||
const { scrollPercentage } = detail;
|
||||
console.log('resize', scrollPercentage);
|
||||
const { percentage } = detail;
|
||||
console.log('resize', percentage);
|
||||
|
||||
scrollHandler(scrollPercentage);
|
||||
scrollHandler(percentage);
|
||||
}}
|
||||
>
|
||||
<div class="sticky-wrapper">
|
||||
|
||||
197
src/lib/animations/Products.svelte
Normal file
197
src/lib/animations/Products.svelte
Normal file
@@ -0,0 +1,197 @@
|
||||
<script lang="ts">
|
||||
import { toScale, type Scale } from '$lib/utils/toScale';
|
||||
|
||||
import { spring, type AnimationListOptions, type SpringOptions } from 'motion';
|
||||
import { animation, createScrollHandler, scroll, type Animation, type ScrollInfo } from '.';
|
||||
import { fly, slide } from 'svelte/transition';
|
||||
import { Phone } from '$lib/components';
|
||||
|
||||
const springOptions: SpringOptions = { stiffness: 58.78, mass: 1, damping: 17.14 };
|
||||
const animationOptions: AnimationListOptions = {
|
||||
x: { easing: spring(springOptions) },
|
||||
y: { easing: spring(springOptions) }
|
||||
};
|
||||
const animations: {
|
||||
mobile: {
|
||||
main: Animation;
|
||||
reversed: Animation;
|
||||
};
|
||||
desktop: {
|
||||
main: Animation;
|
||||
reversed: Animation;
|
||||
};
|
||||
}[] = [
|
||||
// {
|
||||
// mobile: {
|
||||
// main: animation('#oss-discord', { x: 0, y: 0, rotate: 1 }, animationOptions),
|
||||
// reversed: animation('#oss-discord', { x: -1200, y: 0, rotate: 1 }, animationOptions)
|
||||
// },
|
||||
// desktop: {
|
||||
// main: animation('#oss-discord', { x: 20, y: '-80vh', rotate: 15 }, animationOptions),
|
||||
// reversed: animation('#oss-discord', { x: -100, y: '0vh', rotate: 15 }, animationOptions)
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// mobile: {
|
||||
// main: animation('#oss-github', { x: 0, y: -10, rotate: -2 }, animationOptions),
|
||||
// reversed: animation('#oss-github', { x: -1200, y: 10, rotate: -2 }, animationOptions)
|
||||
// },
|
||||
// desktop: {
|
||||
// main: animation('#oss-github', { x: -100, y: '-55vh', rotate: 6.26 }, animationOptions),
|
||||
// reversed: animation('#oss-github', { x: 0, y: '0vh', rotate: 6.26 }, animationOptions)
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// mobile: {
|
||||
// main: animation('#oss-twitter', { x: 0, y: 10, rotate: -3 }, animationOptions),
|
||||
// reversed: animation('#oss-twitter', { x: -1200, y: -10, rotate: -3 }, animationOptions)
|
||||
// },
|
||||
// desktop: {
|
||||
// main: animation('#oss-twitter', { x: 100, y: '-70vh', rotate: -15 }, animationOptions),
|
||||
// reversed: animation('#oss-twitter', { x: 0, y: '0vh', rotate: -15 }, animationOptions)
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// mobile: {
|
||||
// main: animation('#oss-youtube', { x: 0, y: 5, rotate: 2 }, animationOptions),
|
||||
// reversed: animation('#oss-youtube', { x: -1200, y: -5, rotate: 2 }, animationOptions)
|
||||
// },
|
||||
// desktop: {
|
||||
// main: animation('#oss-youtube', { x: -100, y: '-55vh', rotate: -3.77 }, animationOptions),
|
||||
// reversed: animation('#oss-youtube', { x: 0, y: '0vh', rotate: -3.77 }, animationOptions)
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// mobile: {
|
||||
// main: animation('#oss-commits', { x: 0, y: -4, rotate: -1 }, animationOptions),
|
||||
// reversed: animation('#oss-commits', { x: -1200, y: 4, rotate: -1 }, animationOptions)
|
||||
// },
|
||||
// desktop: {
|
||||
// main: animation('#oss-commits', { x: 100, y: '-80vh', rotate: -10.2 }, animationOptions),
|
||||
// reversed: animation('#oss-commits', { x: 0, y: '0vh', rotate: -10.2 }, animationOptions)
|
||||
// }
|
||||
// }
|
||||
];
|
||||
|
||||
const animScale: Scale = [0, animations.length - 1];
|
||||
const percentScale: Scale = [0.1, 0.8];
|
||||
|
||||
const scrollHandler = createScrollHandler(
|
||||
animations.map(({ mobile, desktop }, i) => {
|
||||
return {
|
||||
percentage: toScale(i, animScale, percentScale),
|
||||
whenAfter() {
|
||||
const { main, reversed } = isMobile() ? mobile : desktop;
|
||||
|
||||
main.play();
|
||||
return reversed.play;
|
||||
}
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const isMobile = () => {
|
||||
return window.innerWidth < 1024;
|
||||
};
|
||||
|
||||
let scrollInfo: ScrollInfo = {
|
||||
percentage: 0,
|
||||
traversed: 0,
|
||||
remaning: Infinity
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="products"
|
||||
use:scroll
|
||||
on:aw-scroll={({ detail }) => {
|
||||
const { percentage } = detail;
|
||||
scrollInfo = detail;
|
||||
console.log(detail);
|
||||
scrollHandler(percentage);
|
||||
}}
|
||||
on:aw-resize={({ detail }) => {
|
||||
scrollHandler.reset();
|
||||
const { percentage: scrollPercentage } = detail;
|
||||
console.log('resize', scrollPercentage);
|
||||
|
||||
scrollHandler(scrollPercentage);
|
||||
}}
|
||||
>
|
||||
<div class="sticky-wrapper">
|
||||
<div class="text">
|
||||
{#if scrollInfo.traversed > 0}
|
||||
<span class="aw-badges aw-eyebrow" transition:slide={{ axis: 'x' }}>Products_</span>
|
||||
{/if}
|
||||
{#if scrollInfo.traversed > 600}
|
||||
<h2 class="aw-display aw-u-color-text-primary" transition:slide={{ axis: 'x' }}>
|
||||
Your backend, minus the hassle
|
||||
</h2>
|
||||
{/if}
|
||||
{#if scrollInfo.traversed > 1500}
|
||||
<p
|
||||
class="aw-description aw-u-max-width-700 u-margin-inline-auto"
|
||||
transition:fly={{
|
||||
y: 16
|
||||
}}
|
||||
>
|
||||
Build secure and scalable applications faster with Appwrite's core backend products and
|
||||
spend more time building the best products.
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<Phone />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#products {
|
||||
height: 10000px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sticky-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
position: sticky;
|
||||
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
padding-inline: 1.25rem;
|
||||
--navbar-height: 8rem;
|
||||
--padding-block: 7.5rem;
|
||||
padding-block: calc(var(--navbar-height) + var(--padding-block)) var(--padding-block);
|
||||
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
|
||||
text-align: center;
|
||||
|
||||
.text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
white-space: nowrap;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 1.25rem;
|
||||
max-width: 48.875rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
h2 {
|
||||
max-width: 61.375rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -84,39 +84,40 @@ export function createScrollHandler(callbacks: ScrollCallback[]) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
type ScrollDetail = {
|
||||
scrollPercentage: number;
|
||||
scrollY: number;
|
||||
scrollHeight: number;
|
||||
top: number;
|
||||
export type ScrollInfo = {
|
||||
percentage: number;
|
||||
traversed: number;
|
||||
remaning: number;
|
||||
};
|
||||
|
||||
export const scroll: Action<
|
||||
HTMLElement,
|
||||
undefined,
|
||||
{
|
||||
'on:aw-scroll': (e: CustomEvent<ScrollDetail>) => void;
|
||||
'on:aw-resize': (e: CustomEvent<ScrollDetail>) => void;
|
||||
'on:aw-scroll': (e: CustomEvent<ScrollInfo>) => void;
|
||||
'on:aw-resize': (e: CustomEvent<ScrollInfo>) => void;
|
||||
}
|
||||
> = (node) => {
|
||||
const getScrollInfo = () => {
|
||||
function getScrollInfo(): ScrollInfo {
|
||||
const { top, height } = node.getBoundingClientRect();
|
||||
const { innerHeight, scrollY } = window;
|
||||
const { innerHeight } = window;
|
||||
|
||||
const scrollHeight = height - innerHeight;
|
||||
const scrollPercentage = (-1 * top) / scrollHeight;
|
||||
|
||||
const traversed = scrollPercentage * scrollHeight;
|
||||
const remaning = scrollHeight - traversed;
|
||||
|
||||
return {
|
||||
scrollPercentage,
|
||||
scrollY,
|
||||
scrollHeight,
|
||||
top
|
||||
};
|
||||
percentage: scrollPercentage,
|
||||
traversed,
|
||||
remaning
|
||||
};
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
node.dispatchEvent(
|
||||
new CustomEvent<ScrollDetail>('aw-scroll', {
|
||||
new CustomEvent<ScrollInfo>('aw-scroll', {
|
||||
detail: getScrollInfo()
|
||||
})
|
||||
);
|
||||
@@ -124,7 +125,7 @@ export const scroll: Action<
|
||||
|
||||
const handleResize = () => {
|
||||
node.dispatchEvent(
|
||||
new CustomEvent<ScrollDetail>('aw-resize', {
|
||||
new CustomEvent<ScrollInfo>('aw-resize', {
|
||||
detail: getScrollInfo()
|
||||
})
|
||||
);
|
||||
|
||||
51
src/lib/components/Phone.svelte
Normal file
51
src/lib/components/Phone.svelte
Normal file
@@ -0,0 +1,51 @@
|
||||
<div class="phone">
|
||||
<div class="inner">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.phone {
|
||||
@include border-gradient;
|
||||
--m-border-size: 1px;
|
||||
--m-border-radius: 2.5rem;
|
||||
--m-border-gradient-after: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.12) 0%,
|
||||
rgba(255, 255, 255, 0) 125.11%
|
||||
);
|
||||
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 0.5rem;
|
||||
|
||||
width: 275px;
|
||||
// height: 550px;
|
||||
flex-shrink: 0;
|
||||
|
||||
aspect-ratio: 1 / 2;
|
||||
}
|
||||
|
||||
.inner {
|
||||
background-color: white;
|
||||
border-radius: 2rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 0.5rem;
|
||||
|
||||
border-radius: 100rem;
|
||||
background: var(--label-color-light-primary, #000);
|
||||
|
||||
width: 6.25rem;
|
||||
height: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,3 +2,4 @@ export { default as FooterNav } from './FooterNav.svelte';
|
||||
export { default as MainFooter } from './MainFooter.svelte';
|
||||
export { default as PreFooter } from './PreFooter.svelte';
|
||||
export { default as MobileNav } from './MobileNav.svelte';
|
||||
export { default as Phone } from './Phone.svelte';
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Tabs } from '$lib/UI';
|
||||
import PreFooter from '$lib/components/PreFooter.svelte';
|
||||
import OpenSource from '$lib/animations/OpenSource.svelte';
|
||||
import Products from '$lib/animations/Products.svelte';
|
||||
</script>
|
||||
|
||||
<!-- <div
|
||||
@@ -133,20 +134,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aw-big-padding-section-level-1">
|
||||
<div class="aw-big-padding-section-level-2">
|
||||
<div class="aw-container">
|
||||
<div class="aw-hero aw-u-max-width-993">
|
||||
<span class="aw-badges aw-eyebrow">Products_</span>
|
||||
<h2 class="aw-display aw-u-color-text-primary">Your backend, minus the hassle</h2>
|
||||
<p class="aw-description aw-u-max-width-700 u-margin-inline-auto">
|
||||
Build secure and scalable applications faster with Appwrite’s core backend products
|
||||
and spend more time building the best products.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Products />
|
||||
|
||||
<div class="aw-big-padding-section-level-1 u-position-relative aw-white-section theme-light">
|
||||
<div class="u-position-absolute u-inset-block-end-0 u-inset-inline-start u-width-full-line">
|
||||
|
||||
@@ -52,59 +52,46 @@
|
||||
color:hsl(var(--aw-color-greyscale-50));
|
||||
}
|
||||
|
||||
&::before {
|
||||
--m-border-gradient: linear-gradient(
|
||||
|
||||
--m-border-gradient-before: linear-gradient(
|
||||
to bottom,
|
||||
rgba(253, 54, 110, 0.48) 0%,
|
||||
rgba(253, 54, 110, 0) 180%
|
||||
);
|
||||
}
|
||||
|
||||
&::after {
|
||||
--m-border-gradient: radial-gradient(
|
||||
|
||||
|
||||
--m-border-gradient-after: radial-gradient(
|
||||
42.86% 42.86% at 50.55% -0%,
|
||||
rgba(255, 255, 255, 0.2) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
&:where(:hover) {
|
||||
background: rgba(253, 54, 110, 0.10);
|
||||
box-shadow: 0px -6px 10px 0px rgba(253, 54, 110, 0.08) inset;
|
||||
|
||||
|
||||
&::before {
|
||||
--m-border-gradient: linear-gradient(
|
||||
--m-border-gradient-before: linear-gradient(
|
||||
180deg, rgba(253, 54, 110, 0.64) 0%,
|
||||
rgba(253, 54, 110, 0.00) 163.1%
|
||||
);
|
||||
}
|
||||
|
||||
&::after {
|
||||
--m-border-gradient: radial-gradient(
|
||||
--m-border-gradient-after: radial-gradient(
|
||||
42.86% 42.86% at 50.55% 0%,
|
||||
rgba(255, 255, 255, 0.20) 0%,
|
||||
rgba(255, 255, 255, 0.00) 100%
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:where(:active) {
|
||||
background: rgba(253, 54, 110, 0.16);
|
||||
box-shadow: 0px -6px 10px 0px rgba(253, 54, 110, 0.08) inset;
|
||||
|
||||
&::before {
|
||||
--m-border-gradient:
|
||||
--m-border-gradient-before:
|
||||
linear-gradient(180deg, rgba(253, 54, 110, 0.64) 0%, rgba(253, 54, 110, 0.00) 163.1%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
--m-border-gradient:
|
||||
--m-border-gradient-after:
|
||||
radial-gradient(42.86% 42.86% at 50.55% 0%, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:where(:focus-visible) {
|
||||
background: rgba(253, 54, 110, 0.04);
|
||||
box-shadow: 0px 0px 0px 4px rgba(253, 54, 110, 0.16), 0px -6px 10px 0px rgba(253, 54, 110, 0.08) inset;
|
||||
@@ -120,15 +107,15 @@
|
||||
color:hsl(var(--aw-color-pink-500));
|
||||
}
|
||||
|
||||
&::before {
|
||||
--m-border-gradient:
|
||||
linear-gradient(180deg, rgba(253, 54, 110, 0.19) 0%, rgba(253, 54, 110, 0.48) 100%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
--m-border-gradient:
|
||||
--m-border-gradient-before:
|
||||
linear-gradient(180deg, rgba(253, 54, 110, 0.19) 0%, rgba(253, 54, 110, 0.48) 100%);
|
||||
|
||||
|
||||
|
||||
--m-border-gradient-after:
|
||||
radial-gradient(42.86% 42.86% at 50.55% 0%, rgba(255, 255, 255, 0.20) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||
}
|
||||
|
||||
|
||||
&:where(:hover) {
|
||||
background: rgba(253, 54, 110, 0.10);
|
||||
|
||||
@@ -56,18 +56,11 @@
|
||||
--m-border-radius: var(--p-card-border-radius);
|
||||
--m-border-size: #{pxToRem(1)};
|
||||
border: none;
|
||||
|
||||
&::before {
|
||||
--m-border-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||
}
|
||||
--m-border-gradient-before: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||
|
||||
&.is-transparent-pink {
|
||||
&::before {
|
||||
--m-border-gradient: linear-gradient(180deg, rgba(253, 54, 110, 0.48) 0%, rgba(253, 54, 110, 0.00) 100%);
|
||||
}
|
||||
&::after {
|
||||
--m-border-gradient: radial-gradient(42.86% 42.86% at 50.55% 0%, rgba(255, 255, 255, 0.20) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||
}
|
||||
--m-border-gradient-before: linear-gradient(180deg, rgba(253, 54, 110, 0.48) 0%, rgba(253, 54, 110, 0.00) 100%);
|
||||
--m-border-gradient-after: radial-gradient(42.86% 42.86% at 50.55% 0%, rgba(255, 255, 255, 0.20) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,26 @@
|
||||
@mixin border-gradient {
|
||||
position: relative;
|
||||
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
@mixin border-gradient-inner {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: var(--m-border-radius);
|
||||
border: var(--m-border-size) solid transparent;
|
||||
background: var(--m-border-gradient) border-box;
|
||||
border: var(--m-border-size, 1px) solid transparent;
|
||||
-webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: destination-out;
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@mixin border-gradient {
|
||||
position: relative;
|
||||
border-radius: var(--m-border-radius);
|
||||
|
||||
&::before {
|
||||
@include border-gradient-inner;
|
||||
background: var(--m-border-gradient-before) border-box;
|
||||
}
|
||||
|
||||
&::after {
|
||||
@include border-gradient-inner;
|
||||
background: var(--m-border-gradient-after) border-box;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,13 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@use '$scss/abstract' as *;`
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [sveltekit()],
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
|
||||
Reference in New Issue
Block a user