├── README.md
├── index.html
├── script.js
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # Download-Button-Animation-
3 | Creating Download Button Animation using HTML - CSS & JS.
4 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Download Button Animation | Ashutosh Python
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | /* Credit - youtube.com/@ashutoshpython */
2 | document.querySelectorAll('.button').forEach(button => {
3 |
4 | let duration = 3000,
5 | svg = button.querySelector('svg'),
6 | svgPath = new Proxy({
7 | y: null,
8 | smoothing: null
9 | }, {
10 | set(target, key, value) {
11 | target[key] = value;
12 | if(target.y !== null && target.smoothing !== null) {
13 | svg.innerHTML = getPath(target.y, target.smoothing, null);
14 | }
15 | return true;
16 | },
17 | get(target, key) {
18 | return target[key];
19 | }
20 | });
21 |
22 | button.style.setProperty('--duration', duration);
23 |
24 | svgPath.y = 20;
25 | svgPath.smoothing = 0;
26 |
27 | button.addEventListener('click', e => {
28 |
29 | e.preventDefault();
30 |
31 | if(!button.classList.contains('loading')) {
32 |
33 | button.classList.add('loading');
34 |
35 | gsap.to(svgPath, {
36 | smoothing: .3,
37 | duration: duration * .065 / 1000
38 | });
39 |
40 | gsap.to(svgPath, {
41 | y: 12,
42 | duration: duration * .265 / 1000,
43 | delay: duration * .065 / 1000,
44 | ease: Elastic.easeOut.config(1.12, .4)
45 | });
46 |
47 | setTimeout(() => {
48 | svg.innerHTML = getPath(0, 0, [
49 | [3, 14],
50 | [8, 19],
51 | [21, 6]
52 | ]);
53 | }, duration / 2);
54 |
55 | }
56 |
57 | });
58 |
59 | });
60 |
61 | function getPoint(point, i, a, smoothing) {
62 | let cp = (current, previous, next, reverse) => {
63 | let p = previous || current,
64 | n = next || current,
65 | o = {
66 | length: Math.sqrt(Math.pow(n[0] - p[0], 2) + Math.pow(n[1] - p[1], 2)),
67 | angle: Math.atan2(n[1] - p[1], n[0] - p[0])
68 | },
69 | angle = o.angle + (reverse ? Math.PI : 0),
70 | length = o.length * smoothing;
71 | return [current[0] + Math.cos(angle) * length, current[1] + Math.sin(angle) * length];
72 | },
73 | cps = cp(a[i - 1], a[i - 2], point, false),
74 | cpe = cp(point, a[i - 1], a[i + 1], true);
75 | return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`;
76 | }
77 |
78 | function getPath(update, smoothing, pointsNew) {
79 | let points = pointsNew ? pointsNew : [
80 | [4, 12],
81 | [12, update],
82 | [20, 12]
83 | ],
84 | d = points.reduce((acc, point, i, a) => i === 0 ? `M ${point[0]},${point[1]}` : `${acc} ${getPoint(point, i, a, smoothing)}`, '');
85 | return ``;
86 | }
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | /* Credit - youtube.com/@ashutoshpython */
2 |
3 | .button.dark-single {
4 | --background: none;
5 | --rectangle: #242836;
6 | --success: #4BC793;
7 | }
8 | .button.white-single {
9 | --background: none;
10 | --rectangle: #F5F9FF;
11 | --arrow: #275efe;
12 | --success: #275efe;
13 | --shadow: rgba(10, 22, 50, .1);
14 | }
15 | .button.dark {
16 | --background: #242836;
17 | --rectangle: #1C212E;
18 | --arrow: #F5F9FF;
19 | --text: #F5F9FF;
20 | --success: #2F3545;
21 | }
22 |
23 | .button {
24 | --background: #275efe;
25 | --rectangle: #184fee;
26 | --success: #4672f1;
27 | --text: #fff;
28 | --arrow: #fff;
29 | --checkmark: #fff;
30 | --shadow: rgba(10, 22, 50, .24);
31 | display: flex;
32 | overflow: hidden;
33 | text-decoration: none;
34 | -webkit-mask-image: -webkit-radial-gradient(white, black);
35 | background: var(--background);
36 | border-radius: 8px;
37 | box-shadow: 0 2px 8px -1px var(--shadow);
38 | transition: transform 0.2s ease, box-shadow 0.2s ease;
39 | }
40 | .button:active {
41 | transform: scale(0.95);
42 | box-shadow: 0 1px 4px -1px var(--shadow);
43 | }
44 | .button ul {
45 | margin: 0;
46 | padding: 16px 40px;
47 | list-style: none;
48 | text-align: center;
49 | position: relative;
50 | -webkit-backface-visibility: hidden;
51 | backface-visibility: hidden;
52 | font-size: 16px;
53 | font-weight: 500;
54 | line-height: 28px;
55 | color: var(--text);
56 | }
57 | .button ul li:not(:first-child) {
58 | top: 16px;
59 | left: 0;
60 | right: 0;
61 | position: absolute;
62 | }
63 | .button ul li:nth-child(2) {
64 | top: 76px;
65 | }
66 | .button ul li:nth-child(3) {
67 | top: 136px;
68 | }
69 | .button > div {
70 | position: relative;
71 | width: 60px;
72 | height: 60px;
73 | background: var(--rectangle);
74 | }
75 | .button > div:before, .button > div:after {
76 | content: "";
77 | display: block;
78 | position: absolute;
79 | }
80 | .button > div:before {
81 | border-radius: 1px;
82 | width: 2px;
83 | top: 50%;
84 | left: 50%;
85 | height: 17px;
86 | margin: -9px 0 0 -1px;
87 | background: var(--arrow);
88 | }
89 | .button > div:after {
90 | width: 60px;
91 | height: 60px;
92 | transform-origin: 50% 0;
93 | border-radius: 0 0 80% 80%;
94 | background: var(--success);
95 | top: 0;
96 | left: 0;
97 | transform: scaleY(0);
98 | }
99 | .button > div svg {
100 | display: block;
101 | position: absolute;
102 | width: 20px;
103 | height: 20px;
104 | left: 50%;
105 | top: 50%;
106 | margin: -9px 0 0 -10px;
107 | fill: none;
108 | z-index: 1;
109 | stroke-width: 2px;
110 | stroke: var(--arrow);
111 | stroke-linecap: round;
112 | stroke-linejoin: round;
113 | }
114 | .button.loading ul {
115 | -webkit-animation: text calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
116 | animation: text calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
117 | }
118 | .button.loading > div:before {
119 | -webkit-animation: line calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
120 | animation: line calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
121 | }
122 | .button.loading > div:after {
123 | -webkit-animation: background calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
124 | animation: background calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
125 | }
126 | .button.loading > div svg {
127 | -webkit-animation: svg calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
128 | animation: svg calc(var(--duration) * 1ms) linear forwards calc(var(--duration) * .065ms);
129 | }
130 |
131 | @-webkit-keyframes text {
132 | 10%, 85% {
133 | transform: translateY(-100%);
134 | }
135 | 95%, 100% {
136 | transform: translateY(-200%);
137 | }
138 | }
139 |
140 | @keyframes text {
141 | 10%, 85% {
142 | transform: translateY(-100%);
143 | }
144 | 95%, 100% {
145 | transform: translateY(-200%);
146 | }
147 | }
148 | @-webkit-keyframes line {
149 | 5%, 10% {
150 | transform: translateY(-30px);
151 | }
152 | 40% {
153 | transform: translateY(-20px);
154 | }
155 | 65% {
156 | transform: translateY(0);
157 | }
158 | 75%, 100% {
159 | transform: translateY(30px);
160 | }
161 | }
162 | @keyframes line {
163 | 5%, 10% {
164 | transform: translateY(-30px);
165 | }
166 | 40% {
167 | transform: translateY(-20px);
168 | }
169 | 65% {
170 | transform: translateY(0);
171 | }
172 | 75%, 100% {
173 | transform: translateY(30px);
174 | }
175 | }
176 | @-webkit-keyframes svg {
177 | 0%, 20% {
178 | stroke-dasharray: 0;
179 | stroke-dashoffset: 0;
180 | }
181 | 21%, 89% {
182 | stroke-dasharray: 26px;
183 | stroke-dashoffset: 26px;
184 | stroke-width: 3px;
185 | margin: -10px 0 0 -10px;
186 | stroke: var(--checkmark);
187 | }
188 | 100% {
189 | stroke-dasharray: 26px;
190 | stroke-dashoffset: 0;
191 | margin: -10px 0 0 -10px;
192 | stroke: var(--checkmark);
193 | }
194 | 12% {
195 | opacity: 1;
196 | }
197 | 20%, 89% {
198 | opacity: 0;
199 | }
200 | 90%, 100% {
201 | opacity: 1;
202 | }
203 | }
204 | @keyframes svg {
205 | 0%, 20% {
206 | stroke-dasharray: 0;
207 | stroke-dashoffset: 0;
208 | }
209 | 21%, 89% {
210 | stroke-dasharray: 26px;
211 | stroke-dashoffset: 26px;
212 | stroke-width: 3px;
213 | margin: -10px 0 0 -10px;
214 | stroke: var(--checkmark);
215 | }
216 | 100% {
217 | stroke-dasharray: 26px;
218 | stroke-dashoffset: 0;
219 | margin: -10px 0 0 -10px;
220 | stroke: var(--checkmark);
221 | }
222 | 12% {
223 | opacity: 1;
224 | }
225 | 20%, 89% {
226 | opacity: 0;
227 | }
228 | 90%, 100% {
229 | opacity: 1;
230 | }
231 | }
232 | @-webkit-keyframes background {
233 | 10% {
234 | transform: scaleY(0);
235 | }
236 | 40% {
237 | transform: scaleY(0.15);
238 | }
239 | 65% {
240 | transform: scaleY(0.5);
241 | border-radius: 0 0 50% 50%;
242 | }
243 | 75% {
244 | border-radius: 0 0 50% 50%;
245 | }
246 | 90%, 100% {
247 | border-radius: 0;
248 | }
249 | 75%, 100% {
250 | transform: scaleY(1);
251 | }
252 | }
253 | @keyframes background {
254 | 10% {
255 | transform: scaleY(0);
256 | }
257 | 40% {
258 | transform: scaleY(0.15);
259 | }
260 | 65% {
261 | transform: scaleY(0.5);
262 | border-radius: 0 0 50% 50%;
263 | }
264 | 75% {
265 | border-radius: 0 0 50% 50%;
266 | }
267 | 90%, 100% {
268 | border-radius: 0;
269 | }
270 | 75%, 100% {
271 | transform: scaleY(1);
272 | }
273 | }
274 | html {
275 | box-sizing: border-box;
276 | -webkit-font-smoothing: antialiased;
277 | }
278 |
279 | * {
280 | box-sizing: inherit;
281 | }
282 | *:before, *:after {
283 | box-sizing: inherit;
284 | }
285 |
286 | body {
287 | min-height: 100vh;
288 | display: flex;
289 | font-family: "Roboto", Arial;
290 | justify-content: center;
291 | align-items: center;
292 | background: #E4ECFA;
293 | }
294 | body .container {
295 | display: flex;
296 | flex-wrap: wrap;
297 | justify-content: center;
298 | }
299 | body .container > div {
300 | flex-basis: 100%;
301 | width: 0;
302 | }
303 | body .container .button {
304 | margin: 16px;
305 | }
306 | @media (max-width: 400px) {
307 | body .container .button {
308 | margin: 12px;
309 | }
310 | }
311 | body .dribbble {
312 | position: fixed;
313 | display: block;
314 | right: 20px;
315 | bottom: 20px;
316 | }
317 | body .dribbble img {
318 | display: block;
319 | height: 28px;
320 | }
--------------------------------------------------------------------------------