Cách tạo node tải xuống với các tương tác nhỏ với CSS, anime.js và segment.js
Giới thiệu
Trong sử dụng thiết kế kinh nghiệm, microinteractions những khoảnh khắc nhỏ của phản hồi giúp user chuyển giao diện. Thông thường, các tương tác vi mô được thực hiện với hoạt ảnh tinh tế trong thiết kế trang web.
Trong hướng dẫn này, bạn sẽ xây dựng một nút download chức năng với các tương tác vi mô. Để làm cho nó làm việc, ta sẽ sử dụng hiệu ứng chuyển tiếp CSS và hình ảnh động, cùng với các thư viện hình ảnh động nhẹ anime.js và segment.js cho SVG path
hình ảnh động.
Ở cuối hướng dẫn, ta sẽ nhận được một nút download như sau:
Thiết kế ban đầu của nút download thuộc về Pedro Aquino và có thể được tìm thấy trên cú sút Dribbble này . Bạn có thể tìm thấy mã đầy đủ trên kho Github này và đây là trang demo .
Bước 1 - Tạo cấu trúc HTML
Hãy xem mã HTML mà ta sẽ sử dụng:
<!-- 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>
Điều quan trọng cần lưu ý là các phần tử path
SVG đã được vẽ bằng tay để có được kết quả ta muốn. Ví dụ: tại một số điểm, ta muốn đường viền nút thực hiện hoạt ảnh co giãn, vì vậy ta cần một path
SVG sẵn sàng cho hoạt ảnh biến đổi đó với anime.js (cấu trúc giống nhau trong cả hai paths
):
Bước 2 - Thêm kiểu
Với đánh dấu của ta đã sẵn sàng, hãy tạo kiểu cho nút của ta . Xin lưu ý ta không bao gồm toàn bộ biểu định kiểu ở đây, mà là những phần quan trọng nhất; bạn có thể tìm thấy toàn bộ mã trên kho lưu trữ Github . Mã đã được comment đầy đủ để hiểu rõ hơn.
Hãy xem các biến SCSS mà ta đã xác định và lớp trợ giúp để ẩn các phần tử:
// 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;
}
Các kiểu cho phần tử button
thực:
// 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%);
}
}
Nút của ta có thể ở ba trạng thái khác nhau: đang downloading
, progressing
và completed
. Do đó, ta đã xác định các kiểu cần thiết cho mỗi trạng thái bằng cách sử dụng cấu trúc sau:
// 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...
}
}
Một đoạn mã thú vị khác được sử dụng để tạo hoạt ảnh quả bóng khi quá trình download hoàn tất:
.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;
}
}
Tất cả các kiểu khác được sử dụng có thể được tìm thấy trên kho lưu trữ Github .
Bước 3 - Tạo hoạt ảnh bằng Javascript
Ta sẽ sử dụng anime.js và segment.js , cả hai thư viện nhẹ để giúp đỡ với hình ảnh động.
Xin lưu ý ta sẽ không bao gồm một số khai báo biến trong các đoạn mã sau, vì mục đích rõ ràng. Nếu bạn có nghi ngờ nào, vui lòng kiểm tra kho lưu trữ Github .
Đây là mã cơ bản mà ta đang sử dụng để nắm bắt các sự kiện nhấp vào button
và thực hiện hành vi ta muốn:
// 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']});
}
Tiến trình hoạt ảnh đã được làm giả trong bản demo; đối với trường hợp sử dụng thực, nó sẽ được thay thế bằng dữ liệu tiến độ thực. Đây là chức năng xử lý tiến trình:
// 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});
}
Cuối cùng, đây là đoạn mã được sử dụng để thực hiện hoạt ảnh khi quá trình download đã hoàn tất, nơi hoạt ảnh quả bóng được kích hoạt và ta biến đổi các phần tử 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);
}
Kết luận
Bài viết này cho thấy các đoạn mã chính được sử dụng để tạo nút download này:
Bạn có thể chơi với DEMO trực tiếp hoặc nhận mã đầy đủ trên Github . Cũng xin lưu ý thành phần này chưa hoàn toàn sẵn sàng để production , vì nó cần dữ liệu tiến độ thực và một số cân nhắc về cách phần backend sẽ ảnh hưởng đến các tương tác vi mô.
Các tin liên quan
Cách hiển thị CSS trên server ứng dụng React2019-12-12
Cách tạo Thư viện ảnh cuộn vô hạn với React và CSS Grid
2019-12-12
Cách giải quyết tắc nghẽn CSS quy mô lớn với ITCSS và BEM
2019-12-12
Đặt, giãn cách và mật độ trong CSS Grid
2019-12-12
Hiểu tính cụ thể trong CSS
2019-09-03
CSS sẽ thay đổi Thuộc tính: Khi nào và Không sử dụng Nó
2019-08-26
Trình xử lý nhấp chuột chỉ dành cho CSS sử dụng: target Pseudo-Class (Không có JavaScript)
2019-08-22
Giới thiệu về Tailwind CSS
2019-08-13
Hiểu CSS Float
2019-06-07
Giới thiệu về Bulma CSS với React
2018-10-12