Page Transition With Server Side Rendering
Khi xem demo ở phía trên mọi người thường nghĩ là chỉ những website CSR (Client Side Rendering) mới có thể chuyển trang như vậy thôi đúng không ?
Nhưng hôm nay như tiêu đề chúng ta sẽ hiện thực nó các ở website SSR (Server Side Rendering) luôn nhé 😼.
# Bắt đầu.
Chúng ta cần gì ?
- Jquery (quá quen thuộc rồi)
- Gsap (thư viện về animation cực kì mạnh mẽ)
Trước hết chúng ta sẽ có một source đơn giản thôi.
├── images
│ └── xxxxxx
├── main.js
├── style.css
└── index.html
└── about.html
└── product.html
Các page sẽ có chung cấu trúc như sau:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><!-- Tên trang --></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="wrapper">
<div class="content">
<!-- Nội dung -->
</div>
<ul class="nav-list">
<li><a href="/">Trang chủ</a></li>
<li><a class="active" href="about.html">Về chúng tôi</a></li>
<li><a href="product.html">Sản phẩm</a></li>
</ul>
<div class="overlay"></div>
</main>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="main.js"></script>
</html>
Phần css đơn giản một chút cho đời thêm đẹp
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
min-height: 100vh;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
-ms-overflow-style: none;
scrollbar-width: none;
}
html::-webkit-scrollbar,
body::-webkit-scrollbar {
display: none;
}
html.transition,
.transtion body {
overflow: hidden;
}
.overlay {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
opacity: 0;
z-index: 1;
background: rgba(0, 0, 0, 0.5);
pointer-events: none;
}
.nav-list {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
bottom: calc(env(safe-area-inset-bottom) + 2rem);
left: 50%;
transform: translateX(-50%);
list-style: none;
padding: 1rem 1.6rem;
border-radius: 3rem;
background: #fff;
border: 1px solid #232323;
z-index: 99;
transition: 0.3s;
}
.nav-list li:nth-child(2) {
margin: 0 1rem;
}
.nav-list a {
text-decoration: none;
display: block;
text-transform: uppercase;
font-size: 0.8rem;
color: #232323;
position: relative;
width: fit-content;
min-width: max-content;
}
.nav-list a::before {
content: "";
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 1px;
background: #232323;
transform: scaleX(0);
transform-origin: right;
transition: transform 0.6s;
}
.nav-list a.active::before,
.nav-list a:hover::before {
transform: scaleX(1);
transform-origin: left;
}
.content {
background: #fff;
min-height: 100vh;
}
.content-inner {
padding: 0.5rem 0.5rem 5rem;
}
.content h1 {
line-height: 1.3;
font-size: 4rem;
margin-bottom: 1rem;
}
.content h3 {
margin: 0.5rem 0;
}
.content ul {
margin-left: 2rem;
margin-bottom: 1rem;
}
.content p {
max-width: 80%;
font-size: 1rem;
margin-left: 3rem;
padding-bottom: 1.5rem;
}
.content img {
display: block;
width: auto;
height: auto;
object-fit: cover;
max-width: 100%;
margin: 0 auto;
border-radius: 0.5rem;
overflow: hidden;
}
.content.sec {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 4;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
}
.grid .grid-item {
text-align: center;
position: relative;
}
@media screen and (min-width: 768px) {
.nav-list {
padding: 1.5rem 4rem;
width: auto;
}
.nav-list li:nth-child(2) {
margin: 0 3rem;
}
.grid {
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.content {
padding: 2rem;
padding-bottom: 9rem;
}
.content h1 {
font-size: 7rem;
}
.content p {
font-size: 2rem;
}
}
# Logic
Về phần logic cũng khá đơn giản như đang giỡn vậy thôi. Mỗi khi user
click vào đường dẫn qua trang khác, chúng ta sẽ gọi ajax với trang đó, lấy nội dung content rồi append vào trang hiện có và thực hiện animation blalalala, khi animation chạy xong thì redirect qua trang đó thôi.
# Bắt tay vào code.
Ở file main.js
chúng ta sẽ viết như này.
Trước tiên khi page được load, chúng ta sẽ tạo một biến là nextLink
dùng để lưu đường dẫn của trang mà user
click vào.
Và thêm class first
cho content của trang hiện tại để phân biệt với content của trang được ajax
load vào.
let nextLink
$(".content").addClass("first")
Tiếp theo chúng ta sẽ tạo một function
để thực hiện animation với Gsap, khá đơn giản.
function pageTransition() {
$("html").addClass("transition")
let tl = gsap.timeline({
onComplete: updatePage
})
tl.from(".content.sec", {
y: "110vh",
delay: 0.2,
duration: 0.8,
ease: "power4.out"
})
tl.to(
".overlay",
{
opacity: 1,
duration: 0.3,
ease: "power1.out"
},
0
)
tl.to(
".content.first",
{
scale: 0.95,
duration: 0.3,
ease: "power1.out"
},
0
)
}
Tiếp tục tạo một function
để update lại link khi thực hiện xong animation. Lại càng đơn giản nữa.
function updatePage() {
window.location = nextLink
}
Chúng ta sẽ bắt sự kiện khi user
click vào menu và thực hiện gọi ajax
để lấy html của trang đó rồi append vào trang hiện tại. Khi hoàn thành thì gọi đến function pageTransition
ở trên là xong.
$(".nav-list a:not(.active)").on("click", function (e) {
e.preventDefault()
nextLink = this.href
$.ajax({
url: this.href,
success: function (data) {
let el = $(data).find(".content").addClass("sec")
$(".wrapper").append(el)
},
complete: function () {
pageTransition()
}
})
})
Cuối cùng main.js
sẽ như sau.
let nextLink
$(".content").addClass("first")
function updatePage() {
window.location = nextLink
}
function pageTransition() {
$("html").addClass("transition")
let tl = gsap.timeline({
onComplete: updatePage
})
tl.from(".content.sec", {
y: "110vh",
delay: 0.2,
duration: 0.8,
ease: "power4.out"
})
tl.to(
".overlay",
{
opacity: 1,
duration: 0.3,
ease: "power1.out"
},
0
)
tl.to(
".content.first",
{
scale: 0.95,
duration: 0.3,
ease: "power1.out"
},
0
)
}
$(".nav-list a:not(.active)").on("click", function (e) {
e.preventDefault()
nextLink = this.href
$.ajax({
url: this.href,
success: function (data) {
let el = $(data).find(".content").addClass("sec")
$(".wrapper").append(el)
},
complete: function () {
pageTransition()
}
})
})
Vậy là đã xong, cùng xem thành quả nào.
Ok vậy là đã xong rồi. Mượt như sunsilk 😼😼😼.
# Kết thúc.
Vậy là chúng ta đã hoàn thành xong rồi.
Cảm ơn mọi người đã dành thời gian đọc.
Mọi người có thể tham khảo code ở đây và demo ở đây