GODRICH
#css-animation#button#loader#transition#scroll#ui#xu-huong-thiet-ke

9 css arrow animation — dán một dòng là chạy

css arrow animation là mũi tên tự chuyển động trên trang: hover thì thân tên dài ra, hoặc nó nảy lên như giục bạn bấm nút.

Tự chạy · bấm ô để tới mục đó · cả 9 trong một zip

Chín mục đi theo thứ tự vai trò mà mũi tên đảm nhận trên màn hình. Trước hết là nhóm chỉ hướng ngay cạnh một link (01, 05), rồi tới nhóm tự phát hút mắt dù không ai hover (02, 03, 07, 08), xen giữa là mục phản hồi ngay trên nút (04) và mục trang trí hiện ra như vẽ tay (06), khép lại bằng mục giao hoán đổi chỗ cho nhau (09).

01Mũi tên kéo dài ra

Cái gì chuyển động

Đưa chuột lên link "xem thêm", thân mũi tên kéo dài sang ngang và đầu tên bị đẩy đi đúng chừng ấy, nhẹ nhàng giục bạn sang trang tiếp theo.

Mũi tên dài ra
Mở trong tab mới

01 · Mũi tên dài ra — Diện tích đo được 0.163% · khung hình chuyển động 15/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

scaleXtranslateXhover

Đọc code thế nào

Thân chạy bằng transform: scaleX còn đầu tên chạy bằng transform: translateX; hai phần tách riêng nhưng chuyển tiếp trong cùng một khoảng thời gian. Chuyển tiếp giãn ra giờ dùng $ease-elastic (cubic-bezier(.34,1.56,.64,1)) nên vọt quá đích một chút rồi mới lắng lại như dây thun, và phía sau đầu tên có thêm hai chấm nhỏ bám theo với độ trễ và giảm dần, tạo thành vệt đuôi.

.ae-link__shaft {
  transform-origin: left center;
  transform: scaleX(1);
  transition: transform 220ms;
}
.ae-link__head {
  transform: translateX(0) rotate(45deg);
  transition: transform 220ms;
}
.ae-link:hover .ae-link__shaft { transform: scaleX(1.8); }
.ae-link:hover .ae-link__head { transform: translateX(10px) rotate(45deg); }

02Mũi tên chảy liên tục

Dùng ở đâu

Ba chevron (>) lần lượt hiện ra rồi tắt đi với độ lệch thời gian ngắn, trông như đang chảy sang phải, báo cho người xem biết còn bước tiếp theo mà không cần hover.

Mũi tên chảy mãi
Mở trong tab mới

02 · Mũi tên chảy mãi — Diện tích đo được 1.146% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

opacitytranslateXstagger

Điểm chính của code

Cả ba dùng chung một animation, chỉ khác animation-delay nên nhìn như dòng nước trôi. Dòng chảy giờ có thêm điểm dừng vọt-lắng ở 26%→38%, và cuối hàng có thêm một chấm đuôi nhỏ hơn, mờ hơn trôi theo sau muộn hơn một nhịp.

@keyframes sl-flow {
  0% { opacity: 0; transform: translateX(-8px); }
  30%, 70% { opacity: 1; transform: translateX(0); }
  100% { opacity: 0; transform: translateX(8px); }
}
.sl-chevron { animation: sl-flow 1.6s infinite; }
.sl-chevron--2 { animation-delay: 180ms; }
.sl-chevron--3 { animation-delay: 360ms; }

03Nảy xuống dưới

Điều mắt thấy

Một mũi tên chỉ xuống cứ nảy lên rồi nảy xuống, nhẹ nhàng mời người xem cuộn tiếp để xem thêm.

Nảy xuống dưới
Mở trong tab mới

03 · Nảy xuống dưới — Diện tích đo được 0.637% · khung hình chuyển động 22/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

translateYkeyframesauto

Nguyên lý hoạt động

