如何使用CSS、anime.js和segment.js构建具有微交互的下载按钮
介绍
在用户体验设计中,微交互是帮助用户导航界面的小反馈时刻。 通常,微交互是通过网站设计中的微妙动画进行的。
在本教程中,您将构建一个带有微交互的功能下载按钮。 为了让它工作,我们将使用 CSS 过渡和动画,以及用于 SVG path
动画的轻量级动画库 anime.js 和 segment.js。
在教程结束时,我们将获得一个下载按钮,如下所示:
下载按钮的原始设计属于佩德罗·阿基诺,可以在这张Dribbble shot上找到。 完整代码可以在这个Github存储库上找到,这里是演示页面。
第 1 步 - 制作 HTML 结构
让我们看看我们将使用的 HTML 代码:
<!-- Button container --> <div class="download-button-container"> <!-- The real button --> <button class="download-button"> <span class="button-text-real hidden">download</span> <!-- Extra elements to perform the animations --> <span class="button-icon"> <span class="button-linear-progress"> <span class="button-linear-progress-bar"></span> </span> <svg class="button-icon-svg" viewBox="0 0 60 60"> <path class="button-icon-path button-icon-path-square" d="M 20 40 l 0 -20 l 20 0 l 0 20 Z"></path> <path class="button-icon-path button-icon-path-line" d="M 40 20 l -20 20"></path> </svg> </span> </button> <!-- Extra elements to perform the animations --> <svg class="border-svg" width="240px" height="100px" viewBox="0 0 240 100"> <path class="border-path hidden" d="M 40 3.5 a 36.5 36.5 0 0 0 -36.5 36.5 a 36.5 36.5 0 0 0 36.5 36.5 C 70 76.5 90 76.5 120 76.5 S 170 76.5 200 76.5 a 36.5 36.5 0 0 0 36.5 -36.5 a 36.5 36.5 0 0 0 -36.5 -36.5 Z"></path> </svg> <span class="button-text button-text-download">download</span> <span class="button-text button-text-done">done!</span> <div class="button-wave"></div> <div class="button-progress-container"> <svg class="button-svg"> <path class="button-circular-progress" d="M 50 50 m 0 -32.5 a 32.5 32.5 0 0 1 0 65 a 32.5 32.5 0 0 1 0 -65"></path> </svg> <span class="button-ball"></span> </div> </div>
需要注意的是,SVG path
元素是手工绘制的,以获得我们想要的结果。 例如,在某些时候,我们希望按钮边框执行弹性动画,因此我们需要一个 SVG path
为带有anime.js 的变形动画做好准备(paths
中的结构相同) :
第 2 步 — 添加样式
准备好标记后,让我们为按钮设置样式。 请注意,我们在这里不包括整个样式表,而是最重要的部分; 您可以在 Github 存储库 上找到完整的代码。 为了更好地理解,代码已被充分注释。
让我们看看我们定义的 SCSS 变量,以及隐藏元素的辅助类:
// Some variables to use later $button-width: 300px; $button-height: 70px; $button-border: 3px; $icon-padding: 5px; $icon-width: $button-height - ($icon-padding * 2); $ball-width: 18px; // Helper class to hide elements .hidden { visibility: hidden !important; opacity: 0 !important; }
真正的 button
元素的样式:
// Real button styles .download-button { position: relative; display: inline-block; width: $button-width; height: $button-height; background-color: #2C2E2F; border: none; box-shadow: 0 0 0 $button-border #02D1FF; // This will be our 'border' border-radius: 100px; cursor: pointer; transition: 1s width, 0.3s box-shadow; // Remove the custom behavior in some browsers &, &:focus { padding: 0; outline: none; } &::-moz-focus-inner { border: 0; } // Styles for the different states of the button &:hover, &:active, &:focus { box-shadow: 0 0 0 $button-border #02D1FF, 0 0 20px $button-border darken(#02D1FF, 20%); } }
我们的按钮可以处于三种不同的状态:downloading
、progressing
和 completed
。 因此,我们使用以下结构定义了每个状态所需的样式:
// Button container .download-button-container { // ...CODE... // Following are the different states for the button: downloading, progressing and completed // We have defined the states in the container to have access to all descendants in CSS // Downloading: The download button has been pressed &.downloading { // ...CODE... } // Progressing: The progress starts &.progressing { // ...CODE... } // Completed: The progress ends &.completed { // ...CODE... } }
另一个有趣的代码用于在下载完成后实现球动画:
.button-ball { left: 50%; transition: none; // CSS animations for the ball. All of them start at the same time, so we need to take care of delays animation: ball-throw-up 0.5s ease-out forwards, // Throw up the ball for 0.5s ball-throw-down 0.5s 0.5s ease-in forwards, // Wait 0.5 seconds (throw up), and throw down the ball for 0.5s ball-rubber 1s forwards; // Move the ball like a rubber deformation during 1s (throw up + throw down) } // Throw up animation @keyframes ball-throw-up { from { transform: translate(-50%, 17.5px); } to { transform: translate(-50%, -60px); background-color: #00FF8D; } } // Throw down animation @keyframes ball-throw-down { from { transform: translate(-50%, -60px); } to { transform: translate(-50%, 80px); } } // Rubber animation @keyframes ball-rubber { from { width: $ball-width; } 25% { width: $ball-width * 0.75; } 50% { width: $ball-width; } to { width: $ball-width / 2; } }
所有其他使用的样式都可以在 Github 存储库 上找到。
第 3 步 — 使用 Javascript 制作动画
我们将使用 anime.js 和 segment.js 这两个轻量级库来帮助制作动画。
请注意,为清楚起见,我们不会在以下代码片段中包含一些变量声明。 如果您有任何疑问,请查看 Github 存储库 。
这是我们用来捕获 button
上的点击事件并执行我们想要的行为的基本代码:
// Capture click events button.addEventListener('click', function () { if (!completed) { // Don't do anything if downloading has been completed if (downloading) { // If it's downloading, stop the download stopDownload(); } else { // Start the download startDownload(); } } }); // Start the download function startDownload() { // Update variables and CSS classes downloading = true; buttonContainer.classList.add('downloading'); animateIcon(); // Update progress after 1s progressTimer = setTimeout(function () { buttonContainer.classList.add('progressing'); animateProgress(); }, 1000); } // Stop the download function stopDownload() { // Update variables and CSS classes downloading = false; clearTimeout(progressTimer); buttonContainer.classList.remove('downloading'); buttonContainer.classList.remove('progressing'); // Stop progress and draw icons back to initial state stopProgress(); iconLine.draw(0, '100%', 1, {easing: anime.easings['easeOutCubic']}); iconSquare.draw('30%', '70%', 1, {easing: anime.easings['easeOutQuad']}); }
演示中的动画进度已被伪造; 对于真实的用例,它将被真实的进度数据取代。 这是处理进度的函数:
// Progress animation function animateProgress() { // Fake progress animation from 0 to 100% // This should be replaced with real progress data (real progress percent instead '100%'), and maybe called multiple times circularProgressBar.draw(0, '100%', 2.5, {easing: anime.easings['easeInQuart'], update: updateProgress, callback: completedAnimation}); }
最后,这里是用于在下载完成后执行动画的一段代码,其中触发了球动画,我们变形了 path
元素。
// Animation performed when download has been completed function completedAnimation() { // Update variables and CSS classes completed = true; buttonContainer.classList.add('completed'); // Wait 1s for the ball animation setTimeout(function () { button.classList.add('button-hidden'); ball.classList.add('hidden'); borderPath.classList.remove('hidden'); // Morphing the path to the second shape var morph = anime({ targets: borderPath, d: 'M 40 3.5 a 36.5 36.5 0 0 0 -36.5 36.5 a 36.5 36.5 0 0 0 10.5 26.5 C 35 86.5 90 91.5 120 91.5 S 205 86.5 226 66.5 a 36.5 36.5 0 0 0 10.5 -26.5 a 36.5 36.5 0 0 0 -36.5 -36.5 Z', duration: 100, easing: 'linear', complete: function () { // Morphing the path back to the original shape with elasticity morph = anime({ targets: borderPath, d: 'M 40 3.5 a 36.5 36.5 0 0 0 -36.5 36.5 a 36.5 36.5 0 0 0 36.5 36.5 C 70 76.5 90 76.5 120 76.5 S 170 76.5 200 76.5 a 36.5 36.5 0 0 0 36.5 -36.5 a 36.5 36.5 0 0 0 -36.5 -36.5 Z', duration: 1000, elasticity: 600, complete: function () { // Update variables and CSS classes, and return the button to the original state completed = false; setTimeout(function () { buttonContainer.classList.remove('completed'); button.classList.remove('button-hidden'); ball.classList.remove('hidden'); borderPath.classList.add('hidden'); stopDownload(); }, 500); } }); } }); }, 1000); }
结论
本文展示了用于构建此下载按钮的主要代码片段:
你可以玩现场DEMO,或者在Github上获取完整代码。 另请注意,此组件尚未完全准备好投入生产,因为它需要真实的进度数据以及后端如何影响微交互的一些考虑。