如何使用 CSS、anime.js 和 segment.js 通过微交互创建下载按钮

介绍

在用户体验设计中, microinteractions是帮助用户导航界面的小反馈时刻。

在本教程中,您将构建一个功能性下载按钮与微互动. 要使它工作,我们将使用CSS过渡和动画,以及轻量级动画库 anime.jssegment.js为SVG path动画。

在教程结束时,我们会得到这样的下载按钮:

Fancy Download Button Demo

该下载按钮的原始设计属于Pedro Aquino,并可以在 这个Dribbble拍摄找到完整的代码,可以在 这个Github存储,和 这里是演示页面

第1步:创建HTML结构

让我们来看看我们将使用的HTML代码:

 1<!-- Button container -->
 2<div class="download-button-container">
 3    <!-- The real button -->
 4    <button class="download-button">
 5        <span class="button-text-real hidden">download</span>
 6        <!-- Extra elements to perform the animations -->
 7        <span class="button-icon">
 8            <span class="button-linear-progress">
 9                <span class="button-linear-progress-bar"></span>
10            </span>
11            <svg class="button-icon-svg" viewBox="0 0 60 60">
12                <path class="button-icon-path button-icon-path-square" d="M 20 40 l 0 -20 l 20 0 l 0 20 Z"></path>
13                <path class="button-icon-path button-icon-path-line" d="M 40 20 l -20 20"></path>
14            </svg>
15        </span>
16    </button>
17    <!-- Extra elements to perform the animations -->
18    <svg class="border-svg" width="240px" height="100px" viewBox="0 0 240 100">
19        <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>
20    </svg>
21    <span class="button-text button-text-download">download</span>
22    <span class="button-text button-text-done">done!</span>
23    <div class="button-wave"></div>
24    <div class="button-progress-container">
25        <svg class="button-svg">
26            <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>
27        </svg>
28        <span class="button-ball"></span>
29    </div>
30</div>

例如,在某个时刻,我们希望按钮边缘执行弹性动画,所以我们需要一个 SVG):

SVG paths for the download button border

第2步:添加风格

请注意,我们在这里不包括整个风格表,而是最重要的部分;您可以在 Github 存储库找到整个代码。

让我们看看我们定义的 SCSS 变量,以及隐藏元素的辅助类:

 1// Some variables to use later
 2$button-width: 300px;
 3$button-height: 70px;
 4$button-border: 3px;
 5$icon-padding: 5px;
 6$icon-width: $button-height - ($icon-padding * 2);
 7$ball-width: 18px;
 8
 9// Helper class to hide elements
10.hidden {
11  visibility: hidden !important;
12  opacity: 0 !important;
13}

实际按钮元素的风格:

 1// Real button styles
 2.download-button {
 3  position: relative;
 4  display: inline-block;
 5  width: $button-width;
 6  height: $button-height;
 7  background-color: #2C2E2F;
 8  border: none;
 9  box-shadow: 0 0 0 $button-border #02D1FF; // This will be our 'border'
10  border-radius: 100px;
11  cursor: pointer;
12  transition: 1s width, 0.3s box-shadow;
13
14  // Remove the custom behavior in some browsers
15  &, &:focus {
16    padding: 0;
17    outline: none;
18  }
19  &::-moz-focus-inner {
20    border: 0;
21  }
22
23  // Styles for the different states of the button
24  &:hover, &:active, &:focus {
25    box-shadow: 0 0 0 $button-border #02D1FF, 0 0 20px $button-border darken(#02D1FF, 20%);
26  }
27}

我们的按钮可以处于三个不同的状态:下载,进展完成,因此我们使用以下结构定义了每个状态所需的样式:

 1// Button container
 2.download-button-container {
 3  // ...CODE...
 4
 5  // Following are the different states for the button: downloading, progressing and completed
 6  // We have defined the states in the container to have access to all descendants in CSS
 7
 8  // Downloading: The download button has been pressed
 9  &.downloading {
10    // ...CODE...
11  }
12
13  // Progressing: The progress starts
14  &.progressing {
15    // ...CODE...
16  }
17
18  // Completed: The progress ends
19  &.completed {
20    // ...CODE...
21  }
22}

另一个有趣的代码被用来完成下载后实现球动画:

 1.button-ball {
 2  left: 50%;
 3  transition: none;
 4  // CSS animations for the ball. All of them start at the same time, so we need to take care of delays
 5  animation:
 6          ball-throw-up 0.5s ease-out forwards, // Throw up the ball for 0.5s
 7          ball-throw-down 0.5s 0.5s ease-in forwards, // Wait 0.5 seconds (throw up), and throw down the ball for 0.5s
 8          ball-rubber 1s forwards; // Move the ball like a rubber deformation during 1s (throw up + throw down)
 9}
