Skip to content

Commit f9bce62

Browse files
committed
feat: add DomeGallery component
1 parent b2a63c3 commit f9bce62

File tree

6 files changed

+1244
-1
lines changed

6 files changed

+1244
-1
lines changed

src/constants/Categories.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Highlighted sidebar items
2-
export const NEW = ['Liquid Ether', 'Staggered Menu', 'Pixel Blast', 'Gradual Blur', 'Gradient Blinds', 'Bubble Menu', 'Prism', 'Plasma', 'Electric Border', 'Target Cursor', 'Pill Nav', 'Card Nav', 'Logo Loop', 'Prismatic Burst'];
2+
export const NEW = ['Dome Gallery', 'Liquid Ether', 'Staggered Menu', 'Pixel Blast', 'Gradual Blur', 'Gradient Blinds', 'Bubble Menu', 'Prism', 'Plasma', 'Electric Border', 'Target Cursor', 'Pill Nav', 'Card Nav', 'Logo Loop', 'Prismatic Burst'];
33
export const UPDATED = [];
44

55
// Used for main sidebar navigation
@@ -86,6 +86,7 @@ export const CATEGORIES = [
8686
'Tilted Card',
8787
'Glass Icons',
8888
'Decay Card',
89+
'Dome Gallery',
8990
'Flowing Menu',
9091
'Elastic Slider',
9192
'Stack',

src/constants/Components.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const components = {
7070
'infinite-scroll': () => import('../demo/Components/InfiniteScrollDemo.vue'),
7171
'glass-icons': () => import('../demo/Components/GlassIconsDemo.vue'),
7272
'decay-card': () => import('../demo/Components/DecayCardDemo.vue'),
73+
'dome-gallery': () => import('../demo/Components/DomeGalleryDemo.vue'),
7374
'flowing-menu': () => import('../demo/Components/FlowingMenuDemo.vue'),
7475
'elastic-slider': () => import('../demo/Components/ElasticSliderDemo.vue'),
7576
'tilted-card': () => import('../demo/Components/TiltedCardDemo.vue'),
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import code from '@content/Components/DomeGallery/DomeGallery.vue?raw';
2+
import { createCodeObject } from '../../../types/code';
3+
4+
export const domeGallery = createCodeObject(code, 'Components/DomeGallery', {
5+
installation: `# No external dependencies required`,
6+
usage: `<template>
7+
<DomeGallery
8+
:images="[
9+
'https://images.unsplash.com/photo-1755331039789-7e5680e26e8f?q=80&w=774&auto=format&fit=crop',
10+
'https://images.unsplash.com/photo-1755569309049-98410b94f66d?q=80&w=772&auto=format&fit=crop',
11+
'https://images.unsplash.com/photo-1755497595318-7e5e3523854f?q=80&w=774&auto=format&fit=crop'
12+
]"
13+
:fit="0.8"
14+
fit-basis="auto"
15+
:min-radius="600"
16+
:segments="34"
17+
:drag-sensitivity="20"
18+
:enlarge-transition-ms="300"
19+
:grayscale="true"
20+
overlay-blur-color="#060010"
21+
image-border-radius="30px"
22+
opened-image-width="250px"
23+
opened-image-height="350px"
24+
/>
25+
</template>
26+
27+
<script setup lang="ts">
28+
import DomeGallery from "./DomeGallery.vue";
29+
</script>`
30+
});
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
.sphere-root {
2+
position: relative;
3+
width: 100%;
4+
height: 100%;
5+
--radius: 520px;
6+
--viewer-pad: 72px;
7+
--circ: calc(var(--radius) * 3.14);
8+
--rot-y: calc((360deg / var(--segments-x)) / 2);
9+
--rot-x: calc((360deg / var(--segments-y)) / 2);
10+
--item-width: calc(var(--circ) / var(--segments-x));
11+
--item-height: calc(var(--circ) / var(--segments-y));
12+
}
13+
14+
.sphere-root * {
15+
box-sizing: border-box;
16+
}
17+
18+
.sphere,
19+
.item,
20+
.item__image {
21+
transform-style: preserve-3d;
22+
}
23+
24+
main.sphere-main {
25+
position: absolute;
26+
inset: 0;
27+
display: grid;
28+
place-items: center;
29+
overflow: hidden;
30+
touch-action: none;
31+
user-select: none;
32+
-webkit-user-select: none;
33+
background: transparent;
34+
}
35+
36+
.stage {
37+
width: 100%;
38+
height: 100%;
39+
display: grid;
40+
place-items: center;
41+
perspective: calc(var(--radius) * 2);
42+
perspective-origin: 50% 50%;
43+
contain: layout paint size;
44+
}
45+
46+
.sphere {
47+
transform: translateZ(calc(var(--radius) * -1));
48+
will-change: transform;
49+
}
50+
51+
.overlay,
52+
.overlay--blur {
53+
position: absolute;
54+
inset: 0;
55+
margin: auto;
56+
z-index: 3;
57+
pointer-events: none;
58+
}
59+
60+
.overlay {
61+
background-image: radial-gradient(rgba(235, 235, 235, 0) 65%, var(--overlay-blur-color, #060010) 100%);
62+
}
63+
64+
.overlay--blur {
65+
-webkit-mask-image: radial-gradient(rgba(235, 235, 235, 0) 70%, var(--overlay-blur-color, #060010) 90%);
66+
mask-image: radial-gradient(rgba(235, 235, 235, 0) 70%, var(--overlay-blur-color, #060010) 90%);
67+
backdrop-filter: blur(3px);
68+
}
69+
70+
.item {
71+
width: calc(var(--item-width) * var(--item-size-x));
72+
height: calc(var(--item-height) * var(--item-size-y));
73+
position: absolute;
74+
top: -999px;
75+
bottom: -999px;
76+
left: -999px;
77+
right: -999px;
78+
margin: auto;
79+
transform-origin: 50% 50%;
80+
backface-visibility: hidden;
81+
transition: transform 300ms;
82+
transform: rotateY(calc(var(--rot-y) * (var(--offset-x) + ((var(--item-size-x) - 1) / 2)) + var(--rot-y-delta, 0deg)))
83+
rotateX(calc(var(--rot-x) * (var(--offset-y) - ((var(--item-size-y) - 1) / 2)) + var(--rot-x-delta, 0deg)))
84+
translateZ(var(--radius));
85+
}
86+
87+
.item__image {
88+
position: absolute;
89+
display: block;
90+
inset: 10px;
91+
border-radius: var(--tile-radius, 12px);
92+
background: transparent;
93+
overflow: hidden;
94+
backface-visibility: hidden;
95+
transition: transform 300ms;
96+
cursor: pointer;
97+
-webkit-tap-highlight-color: transparent;
98+
touch-action: manipulation;
99+
pointer-events: auto;
100+
-webkit-transform: translateZ(0);
101+
transform: translateZ(0);
102+
}
103+
104+
.item__image:focus {
105+
outline: none;
106+
}
107+
108+
.item__image img {
109+
width: 100%;
110+
height: 100%;
111+
object-fit: cover;
112+
pointer-events: none;
113+
backface-visibility: hidden;
114+
filter: var(--image-filter, none);
115+
}
116+
117+
.viewer {
118+
position: absolute;
119+
inset: 0;
120+
z-index: 20;
121+
pointer-events: none;
122+
display: flex;
123+
align-items: center;
124+
justify-content: center;
125+
padding: var(--viewer-pad);
126+
}
127+
128+
.viewer .frame {
129+
height: 100%;
130+
aspect-ratio: 1;
131+
border-radius: var(--enlarge-radius, 32px);
132+
display: flex;
133+
}
134+
135+
@media (max-aspect-ratio: 1/1) {
136+
.viewer .frame {
137+
height: auto;
138+
width: 100%;
139+
}
140+
}
141+
142+
.viewer .scrim {
143+
position: absolute;
144+
inset: 0;
145+
z-index: 10;
146+
background: rgba(0, 0, 0, 0.4);
147+
pointer-events: none;
148+
opacity: 0;
149+
transition: opacity 500ms ease;
150+
backdrop-filter: blur(3px);
151+
}
152+
153+
.sphere-root[data-enlarging='true'] .viewer .scrim {
154+
opacity: 1;
155+
pointer-events: all;
156+
}
157+
158+
.viewer .enlarge {
159+
position: absolute;
160+
z-index: 30;
161+
border-radius: var(--enlarge-radius, 32px);
162+
overflow: hidden;
163+
transition:
164+
transform 500ms ease,
165+
opacity 500ms ease;
166+
transform-origin: top left;
167+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
168+
}
169+
170+
.viewer .enlarge img {
171+
width: 100%;
172+
height: 100%;
173+
object-fit: cover;
174+
filter: var(--image-filter, none);
175+
}
176+
177+
.sphere-root .enlarge-closing img {
178+
filter: var(--image-filter, none);
179+
}
180+
181+
.edge-fade {
182+
position: absolute;
183+
left: 0;
184+
right: 0;
185+
height: 120px;
186+
z-index: 5;
187+
pointer-events: none;
188+
background: linear-gradient(to bottom, transparent, var(--overlay-blur-color, #060010));
189+
}
190+
191+
.edge-fade--top {
192+
top: 0;
193+
transform: rotate(180deg);
194+
}
195+
196+
.edge-fade--bottom {
197+
bottom: 0;
198+
}
199+
200+
/* Scroll lock utility class */
201+
body.dg-scroll-lock {
202+
overflow: hidden;
203+
}

0 commit comments

Comments
 (0)