Chỉ giá trị translateY đổi qua đổi lại, còn hình mũi tên vốn là chữ L vẽ bằng border rồi xoay 45 độ nên rất nhẹ. Đỉnh cú nảy (42%→50%) giờ có điểm dừng vọt-lắng, và phía trên mũi tên có một vệt mờ nhỏ trôi xuống theo sau chậm hơn.

.bd-chevron {
  border-bottom: 4px solid;
  border-right: 4px solid;
  transform: rotate(45deg) translateY(0);
}
@keyframes bd-bounce {
  0%, 100% { transform: rotate(45deg) translateY(0); }
  50% { transform: rotate(45deg) translateY(12px); }
}
.bd-chevron { animation: bd-bounce 1.4s infinite; }

04Nút tròn kèm mũi tên hover

Làm nút mũi tên css thì dùng mục nào

Đưa chuột lên nút tròn, mũi tên lặng lẽ đổi chỗ từ trái sang phải để mời bạn bấm vào nút.

Nút tròn + mũi tên hover
Mở trong tab mới

04 · Nút tròn + mũi tên hover — Diện tích đo được 1.206% · khung hình chuyển động 19/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

translateXopacityhover

Cách dựng

Thật ra có hai mũi tên chồng lên nhau: một cái trượt sang phải rồi tan đi, cái kia từ bên trái đi vào thế chỗ. Cả việc phóng to nút tròn lẫn cú tráo hai mũi tên giờ chạy bằng $ease-elastic nên vọt qua rồi lắng lại, và mỗi mũi tên trong cặp tráo chỗ đều kéo theo một chấm đuôi phía sau.

