1
+ /*--================================================
2
+ / Name: Zuid - Creative Portfolio
3
+ / Developer: JohnDev19
4
+ / GitHub: https://github.com/JohnDev19/
5
+ / License: MIT
6
+ / Last Update: December 2024
7
+ =====================================================
8
+ / Description:
9
+ / Zuid is a creative portfolio website designed to showcase the work of a digital designer and developer
10
+ =====================================================
11
+ / Technologies Used:
12
+ / - HTML5
13
+ / - CSS3
14
+ / - JavaScript
15
+ / - AOS (Animate On Scroll)
16
+ / - Swiper.js for sliders
17
+ / - Font Awesome for icons
18
+ / - Google Fonts for typography
19
+ =================================================== */
20
+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
21
+ const hamburger = document . querySelector ( '.hamburger' ) ;
22
+ const navMenu = document . querySelector ( '.nav-menu' ) ;
23
+ const navbar = document . querySelector ( '.navbar' ) ;
24
+
25
+ hamburger . addEventListener ( 'click' , ( ) => {
26
+ hamburger . classList . toggle ( 'active' ) ;
27
+ navMenu . classList . toggle ( 'active' ) ;
28
+
29
+ if ( navMenu . classList . contains ( 'active' ) ) {
30
+ document . body . style . overflow = 'hidden' ;
31
+ } else {
32
+ document . body . style . overflow = 'auto' ;
33
+ }
34
+ } ) ;
35
+
36
+ navMenu . querySelectorAll ( 'a' ) . forEach ( link => {
37
+ link . addEventListener ( 'click' , ( ) => {
38
+ hamburger . classList . remove ( 'active' ) ;
39
+ navMenu . classList . remove ( 'active' ) ;
40
+ document . body . style . overflow = 'auto' ;
41
+ } ) ;
42
+ } ) ;
43
+
44
+ window . addEventListener ( 'scroll' ,
45
+ ( ) => {
46
+ if ( window . scrollY > 100 ) {
47
+ navbar . classList . add ( 'scrolled' ) ;
48
+ } else {
49
+ navbar . classList . remove ( 'scrolled' ) ;
50
+ }
51
+ } ) ;
52
+
53
+ document . querySelectorAll ( 'a[href^="#"]' ) . forEach ( anchor => {
54
+ anchor . addEventListener ( 'click' , function ( e ) {
55
+ e . preventDefault ( ) ;
56
+
57
+ const targetId = this . getAttribute ( 'href' ) ;
58
+ const targetElement = document . querySelector ( targetId ) ;
59
+
60
+ if ( targetElement ) {
61
+ targetElement . scrollIntoView ( {
62
+ behavior : 'smooth' ,
63
+ block : 'start'
64
+ } ) ;
65
+ }
66
+ } ) ;
67
+ } ) ;
68
+
69
+ const observerOptions = {
70
+ root : null ,
71
+ rootMargin : '0px' ,
72
+ threshold : 0.1
73
+ } ;
74
+
75
+ const fadeInObserver = new IntersectionObserver ( ( entries ) => {
76
+ entries . forEach ( entry => {
77
+ if ( entry . isIntersecting ) {
78
+ entry . target . classList . add ( 'fade-in' ) ;
79
+ }
80
+ } ) ;
81
+ } , observerOptions ) ;
82
+
83
+ document . querySelectorAll ( '.section' ) . forEach ( section => {
84
+ fadeInObserver . observe ( section ) ;
85
+ } ) ;
86
+
87
+ const subscribeForm = document . querySelector ( '.subscribe-form' ) ;
88
+ if ( subscribeForm ) {
89
+ subscribeForm . addEventListener ( 'submit' , ( e ) => {
90
+ e . preventDefault ( ) ;
91
+ const emailInput = subscribeForm . querySelector ( 'input[type="email"]' ) ;
92
+
93
+ if ( emailInput . value && emailInput . value . includes ( '@' ) ) {
94
+ alert ( 'Thank you for subscribing!' ) ;
95
+ emailInput . value = '' ;
96
+ } else {
97
+ alert ( 'Please enter a valid email address.' ) ;
98
+ }
99
+ } ) ;
100
+ }
101
+ } ) ;
102
+
103
+ const styleSheet = document . createElement ( 'style' ) ;
104
+ styleSheet . textContent = `
105
+ .section {
106
+ opacity: 0;
107
+ transform: translateY(20px);
108
+ transition: opacity 0.6s ease-out, transform 0.6s ease-out;
109
+ }
110
+ .section.fade-in {
111
+ opacity: 1;
112
+ transform: translateY(0);
113
+ }
114
+ ` ;
115
+ document . head . appendChild ( styleSheet ) ;
116
+
117
+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
118
+ const filterBtns = document . querySelectorAll ( '.filter-btn' ) ;
119
+ const portfolioItems = document . querySelectorAll ( '.portfolio-item' ) ;
120
+
121
+ filterBtns . forEach ( btn => {
122
+ btn . addEventListener ( 'click' , ( ) => {
123
+
124
+ filterBtns . forEach ( b => b . classList . remove ( 'active' ) ) ;
125
+ btn . classList . add ( 'active' ) ;
126
+
127
+ const filter = btn . getAttribute ( 'data-filter' ) ;
128
+
129
+ portfolioItems . forEach ( item => {
130
+ const category = item . getAttribute ( 'data-category' ) ;
131
+
132
+ if ( filter === 'all' || category === filter ) {
133
+ item . style . display = 'block' ;
134
+ item . style . opacity = 0 ;
135
+ item . style . transform = 'scale(0.9)' ;
136
+ setTimeout ( ( ) => {
137
+ item . style . opacity = 1 ;
138
+ item . style . transform = 'scale(1)' ;
139
+ } , 50 ) ;
140
+ } else {
141
+ item . style . display = 'none' ;
142
+ }
143
+ } ) ;
144
+ } ) ;
145
+ } ) ;
146
+ } ) ;
147
+
148
+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
149
+ const scrollArrow = document . querySelector ( '.scroll-arrow' ) ;
150
+
151
+ scrollArrow . addEventListener ( 'click' , ( e ) => {
152
+ e . preventDefault ( ) ;
153
+ const aboutSection = document . getElementById ( 'about' ) ;
154
+
155
+ aboutSection . scrollIntoView ( {
156
+ behavior : 'smooth' ,
157
+ block : 'start'
158
+ } ) ;
159
+ } ) ;
160
+ } ) ;
161
+
162
+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
163
+ const goToTopBtn = document . getElementById ( 'goToTopBtn' ) ;
164
+
165
+ function debounce ( func , wait = 10 , immediate = true ) {
166
+ let timeout ;
167
+ return function ( ) {
168
+ const context = this , args = arguments ;
169
+ const later = function ( ) {
170
+ timeout = null ;
171
+ if ( ! immediate ) func . apply ( context , args ) ;
172
+ } ;
173
+ const callNow = immediate && ! timeout ;
174
+ clearTimeout ( timeout ) ;
175
+ timeout = setTimeout ( later , wait ) ;
176
+ if ( callNow ) func . apply ( context , args ) ;
177
+ } ;
178
+ }
179
+
180
+ window . addEventListener ( 'scroll' , debounce ( ( ) => {
181
+ if ( window . pageYOffset > 300 ) {
182
+ goToTopBtn . classList . add ( 'show' ) ;
183
+ } else {
184
+ goToTopBtn . classList . remove ( 'show' ) ;
185
+ }
186
+ } ) ) ;
187
+
188
+ goToTopBtn . addEventListener ( 'click' , ( ) => {
189
+ const startPosition = window . pageYOffset ;
190
+ const startTime = performance . now ( ) ;
191
+ const duration = 1000 ;
192
+
193
+ function animation ( currentTime ) {
194
+ const elapsed = currentTime - startTime ;
195
+ const progress = Math . min ( elapsed / duration ,
196
+ 1 ) ;
197
+ const easeInOutCubic = progress < 0.5
198
+ ? 4 * progress ** 3 : 1 - ( ( - 2 * progress + 2 ) ** 3 ) / 2 ;
199
+
200
+ window . scrollTo ( 0 ,
201
+ startPosition * ( 1 - progress ) ) ;
202
+ if ( elapsed < duration ) requestAnimationFrame ( animation ) ;
203
+ }
204
+
205
+ requestAnimationFrame ( animation ) ;
206
+ } ) ;
207
+ } ) ;
208
+
209
+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
210
+ const progressBar = document . createElement ( 'div' ) ;
211
+ progressBar . id = 'scroll-progress' ;
212
+ document . body . appendChild ( progressBar ) ;
213
+
214
+ window . addEventListener ( 'scroll' ,
215
+ ( ) => {
216
+ const winScroll = document . body . scrollTop || document . documentElement . scrollTop ;
217
+ const height = document . documentElement . scrollHeight - document . documentElement . clientHeight ;
218
+ const scrolled = ( winScroll / height ) * 100 ;
219
+
220
+ progressBar . style . width = `${ scrolled } %` ;
221
+ } ) ;
222
+ } ) ;
0 commit comments