先看效果:
再看代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下载按钮</title>
<link href="https://fonts.googleapis.com/css2?family=Hind&display=swap" type="text/css" rel="stylesheet">
<style>
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--dur: 3s;
--arrowA: polygon(33% 0%,67% 0%,67% 60%,100% 60%,50% 100%,0% 60%,33% 60%);
--arrowB: polygon(0% 37.5%,100% 37.5%,100% 62.5%,100% 62.5%,50% 62.5%,0% 62.5%,0% 62.5%);
font-size: calc(20px + (40 - 20)*(100vw - 320px)/(2560 - 320));
}
body, button {
display: flex;
font: 1em/1.5 Hind, sans-serif;
}
body {
background: #e3e4e8;
height: 100vh;
overflow: hidden;
}
button {
background: #255ff4;
border-radius: 0.2em;
color: #fff;
cursor: pointer;
display: flex;
margin: auto;
padding: 0.5em 1em;
position: relative;
transition: background 0.15s linear;
width: 10.5em;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-tap-highlight-color: transparent;
}
button:focus {
outline: transparent;
}
button::-moz-focus-inner {
border: 0;
}
button:not(:disabled):focus, button:not(:disabled):hover {
background: #0b46da;
}
button:not(:disabled):active {
transform: translateY(1px);
}
button:disabled {
cursor: not-allowed;
}
button span, button:before, button:after {
display: inline-block;
pointer-events: none;
}
button:before, button:after {
border-radius: 0.25em;
opacity: 0;
top: 1em;
left: 1.3em;
height: 0.5em;
transform-origin: 0.25em 50%;
z-index: 2;
}
button:before {
transform: rotate(-180deg);
width: 0.8em;
}
button:after {
width: 1.2em;
}
button:before, button:after, .dl-icon:before, .dl-icon:after {
content: "";
display: block;
position: absolute;
}
button:before, button:after, .dl-icon:before {
background: currentColor;
}
button span + span {
margin: auto;
}
.dl-icon {
margin-right: 0.5em;
position: relative;
width: 1.5em;
height: 1.5em;
}
.dl-icon:before {
clip-path: var(--arrowA);
-webkit-clip-path: var(--arrowA);
top: 0;
left: calc(50% - 0.55em);
transform-origin: 50% 100%;
width: 1.1em;
height: 1em;
z-index: 1;
}
.dl-icon:after {
background-image: linear-gradient(#0b46da,#0b46da);
background-position: -1.5em 0;
background-size: 100% 100%;
background-repeat: no-repeat;
box-shadow:
0.25em 0 0 inset,
-0.25em 0 0 inset,
0 -0.25em 0 inset;
bottom: 0;
width: 100%;
height: 0.5em;
}
.dl-working:before {
animation: checkOutA var(--dur) linear forwards;
}
.dl-working:after {
animation: checkOutB var(--dur) linear forwards;
}
.dl-working .dl-icon {
animation: impact var(--dur) linear forwards;
}
.dl-working .dl-icon:before {
animation: arrowToBar var(--dur) linear forwards;
}
.dl-working .dl-icon:after {
animation: trayToBar var(--dur) linear forwards;
}
/* Animation */
@keyframes impact {
from, 15% {
transform: translateY(0);
}
17.5% {
transform: translateY(0.25em);
}
20%, to {
transform: translateY(0);
}
}
@keyframes arrowToBar {
from {
clip-path: var(--arrowA);
-webkit-clip-path: var(--arrowA);
}
10% {
clip-path: var(--arrowB);
-webkit-clip-path: var(--arrowB);
transform: translateY(0);
}
15% {
clip-path: var(--arrowB);
-webkit-clip-path: var(--arrowB);
transform: translateY(0.625em);
}
30% {
clip-path: var(--arrowB);
-webkit-clip-path: var(--arrowB);
opacity: 1;
transform: translateY(0.125em);
}
35%, to {
clip-path: var(--arrowB);
-webkit-clip-path: var(--arrowB);
opacity: 0;
transform: translateY(0.125em);
}
}
@keyframes trayToBar {
from, 15% {
background-color: transparent;
border-radius: 0;
box-shadow:
0.25em 0 0 inset,
-0.25em 0 0 inset,
0 -0.25em 0 inset;
transform: translateY(0);
}
15.1% {
background-color: currentColor;
border-radius: 0;
box-shadow:
0 0 0 inset,
0 0 0 inset,
0 0 0 0.1em inset;
transform: translateY(0);
}
30% {
background-color: currentColor;
background-position: -1.5em 0;
border-radius: 0.25em;
box-shadow:
0 0 0 inset,
0 0 0 inset,
0 0 0 0.1em inset;
transform: translateY(-0.5em);
}
90% {
background-color: currentColor;
background-position: 0 0;
border-radius: 0.25em;
box-shadow:
0 0 0 inset,
0 0 0 inset,
0 0 0 0.1em inset;
opacity: 1;
transform: translateY(-0.5em);
}
90.1%, to {
background-color: currentColor;
background-position: 0 0;
border-radius: 0.25em;
box-shadow:
0 0 0 inset,
0 0 0 inset,
0 0 0 0.1em inset;
opacity: 0;
transform: translateY(-0.5em);
}
}
@keyframes checkOutA {
from, 90% {
opacity: 0;
transform: translate(0,0) rotate(-180deg);
}
90.1% {
opacity: 1;
width: 0.8em;
transform: translate(0,0) rotate(-180deg);
}
92.5% {
opacity: 1;
width: 1em;
transform: translate(0,0.5em) rotate(-120deg);
}
95%, to {
opacity: 1;
width: 1em;
transform: translate(0,0.375em) rotate(-135deg);
}
}
@keyframes checkOutB {
from, 90% {
opacity: 0;
transform: translate(0,0) rotate(0);
}
90.1% {
opacity: 1;
width: 1.2em;
transform: translate(0,0) rotate(0);
}
92.5% {
opacity: 1;
width: 1.6em;
transform: translate(0,0.5em) rotate(-60deg);
}
95%, to {
opacity: 1;
width: 1.6em;
transform: translate(0,0.375em) rotate(-45deg);
}
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
body {
background: #2e3138;
}
}
</style>
</head>
<body>
<button type="button" data-dl>
<span class="dl-icon"></span><span>Download</span>
</button>
</body>
<script>
document.addEventListener("DOMContentLoaded",function(){
this.addEventListener("click",e => {
let tar = e.target;
if (tar.hasAttribute("data-dl")) {
let dlClass = "dl-working";
if (!tar.classList.contains(dlClass)) {
let lastSpan = tar.querySelector("span:last-child"),
lastSpanText = lastSpan.textContent,
timeout = getMSFromProperty("--dur",":root");
tar.classList.add(dlClass);
lastSpan.textContent = "Downloading…";
tar.disabled = true;
setTimeout(() => {
lastSpan.textContent = "Completed!";
},timeout * 0.9);
setTimeout(() => {
tar.classList.remove(dlClass);
lastSpan.textContent = lastSpanText;
tar.disabled = false;
},timeout + 1e3);
}
}
});
});
function getMSFromProperty(property,selector) {
let cs = window.getComputedStyle(document.querySelector(selector)),
transDur = cs.getPropertyValue(property),
msLabelPos = transDur.indexOf("ms"),
sLabelPos = transDur.indexOf("s");
if (msLabelPos > -1)
return transDur.substr(0,msLabelPos);
else if (sLabelPos > -1)
return transDur.substr(0,sLabelPos) * 1e3;
}
</script>
</html>