10
11// Throw up animation
12@keyframes ball-throw-up {
13  from {
14    transform: translate(-50%, 17.5px);
15  }
16  to {
17    transform: translate(-50%, -60px);
18    background-color: #00FF8D;
19  }
20}
21
22// Throw down animation
23@keyframes ball-throw-down {
24  from {
25    transform: translate(-50%, -60px);
26  }
27  to {
28    transform: translate(-50%, 80px);
29  }
30}
31
32// Rubber animation
33@keyframes ball-rubber {
34  from {
35    width: $ball-width;
36  }
37  25% {
38    width: $ball-width * 0.75;
39  }
40  50% {
41    width: $ball-width;
42  }
43  to {
44    width: $ball-width / 2;
45  }
46}

所有其他使用的风格都可以在 Github 存储库找到。

第3步:使用JavaScript动画

我们将使用 anime.jssegment.js,这两个轻量级的库来帮助动画。

请注意,我们不会在以下代码片段中包含某些变量声明,为了澄清。如果您有任何疑问,请检查 Github 存储库

以下是我们使用的基本代码来捕捉按钮上的点击事件,并执行我们想要的行为:

 1// Capture click events
 2button.addEventListener('click', function () {
 3    if (!completed) { // Don't do anything if downloading has been completed
 4        if (downloading) { // If it's downloading, stop the download
 5            stopDownload();
 6        } else { // Start the download
 7            startDownload();
 8        }
 9    }
10});
11
12// Start the download
13function startDownload() {
14    // Update variables and CSS classes
15    downloading = true;
16    buttonContainer.classList.add('downloading');
17    animateIcon();
18    // Update progress after 1s
19    progressTimer = setTimeout(function () {
20        buttonContainer.classList.add('progressing');
21        animateProgress();
22    }, 1000);
23}
24
25// Stop the download
26function stopDownload() {
27    // Update variables and CSS classes
28    downloading = false;
29    clearTimeout(progressTimer);
30    buttonContainer.classList.remove('downloading');
31    buttonContainer.classList.remove('progressing');
32    // Stop progress and draw icons back to initial state
33    stopProgress();
34    iconLine.draw(0, '100%', 1, {easing: anime.easings['easeOutCubic']});
35    iconSquare.draw('30%', '70%', 1, {easing: anime.easings['easeOutQuad']});
36}

动画进度在演示中被伪造了;对于真实的使用案例,它将被真实的进度数据取代。

1// Progress animation
2function animateProgress() {
3    // Fake progress animation from 0 to 100%
4    // This should be replaced with real progress data (real progress percent instead '100%'), and maybe called multiple times
5    circularProgressBar.draw(0, '100%', 2.5, {easing: anime.easings['easeInQuart'], update: updateProgress, callback: completedAnimation});
6}

最后,这里是用来执行动画的代码,当下载完成时,球动画被触发,我们改变了路径元素。

 1// Animation performed when download has been completed
 2function completedAnimation() {
 3    // Update variables and CSS classes
 4    completed = true;
 5    buttonContainer.classList.add('completed');
 6    // Wait 1s for the ball animation
 7    setTimeout(function () {
 8        button.classList.add('button-hidden');
 9        ball.classList.add('hidden');
10        borderPath.classList.remove('hidden');
11        // Morphing the path to the second shape
12        var morph = anime({
13            targets: borderPath,
14            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',
15            duration: 100,
16            easing: 'linear',
17            complete: function () {
18                // Morphing the path back to the original shape with elasticity
19                morph = anime({
20                    targets: borderPath,
21                    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',
22                    duration: 1000,
23                    elasticity: 600,
24                    complete: function () {
25                        // Update variables and CSS classes, and return the button to the original state
26                        completed = false;
27                        setTimeout(function () {
28                            buttonContainer.classList.remove('completed');
29                            button.classList.remove('button-hidden');
30                            ball.classList.remove('hidden');
31                            borderPath.classList.add('hidden');
32                            stopDownload();
33                        }, 500);
34                    }
35                });
36            }
37        });
38    }, 1000);
39}

结论

本文展示了用于构建此下载按钮的主要代码:

Fancy Download Button Demo

您可以 玩与现场 DEMO,或 得到完整的代码在Github. 请注意,这个组件还没有完全准备好生产,因为它需要真实的进度数据和一些考虑后端将如何影响微互动。

Published At
Categories with 技术
Tagged with
comments powered by Disqus