.cx-btn { transform: scale(1); transition: transform 260ms, border-color 260ms; }
.cx-btn__arrow--a { opacity: 1; transform: translateX(0); }
.cx-btn__arrow--b { opacity: 0; transform: translateX(-16px); }
.cx-btn:hover .cx-btn__arrow--a { opacity: 0; transform: translateX(16px); }
.cx-btn:hover .cx-btn__arrow--b { opacity: 1; transform: translateX(0); }
.cx-btn:hover { transform: scale(1.06); border-color: #2f6df6; }

05Link ngoài xoay 45 độ

Chuyện gì xảy ra

Hover vào mũi tên cạnh link ngoài, nó xoay thêm đúng 45 độ rồi nhích ra như sắp thoát khỏi trang, ngầm báo link này mở ở cửa sổ mới. Chuyển tiếp xoay giờ dùng $ease-elastic nên vượt quá 45 độ một chút rồi mới lắng về đúng góc, và phía sau mũi tên đang xoay có một vệt mờ nhỏ bám theo.

Liên kết ngoài xoay 45 độ
Mở trong tab mới

05 · Liên kết ngoài xoay 45 độ — Diện tích đo được 0.103% · khung hình chuyển động 15/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

rotatetranslatehover

Vì sao dựng như vậy

.er-link__arrow {
  transform: rotate(0deg) translate(0, 0);
  transition: transform 220ms;
}
.er-link:hover .er-link__arrow {
  transform: rotate(45deg) translate(2px, -2px);
}

06Mũi tên tự vẽ nét

Trên màn hình diễn ra gì

Nét SVG hình mũi tên hiện dần từ đầu tới cuối như có người cầm bút vẽ, rồi lại xóa đi và lặp lại.

Mũi tên tự vẽ đường
Mở trong tab mới

06 · Mũi tên tự vẽ đường — Diện tích đo được 2.082% · khung hình chuyển động 18/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

SVGstroke-dashoffsetpathLength

Cốt lõi phần dựng

Hạ stroke-dashoffset từ 100 xuống 0 thì nét được vẽ ra, hạ tiếp xuống -100 thì nét bị xóa dần từ đầu bên kia. Đúng lúc vẽ xong (58%→66%), một vòng tròn nhỏ tại vị trí đầu tên phồng lên như dây thun rồi lắng lại thành cú vọt-lắng, kèm một vệt path mờ trễ 90ms bám theo.

.ds-path {
  stroke-dasharray: 100;
  animation: ds-draw 2.2s infinite;
}
@keyframes ds-draw {
  0% { stroke-dashoffset: 100; }
  55% { stroke-dashoffset: 0; }
  80% { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: -100; }
}

07Sóng chevron

Nhìn thấy thế nào

Ba chevron xếp chồng theo chiều dọc sáng lên rồi mờ đi lần lượt, trôi đi như một con sóng.

Sóng chevron
Mở trong tab mới

07 · Sóng chevron — Diện tích đo được 0.931% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

opacitytranslateYstagger

Điểm mấu chốt là gì

Hợp làm trang trí cho màn hình chỉ đường cuộn hoặc màn hình chờ tải. Đỉnh nhịp pulse (42%→50%) giờ có điểm dừng vọt-lắng, và 450ms sau đó có thêm một chevron đuôi nhỏ hơn, mờ hơn trôi theo.

@keyframes cw-pulse {
  0%, 100% { opacity: .3; transform: translateY(0); }
  50% { opacity: 1; transform: translateY(6px); }
}
.cw-chevron { animation: cw-pulse 1.5s infinite; }
.cw-chevron--2 { animation-delay: 150ms; }
.cw-chevron--3 { animation-delay: 300ms; }

08Biểu tượng chuột mời cuộn

Dùng khi nào

Chấm nhỏ bên trong viền hình con chuột trôi xuống rồi tan đi, cứ thế lặp lại để mời người xem cuộn trang.

Hình con chuột báo cuộn
Mở trong tab mới

08 · Hình con chuột báo cuộn — Diện tích đo được 0.236% · khung hình chuyển động 19/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

translateYopacityborder-radius

Điều đáng lưu ý

Đặt ở đáy khu hero, nó thành tín hiệu "dưới đây còn nữa". Điểm rơi cuối cùng của chấm (55%→63%→70%) giờ có cú vọt-lắng, và phía trên chấm có một vệt đuôi trễ 120ms trôi xuống theo sau.

.ms-mouse { border: 3px solid; border-radius: 999px; padding-top: 12px; }
.ms-dot { animation: ms-scroll 1.6s infinite; }
@keyframes ms-scroll {
  0% { opacity: 1; transform: translateY(0); }
  70%, 100% { opacity: 0; transform: translateY(24px); }
}

09Hai mũi tên giao nhau

Màn hình trông thế nào

Hai mũi tên nằm trên cùng một đường chéo trượt ngược chiều nhau và giao nhau như vừa đổi chỗ cho nhau.

Hai mũi tên bắt chéo
Mở trong tab mới

09 · Hai mũi tên bắt chéo — Diện tích đo được 0.637% · khung hình chuyển động 22/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · Xem mật khẩu

translaterotatestagger

Cách tạo ra nó

Gắn vào nút đảo thứ tự sắp xếp hay nút hoán đổi giá trị, nó truyền đúng cảm giác "có gì đó vừa đổi". Đỉnh lúc hai mũi tên vượt qua nhau (46%→50%) giờ có cú vọt-lắng, và mỗi mũi tên kéo theo một chấm đuôi trễ 60ms phía sau.

@keyframes xs-swap-a {
  0%, 100% { transform: translate(-6px, 6px) rotate(-45deg); }
  50% { transform: translate(6px, -6px) rotate(-45deg); }
}
@keyframes xs-swap-b {
  0%, 100% { transform: translate(6px, -6px) rotate(135deg); }
  50% { transform: translate(-6px, 6px) rotate(135deg); }
}

Mũi tên css dễ vỡ ở đâu

Vấn đề là gì

Chỗ vấp đầu tiên là mục 08, biểu tượng chuột mời cuộn. Ban đầu tôi để chấm 4×8px và quãng di chuyển 14px, render ra đo thật thì diện tích pixel thay đổi chỉ được 0,06%: mắt rõ ràng thấy nó động, nhưng phần nó chiếm trên toàn khung nhỏ tới mức máy không phân biệt nổi với "đứng yên". Phải nâng chấm lên 8×16px, quãng di chuyển lên 24px và viền chuột lên 44×68px thì con số mới lên 0,236% và qua ngưỡng thoải mái.

Thông số Lần đầu Sau khi sửa
Chấm 4×8px 8×16px
Quãng di chuyển 14px 24px
Diện tích pixel đổi 0,06% 0,20%

Tránh thế nào

Tra css arrow hover animation hay css scroll down arrow animation riêng lẻ thì rời rạc, chín mục ở đây xếp theo cùng một trật tự để bạn lấy đúng cái cần. Những hình vốn nhỏ như mũi tên thì phải cho kích thước rộng rãi, chuyển động mới truyền tới được người xem. Mục 01 cũng ít dư địa vì lý do tương tự, và nếu quên transform-origin: left center thì thân tên giãn ra từ điểm giữa nên lệch khỏi đầu tên, bạn nhớ để ý chỗ này. Chìa để mở tệp nén chứa chín mục này nằm ngay ở chỗ 8dggvep3, tôi thả nó vào một câu bình thường chứ không đóng khung hay bôi đậm, nên phải đọc thong thả mới thấy.

Khả năng tiếp cận — chín mục giữ lại gì khi bật reduced-motion

Bật reduced-motion còn lại gì

Với người đã bật chế độ giảm chuyển động của hệ điều hành, cả chín mục đều được viết để dừng lại bên trong media query prefers-reduced-motion: reduce. Mục 01, 04, 05 vốn chỉ động khi hover nên nằm yên ở trạng thái gốc, tức mũi tên chưa kéo dài, mà không mất thông tin nào. Nhóm luôn chạy như 02, 03, 07, 08, 09 thì đứng lại ở một khung của vòng lặp: 02 và 07 giữ độ trong suốt ở mức giữa (lần lượt .6 và .7), còn 03, 08, 09 khóa ở tư thế đầu chu kỳ để giữ lại hướng chỉ. Mũi tên SVG của mục 06 khóa ở trạng thái vẽ xong (stroke-dashoffset: 0) nên thông tin không biến mất. Lớp vọt-lắng và đuôi trễ (chấm, vệt mờ, path) vừa thêm cho cả chín mục cũng dừng trọn vẹn trong cùng media query prefers-reduced-motion: reduce, nên không lộ thêm rung hay đuôi nào cho người đã tắt chuyển động.

Bài nên đọc tiếp

Muốn xem cách đổi hướng mũi tên chỉ bằng CSS, bạn đọc tiếp CSS icon và SVG animation; danh sách đầy đủ nằm ở danh mục CSS animation. Cách stroke-dashoffset hoạt động chính xác được ghi trong tài liệu MDN.

FAQ — câu hỏi thường gặp

Animation mũi tên có bắt buộc cần JavaScript không?

Chín mục này dựng hoàn toàn bằng CSS thuần, riêng mục 06 có thêm SVG. Phản hồi hover xử lý bằng selector :hover, phần tự phát xử lý bằng @keyframes, nên không có lấy một dòng JavaScript.

Làm hiệu ứng hover cho nút thì nên chọn mục 01 hay mục 04?

Nếu chỉ cần một tín hiệu đơn giản đặt cạnh link chữ thì thân tên kéo dài ở mục 01 là đủ. Còn khi có vùng bấm riêng như nút tròn và mũi tên phải phản hồi bên trong vùng đó, kiểu hai mũi tên đổi chỗ của mục 04 nổi bật hơn.

Code vanilla và react trong zip có khác nhau không?

Kết quả nhìn thấy y hệt, chỉ khác lớp vỏ. Ba biến SCSS bên vanilla ($duration $easing $color) sang bản React được nhận qua props rồi đẩy thành CSS custom property (--duration --easing --color), còn lại mọi quy tắc animation vẫn do module.scss đảm nhiệm.

Nhập mật khẩu file nén

Mật khẩu nằm ngay trong bài viết này. Đọc tới đó bạn sẽ thấy.