Điểm chính
Easing (hàm làm mịn) là một hàm toán học f(t) ánh xạ thời gian tuyến tính t ∈ [0,1] thành một giá trị hoạt hình phi tuyến. Nếu một hoạt hình thay đổi vị trí của đối tượng từ A đến B trong 500 ms, easing xác định cách đối tượng di chuyển giữa các điểm này tại mỗi thời điểm. Easing tuyến tính (f(t) = t) có nghĩa là tốc độ không đổi — đối tượng di chuyển một cách máy móc. Easing phi tuyến cảm giác tự nhiên: đối tượng tăng tốc (ease-in), giảm tốc (ease-out) hoặc có bắt đầu và kết thúc mượt mà (ease-in-out).
Các hàm easing được mô tả bằng đường cong Bézier bậc ba, trong đó trục X là thời gian được chuẩn hóa và trục Y là tiến trình được chuẩn hóa. Hai điểm điều khiển (P1, P2) xác định hình dạng đường cong. Các đường cong tiêu chuẩn được quy định bởi thông số kỹ thuật CSS: ease (0.25, 0.1, 0.25, 1.0), ease-in (0.42, 0.0, 1.0, 1.0), ease-out (0.0, 0.0, 0.58, 1.0), ease-in-out (0.42, 0.0, 0.58, 1.0). Các giá trị tương tự này được sử dụng trong CAMediaTimingFunction trên iOS và PathInterpolator trên Android. Theo Hướng dẫn Chuyển động Material Design, các đường cong easing phải phù hợp với ngữ cảnh: cho sự xuất hiện — ease-out (bắt đầu nhanh), cho sự biến mất — ease-in (kết thúc nhanh).
Trong iOS, easing hoạt động chủ yếu qua lớp CAMediaTimingFunction của Core Animation. Nó chấp nhận một đường cong Bézier bậc ba dưới dạng hai điểm điều khiển (c1x, c1y, c2x, c2y) và áp dụng cho CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction hỗ trợ 5 hàm định sẵn: linear, easeIn, easeOut, easeInEaseOut và default (tương đương easeInEaseOut).
CAMediaTimingFunction hoạt động ở cấp CALayer — hoạt hình chạy trong một tiến trình máy chủ kết xuất riêng, đảm bảo 60fps ngay cả với các biến đổi phức tạp. Đối với hoạt hình thuộc tính UIView (frame, alpha, transform), UIKit sử dụng UIView.animate với tham số options: .curveEaseInOut, bên trong cũng tạo một CAMediaTimingFunction với đường cong tương ứng. Trong iOS 17+, UIViewAnimationCurve.custom với BlockParameters cho Bézier tùy chỉnh đã được thêm vào — điều này loại bỏ nhu cầu viết CAAnimation tùy chỉnh. CAMediaTimingFunction cũng được sử dụng trong UIKit Dynamics và UIScrollView để giảm tốc (UIScrollViewDecelerationRate).
Trong SwiftUI, easing được thiết lập qua cấu trúc Animation với các bộ sửa đổi .easeIn, .easeOut, .easeInOut, .spring và .interpolatingSpring. Không giống UIKit, nơi hoạt hình là thủ tục (beginAnimations/commitAnimations), SwiftUI sử dụng cách tiếp cận khai báo: hoạt hình là một thuộc tính trạng thái thay đổi và khung làm việc tự xác định cách hoạt hình chuyển tiếp.
Bộ sửa đổi .timingCurve(c0x, c0y, c1x, c1y) chấp nhận các điểm điều khiển Bézier bậc ba trực tiếp — đây là một chức năng tương tự đầy đủ của CAMediaTimingFunction. SwiftUI Animation hỗ trợ các đường cong định sẵn dưới dạng thuộc tính tĩnh: Animation.easeInOut tương đương với ease-in-out (0.42, 0.0, 0.58, 1.0). Cho hoạt hình spring, Animation.spring(response:dampingFraction:blendDuration) được sử dụng, trong đó dampingFraction < 1 tạo hiệu ứng lò xo có giảm chấn. Tại WWDC 2023, Apple đã giới thiệu Animation.keyframes — hoạt hình từng khung hình với easing tùy chỉnh trên mỗi khung hình chính, mang lại toàn quyền kiểm soát quỹy đạo.
Trong Android, easing được triển khai qua PathInterpolator (API 21+) — một lớp chấp nhận đối tượng Path với một đường cong Bézier bậc ba. Các điểm điều khiển được chỉ định qua Path.cubicTo(x1, y1, x2, y2, x3, y3), trong đó (x1,y1) là điểm điều khiển đầu tiên, (x2,y2) là điểm thứ hai và (x3,y3) là điểm cuối (luôn là 1,1). PathInterpolator là bắt buộc nếu cần một đường cong tùy chỉnh; cho các kịch bản tiêu chuẩn, có sẵn AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) và DecelerateInterpolator (ease-out).
Trong Jetpack Compose, easing được thiết lập qua đối tượng Easing từ gói androidx.compose.animation. Có sẵn FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing, cũng như Easing tùy chỉnh qua CubicBezierEasing. Compose Easing được áp dụng trong animate*AsState, AnimatedVisibility và AnimatedContent. Sự khác biệt chính so với hệ thống View: Compose nội suy easing ở phía Kotlin (không phải trong RenderNode), mang lại sự linh hoạt hơn cho các hoạt hình tùy chỉnh nhưng yêu cầu tối ưu hóa trên màn hình 120Hz.
Hoạt hình vị trí lớp cơ bản với đường cong ease-in-out tùy chỉnh. CAMediaTimingFunction chấp nhận các điểm điều khiển Bézier bậc ba xác định sự bắt đầu và kết thúc mượt mà của chuyển động.
import UIKit
func animateLayerPosition() {
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = CGPoint(x: 0, y: 0)
animation.toValue = CGPoint(x: 300, y: 400)
animation.duration = 0.8
let easing = CAMediaTimingFunction(
controlPoints: 0.42, 0.0, 0.58, 1.0
)
animation.timingFunction = easing
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
layer.add(animation, forKey: "positionAnimation")
}
Các điểm điều khiển (0.42, 0.0, 0.58, 1.0) xác định đường cong ease-in-out tiêu chuẩn. SwiftUI sử dụng cùng các điểm cho Animation.easeInOut. Cho ease-in, sử dụng (0.42, 0.0, 1.0, 1.0); cho ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation thực thi hoạt hình trong máy chủ kết xuất ở 60/120 fps.
Hoạt hình khai báo trong SwiftUI với đường cong tùy chỉnh và hiệu ứng spring. Bộ sửa đổi hoạt hình liên kết với sự thay đổi của isAnimated.
import SwiftUI
struct EasingExampleView: View {
@State private var isAnimated = false
var body: some View {
VStack {
Circle()
.fill(Color.blue)
.frame(width: 60, height: 60)
.offset(x: isAnimated ? 100 : 0, y: 0)
.animation(
.timingCurve(0.42, 0.0, 0.58, 1.0, duration: 0.6),
value: isAnimated
)
Button("Toggle") {
isAnimated.toggle()
}
}
}
}
Bộ sửa đổi .timingCurve(c0x, c0y, c1x, c1y, duration) tạo một hoạt hình easing tùy chỉnh trong SwiftUI. Tham số value liên kết hoạt hình với một trạng thái cụ thể — khi isAnimated thay đổi, SwiftUI tự động áp dụng đường cong. Cho hoạt hình spring, sử dụng .spring(response: 0.4, dampingFraction: 0.6) thay vì timingCurve.
Tương đương ease-in-out trong Android qua PathInterpolator. Các điểm điều khiển (0.42, 0.0, 0.58, 1.0) xác định cùng một đường cong như trong iOS.
import android.animation.ObjectAnimator
import android.graphics.Path
import android.view.animation.PathInterpolator
import android.view.View
fun View.fadeInWithEase() {
val path = Path().apply {
cubicTo(0.42f, 0.0f, 0.58f, 1.0f, 1.0f, 1.0f)
}
val interpolator = PathInterpolator(path)
ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
duration = 500
interpolator = interpolator
start()
}
}
PathInterpolator với các điểm điều khiển (0.42, 0.0, 0.58, 1.0) giống hệt CAMediaTimingFunction ease-in-out trên iOS. Cho các dự án đa nền tảng, hãy sử dụng cùng các điểm điều khiển — điều này đảm bảo hành vi hoạt hình giống hệt về mặt trực quan. Material Design khuyến nghị FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) như một sự thay thế cho các giao diện dành riêng cho Android.
Trong Compose, easing được thiết lập qua CubicBezierEasing với các điểm điều khiển tương tự. Kết quả là sự xuất hiện mượt mà của phần tử với độ trong suốt và độ dịch chuyển.
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
@Composable
fun EasingComposable() {
var visible by remember { mutableStateOf(false) }
val easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f)
val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
animationSpec = tween(durationMillis = 500, easing = easing)
)
Column {
Button(onClick = { visible = !visible }) {
Text("Toggle")
}
Box(
modifier = Modifier
.size(100.dp)
.graphicsLayer(alpha = alpha)
.background(MaterialTheme.colorScheme.primary)
)
}
}
CubicBezierEasing trong Compose chấp nhận cùng 4 tham số điểm điều khiển. animateFloatAsState tự động nội suy giá trị alpha với đường cong được chỉ định. Để so sánh: FastOutSlowInEasing trong Compose tương đương với (0.4, 0.0, 0.2, 1.0) — đường cong tiêu chuẩn của Material Design 3. Các đường cong tùy chỉnh qua CubicBezierEasing mang lại toàn quyền kiểm soát cho các hoạt hình thương hiệu.
Câu hỏi thường gặp
Easing là một khái niệm chung về hàm làm mịn được sử dụng trong iOS (CAMediaTimingFunction), web (CSS ease-in-out) và thiết kế. Interpolator là sự triển khai của khái niệm này trong Android (TimeInterpolator). Về bản chất, chúng là cùng một thứ — một đường cong toán học của tốc độ hoạt hình. Sự khác biệt chỉ ở tên gọi và API nền tảng. Trong các dự án đa nền tảng, thuật ngữ easing được sử dụng như một thuật ngữ phổ quát.
iOS cung cấp CAMediaTimingFunction với 5 hàm định sẵn: linear, easeIn, easeOut, easeInEaseOut và custom (Bézier bậc ba). Trong SwiftUI, có sẵn Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring và Animation.interpolatingSpring. Cho các đường cong tùy chỉnh, hãy sử dụng Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).
Có, nếu cài đặt cùng một đường cong Bézier bậc ba. CAMediaTimingFunction trên iOS và PathInterpolator trên Android chấp nhận các điểm điều khiển Bézier bậc ba (x1, y1, x2, y2). Ví dụ, đường cong ease-in-out (0.42, 0.0, 0.58, 1.0) sẽ cho hành vi giống hệt trên cả hai nền tảng. Material Design sử dụng cách tiếp cận này cho các hoạt hình đa nền tảng.
Sử dụng Hướng dẫn Chuyển động Material Design: cho sự xuất hiện của phần tử — ease-out (bắt đầu nhanh, kết thúc mượt), cho sự biến mất — ease-in (kết thúc nhanh), cho chuyển động giữa các màn hình — ease-in-out. Sử dụng hoạt hình spring cho các phần tử tương tác (nút, thẻ). Tránh linear — nó trông không tự nhiên.
Giật (jank) xảy ra khi hoạt hình không đạt được 60fps. Hãy kiểm tra: bạn có đang sử dụng CAMediaTimingFunction (không phải timer) trong iOS, PathInterpolator (không phải Handler.post) trong Android không? Các tính toán nặng trên luồng chính, hệ thống phân cấp lớp phức tạp hoặc một drawable lớn có thể gây rụng khung hình. Trong iOS, hãy sử dụng Core Animation với shouldRasterize cho các lớp phức tạp; trong Android — Tăng tốc phần cứng (Hardware Acceleration) và kết xuất GPU.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm