Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 3c2c1ec

Browse files
Hero section (#3)
1 parent 0c7811e commit 3c2c1ec

12 files changed

+369
-25
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"format": "prettier --plugin-search-dir . --write ."
1515
},
1616
"devDependencies": {
17+
"@babeard/svelte-heroicons": "2.0.0-rc.0",
1718
"@rgossiaux/svelte-headlessui": "^1.0.2",
18-
"@rgossiaux/svelte-heroicons": "^0.1.2",
1919
"@sveltejs/adapter-static": "^2.0.2",
2020
"@sveltejs/kit": "^1.15.9",
2121
"@types/node": "^18.16.3",
@@ -32,6 +32,7 @@
3232
"svelte": "^3.58.0",
3333
"svelte-check": "^3.2.0",
3434
"tailwindcss": "^3.3.2",
35+
"tailwindcss-3d": "^0.2.2",
3536
"tslib": "^2.5.0",
3637
"typescript": "^5.0.4",
3738
"vite": "^4.3.3"

pnpm-lock.yaml

+27-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app.html

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
<meta charset="utf-8" />
55
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
66
<meta name="viewport" content="width=device-width" />
7-
<title>Loading...</title>
87
%sveltekit.head%
98
</head>
10-
<body data-sveltekit-preload-data="hover" class="bg-primary text-primary">
9+
<body data-sveltekit-preload-data="hover">
1110
<div style="display: contents">%sveltekit.body%</div>
1211
</body>
1312
</html>
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<!-- Created from https://github.com/andrewwoan/magnetic-button-effect-tutorial-01 -->
2+
<script lang="ts">
3+
import { createEventDispatcher, onMount } from "svelte";
4+
import { useId } from "$lib/ts/id";
5+
6+
// Constants
7+
const elementId = `svelte-magnetic-element-${useId()}`;
8+
const dispatch = createEventDispatcher();
9+
10+
// Configuration
11+
export let triggerArea = 200;
12+
export let interpolationFactor = 0.8;
13+
14+
// Magnetic Object
15+
class MagneticObject {
16+
mousePosition: { x: number; y: number };
17+
domElement: HTMLElement;
18+
boundingClientRect: DOMRect;
19+
inArea: boolean;
20+
lerpingData: {
21+
x: { current: number; target: number };
22+
y: { current: number; target: number };
23+
};
24+
25+
constructor(domElement: HTMLElement) {
26+
this.domElement = domElement;
27+
this.boundingClientRect = this.domElement.getBoundingClientRect();
28+
29+
this.mousePosition = { x: 0, y: 0 };
30+
this.inArea = false;
31+
32+
this.lerpingData = {
33+
x: { current: 0, target: 0 },
34+
y: { current: 0, target: 0 }
35+
};
36+
37+
this.render();
38+
window.addEventListener("resize", () => {
39+
this.boundingClientRect = this.domElement.getBoundingClientRect();
40+
});
41+
document.addEventListener("mousemove", (e) => {
42+
this.mousePosition.x = e.pageX;
43+
this.mousePosition.y = e.pageY;
44+
this.render();
45+
});
46+
}
47+
48+
render() {
49+
const distanceFromMouseToCenter = this.calculateDistance(
50+
this.mousePosition.x,
51+
this.mousePosition.y,
52+
this.boundingClientRect.left + this.boundingClientRect.width / 2,
53+
this.boundingClientRect.top + this.boundingClientRect.height / 2
54+
);
55+
56+
let targetHolder = { x: 0, y: 0 };
57+
58+
if (distanceFromMouseToCenter < triggerArea) {
59+
if (!this.inArea) {
60+
this.inArea = true;
61+
this.domElement.classList.add("in-zone");
62+
dispatch("in_zone", { element: this.domElement });
63+
}
64+
targetHolder.x =
65+
(this.mousePosition.x -
66+
(this.boundingClientRect.left + this.boundingClientRect.width / 2)) *
67+
0.2;
68+
targetHolder.y =
69+
(this.mousePosition.y -
70+
(this.boundingClientRect.top + this.boundingClientRect.height / 2)) *
71+
0.2;
72+
} else if (this.inArea) {
73+
this.inArea = false;
74+
this.domElement.classList.remove("in-zone");
75+
dispatch("out_zone", { element: this.domElement });
76+
}
77+
this.lerpingData["x"].target = targetHolder.x;
78+
this.lerpingData["y"].target = targetHolder.y;
79+
80+
for (const item of Object.values(this.lerpingData)) {
81+
const lerp = this.lerp(item.current, item.target, interpolationFactor);
82+
item.current = Math.abs(lerp) < 0.1 ? 0 : lerp;
83+
}
84+
85+
this.domElement.style.transform = `translate(${this.lerpingData["x"].current}px, ${this.lerpingData["y"].current}px)`;
86+
}
87+
88+
private calculateDistance(x1: number, y1: number, x2: number, y2: number) {
89+
return Math.hypot(x1 - x2, y1 - y2);
90+
}
91+
92+
private lerp(current: number, target: number, factor: number) {
93+
return current * (1 - factor) + target * factor;
94+
}
95+
}
96+
97+
onMount(() => {
98+
const button = document.querySelector(`.${elementId} > *`);
99+
new MagneticObject(<HTMLElement>button);
100+
});
101+
</script>
102+
103+
<div class="{elementId} {$$props.class}">
104+
<slot />
105+
</div>
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<!-- Created from https://codepen.io/kevinpowell/pen/GRBdLEv -->
2+
<script lang="ts">
3+
import { onMount } from "svelte";
4+
import { useId } from "$lib/ts/id";
5+
6+
const elementId = `svelte-mouse-tilt-${useId()}`;
7+
let firstFire = true;
8+
let isWorking = false;
9+
10+
// Svelte Props
11+
export let initialX = 0;
12+
export let initialY = 0;
13+
export let intensity = 0.5;
14+
15+
// Rotate Element
16+
async function rotateElement(event: MouseEvent, element: HTMLElement) {
17+
if (isWorking) return;
18+
function setProperties() {
19+
element.style.setProperty("--rotateX", offsetX + "deg");
20+
element.style.setProperty("--rotateY", -1 * offsetY + "deg");
21+
}
22+
23+
// Get mouse position
24+
const x = event.clientX;
25+
const y = event.clientY;
26+
27+
// Find the middle
28+
const middleX = window.innerWidth / 2;
29+
const middleY = window.innerHeight / 2;
30+
31+
// Get offset from middle as a percentage
32+
// and tone it down a little
33+
const offsetX = ((x - middleX) / middleX) * (intensity * 100);
34+
const offsetY = ((y - middleY) / middleY) * (intensity * 100);
35+
36+
// Set rotation
37+
if (firstFire) {
38+
setProperties();
39+
isWorking = true;
40+
// Caveat: Doesn't solve the problem if you move too much during the x ms
41+
await new Promise<void>((resolve) =>
42+
setTimeout(() => {
43+
element.classList.remove("transition-transform", "duration-500");
44+
resolve();
45+
}, 500)
46+
);
47+
isWorking = false;
48+
firstFire = false;
49+
} else {
50+
setProperties();
51+
}
52+
}
53+
54+
onMount(() => {
55+
const element = document.querySelector(`.${elementId}`);
56+
const listener = (e: MouseEvent) => rotateElement(e, <HTMLElement>element);
57+
document.addEventListener("mousemove", listener);
58+
59+
return () => {
60+
document.removeEventListener("mousemove", listener);
61+
};
62+
});
63+
</script>
64+
65+
<div
66+
class="{elementId} transition-transform duration-500 {$$props.class}"
67+
style="
68+
--rotateX: 0deg;
69+
--rotateY: 0deg;
70+
transform: perspective(312rem)
71+
rotateX(calc({initialX}deg + var(--rotateY)))
72+
rotateY(calc({initialY}deg + var(--rotateX)));
73+
transform-style: preserve-3d;
74+
"
75+
>
76+
<slot />
77+
</div>

src/lib/components/Section.svelte

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<section id={$$props.id} class="child:mx-20 py-10">
2+
{#if $$slots.title}
3+
<h2 class="drop-shadow-lg pb-10 !ml-24 text-4xl">
4+
<slot name="title" />
5+
</h2>
6+
{/if}
7+
<slot />
8+
</section>

src/lib/components/SlideOver.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { XIcon } from "@rgossiaux/svelte-heroicons/outline";
2+
import { XMarkIcon } from "@babeard/svelte-heroicons/outline";
33
import {
44
Dialog,
55
DialogOverlay,
@@ -51,7 +51,7 @@
5151
<div class="ml-3 flex h-7 items-center">
5252
<button type="button" class="-m-2 p-2 hover:opacity-75" on:click={close}>
5353
<span class="sr-only">Close panel</span>
54-
<XIcon class="h-6 w-6" aria-hidden="true" />
54+
<XMarkIcon class="h-6 w-6" aria-hidden="true" />
5555
</button>
5656
</div>
5757
</div>

src/lib/ts/id.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// From https://github.com/rgossiaux/svelte-headlessui/blob/master/src/lib/hooks/use-id.ts
2+
let id = 0;
3+
4+
function generateId() {
5+
return ++id;
6+
}
7+
8+
export function useId() {
9+
return generateId();
10+
}

src/lib/ui/Button.svelte

+15-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@
44

55
{#if type === "primary"}
66
<button
7-
class="font-medium text-lg text-inverted border-[1px] border-transparent bg-dominant rounded-xl py-1 px-3 transition-colors duration-300 hover:bg-inherit hover:border-dominant hover:text-dominant"
7+
class="font-medium text-lg text-inverted
8+
bg-dominant
9+
border-transparent border-[1px]
10+
py-1 px-3
11+
inline-flex items-center gap-2
12+
rounded-xl transition-colors duration-300
13+
hover:bg-inherit hover:border-dominant hover:text-dominant
14+
{$$props.class}"
815
>
916
<slot />
1017
</button>
1118
{:else if type === "secondary"}
1219
<button
13-
class="font-medium text-lg border-dominant border-[1px] text-dominant rounded-xl py-1 px-3 transition-colors duration-300 hover:border-primary hover:text-primary"
20+
class="font-medium text-lg text-dominant
21+
border-dominant border-[1px]
22+
py-1 px-3
23+
inline-flex items-center gap-2
24+
rounded-xl transition-colors duration-300
25+
hover:border-primary hover:text-primary
26+
{$$props.class}"
1427
>
1528
<slot />
1629
</button>

0 commit comments

Comments
 (0)