├── README.md ├── index.html ├── script.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | ![down](https://github.com/creatoraashu/Download-Button-Animation-/assets/130897584/65745092-81dd-4a94-9217-17dab5ae3434) 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 |
16 | 17 | 18 | 23 |
24 | 25 |
26 |
27 | 28 | 29 |
30 | 31 |
32 |
33 | 34 |
35 | 36 | 37 |
38 | 39 |
40 |
41 | 42 | 43 | 48 |
49 | 50 |
51 |
52 | 53 |
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 | } --------------------------------------------------------------------------------