Transition (แอนิเมชันการเปลี่ยน) เป็นกลไกการเปลี่ยนแปลงทางภาพที่แทนที่เนื้อหาหนึ่งด้วยอีกเนื้อหาหนึ่ง: หน้าจอ แฟรกเมนต์ มุมมอง หรือสถานะอินเทอร์เฟซ การเปลี่ยนที่นำไปใช้อย่างถูกต้องจะชี้นำความสนใจของผู้ใช้ อธิบายความสัมพันธ์ระหว่างองค์ประกอบ และทำให้การนำทางเป็นไปอย่าง intuit ตาม Material Design Guidelines (2026) แอปที่มีการเปลี่ยนที่ออกแบบมาอย่างดีแสดงอัตราการรักษาผู้ใช้สูงขึ้น 22% ในเซสชันที่สอง เรียนรู้เพิ่มเติมเกี่ยวกับแอนิเมชันประเภทอื่นได้ใน คำแนะนำแอนิเมชันทั่วไป
ประเด็นสำคัญ
Transition คือแอนิเมชันที่เกิดขึ้นเมื่อแทนที่สถานะอินเทอร์เฟซภาพหนึ่งด้วยอีกสถานะหนึ่ง แตกต่างจาก Property Animation (การทำให้คุณสมบัติของวัตถุเดี่ยวเคลื่อนไหว) Transition จัดการชุดการเปลี่ยนแปลงทั้งหมด: การปรากฏ/หายไปขององค์ประกอบ การเปลี่ยนตำแหน่ง ขนาด และคุณสมบัติภายในหนึ่งการเปลี่ยน Transition แก้ปัญหาการเปลี่ยนเนื้อหาที่ราบรื่นเพื่อให้ผู้ใช้เข้าใจว่าอะไรเคลื่อนที่ไปที่ไหน
บนแพลตฟอร์มมือถือ Transition ใช้ในสามสถานการณ์หลัก: การนำทางระหว่างหน้าจอ (push, pop, modal present) การเปลี่ยนแฟรกเมนต์ (Android FragmentTransaction) และ การเปลี่ยนสถานะภายในหน้าจอ (การขยายการ์ด การเปลี่ยนแท็บ) แต่ละสถานการณ์ต้องใช้แนวทางของตนเอง: การเปลี่ยนนำทาง — UIViewControllerAnimatedTransitioning ใน iOS หรือ ActivityOptions ใน Android การเปลี่ยนภายในหน้าจอ — Transition Framework หรือ UIView.transition
ตาม Apple HIG (2026) ระยะเวลาของการเปลี่ยนมาตรฐานไม่ควรเกิน 400–500 มิลลิวินาที — แอนิเมชันที่ยาวกว่าจะถูกรับรู้ว่าล่าช้า Material Design แนะนำ 300–350 มิลลิวินาทีสำหรับการเปลี่ยนบนมือถือ สำหรับองค์ประกอบขนาดใหญ่ (การ์ด รูปภาพ) เวลาสามารถเพิ่มเป็น 500–600 มิลลิวินาทีโดยไม่สูญเสียประสิทธิภาพที่รับรู้ได้
ประเภทการเปลี่ยน แบ่งออกเป็นหลายหมวดหมู่ขึ้นอยู่กับว่าเนื้อหาใดเปลี่ยนแปลงและเปลี่ยนแปลงอย่างไร ประเภทพื้นฐาน: cross-fade — การหายไปอย่างราบรื่นของหนึ่งและการปรากฏของอีกหนึ่ง; slide — หน้าจอหนึ่งออกไปอีกหน้าจอหนึ่งเข้ามา; scale/zoom — องค์ประกอบขยายใหญ่ขึ้นเปิดหน้าจอรายละเอียด การเปลี่ยนแบบผสมผสานหลายเอฟเฟกต์เข้าด้วยกัน
| ประเภทการเปลี่ยน | คำอธิบาย | iOS API | Android API |
|---|---|---|---|
| Cross-fade | เฟด: เนื้อหาหนึ่งหายไป อีกเนื้อหาหนึ่งปรากฏ | UIView.transition(with:duration:options:.transitionCrossDissolve) | TransitionSet + Fade() |
| Slide | เลื่อน: เนื้อหาเข้า/ออกในแนวนอนหรือแนวตั้ง | CATransition(type: .push, subtype: .fromLeft) | Slide() / FragmentTransaction.setCustomAnimations |
| Scale / Zoom | ซูม: องค์ประกอบขยายเป็นเต็มหน้าจอ | UIViewPropertyAnimator พร้อมแอนิเมชันสเกล | Scale() + ActivityOptions.makeScaleUpAnimation |
| Explode | ระเบิด: เนื้อหาแตกเป็นชิ้นเมื่อหายไป | CATransition(type: .reveal) | Explode() (API 21+) |
| Fade-through | เฟดผ่านหน้าจอขาว/ดำ | UIView.transition(with: .transitionCrossDissolve, พื้นหลังสีขาว) | Fade() ผ่าน ChangeBounds |
กฎ Material Design: ใช้การเปลี่ยนประเภทเดียวภายในแอปเดียวเพื่อสร้างภาษาภาพที่ unified แนะนำ: slide สำหรับการนำทางระหว่างหน้าจอระดับเดียวกัน (master → detail), cross-fade สำหรับการเปลี่ยนเนื้อหาภายในหน้าจอเดียว (แท็บ การสลับมุมมอง), scale/zoom สำหรับการเปลี่ยนจากการ์ดไปยังหน้าจอรายละเอียด (การ์ดสินค้า → คำอธิบาย)
ใน iOS UIView.transition เป็นวิธีที่ง่ายที่สุดในการทำให้การเปลี่ยนแปลงเนื้อหาภายใน View เดียวเคลื่อนไหว รองรับ cross-dissolve, curl-up, curl-down, flip-from-left, flip-from-right สำหรับการเปลี่ยนนำทางระหว่าง UIViewController ให้ใช้ UINavigationControllerDelegate กับ UIViewControllerAnimatedTransitioning — ซึ่งให้การควบคุมแอนิเมชัน push/pop อย่างสมบูรณ์
import UIKit
// UIView.transition: เปลี่ยนรูปภาพด้วย cross-dissolve
UIView.transition(with: imageView,
duration: 0.4,
options: [.transitionCrossDissolve, .curveEaseInOut]
) {
self.imageView.image = UIImage(named: "new_image")
} completion: { _ in
print("Transition completed")
}
// UIView.transition: สลับสอง View
UIView.transition(from: firstView, to: secondView,
duration: 0.5,
options: [.transitionFlipFromRight, .showHideTransitionViews]
) { _ in
// firstView ซ่อน, secondView แสดง
}
// CATransition สำหรับเลเยอร์ (ไม่มี Auto Layout)
let transition = CATransition()
transition.type = .push
transition.subtype = .fromRight
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
view.layer.add(transition, forKey: "pushTransition")
UIViewPropertyAnimator เป็นทางเลือกสมัยใหม่สำหรับการเปลี่ยนแบบกำหนดเองบน iOS 10+ แตกต่างจาก UIView.transition PropertyAnimator รองรับการหยุดชั่วคราว การยกเลิก และแอนิเมชันย้อนกลับ ซึ่งสำคัญสำหรับการเปลี่ยนแบบโต้ตอบ (ปัดเพื่อกลับ) สำหรับแอนิเมชันระหว่าง UIViewControllers ให้ใช้ UIPercentDrivenInteractiveTransition ร่วมกับ PropertyAnimator — นี่คือรูปแบบมาตรฐานสำหรับการนำทางแบบโต้ตอบ
ใน Android FragmentTransaction กับ setCustomAnimations เป็นวิธีหลักในการทำให้การเปลี่ยนระหว่าง Fragment เคลื่อนไหว พารามิเตอร์: enter (แอนิเมชันเข้าของ Fragment ใหม่), exit (แอนิเมชันออกของ Fragment เก่า), popEnter และ popExit — สำหรับการนำทางกลับ FragmentTransaction รองรับทรัพยากรแอนิเมชัน (R.anim) และ Transition Framework (R.transition)
import androidx.fragment.app.FragmentTransaction
import androidx.transition.TransitionInflater
// FragmentTransaction พร้อมแอนิเมชันทรัพยากร
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // enter
R.anim.slide_out_left, // exit
R.anim.slide_in_left, // popEnter
R.anim.slide_out_right // popExit
)
.replace(R.id.container, DetailFragment())
.addToBackStack(null)
.commit()
// FragmentTransaction พร้อม Transition Framework
val detailFragment = DetailFragment()
detailFragment.enterTransition = TransitionInflater.from(this)
.inflateTransition(R.transition.slide_right)
detailFragment.exitTransition = TransitionInflater.from(this)
.inflateTransition(R.transition.fade_out)
supportFragmentManager.beginTransaction()
.replace(R.id.container, detailFragment)
.addToBackStack(null)
.commit()
// ActivityOptions: แอนิเมชันเมื่อเริ่ม Activity
val options = ActivityOptions.makeScaleUpAnimation(
view, 0, 0, view.width, view.height
).toBundle()
startActivity(intent, options)
แนวปฏิบัติที่ดีที่สุดของ Android: Slide() — สำหรับการแทนที่ Fragment ในการนำทาง (enter = Slide(Gravity.END), exit = Slide(Gravity.START)) Fade() — สำหรับการแสดง/ซ่อนโอเวอร์เลย์และไดอะล็อก Explode() — สำหรับการเปลี่ยนเน้นย้ำ (การลบรายการออกจากรายการ) สำหรับอุปกรณ์เก่า (API < 21) ให้ใช้ทรัพยากร R.anim เป็นตัวสำรอง Transition Framework ไม่รองรับ SurfaceView, TextureView, GLSurfaceView — สำหรับวิดีโอและกล้องให้ใช้แอนิเมชัน R.anim เท่านั้น
UIViewControllerAnimatedTransitioning เป็นโปรโตคอล iOS สำหรับสร้างแอนิเมชันการเปลี่ยนระหว่างคอนโทรลเลอร์แบบกำหนดเองได้ทั้งหมด ให้การเข้าถึงคอนเทนเนอร์วิว (transitionContext.containerView) วิวต้นทางและวิวเป้าหมาย เมื่อรวมกับ UIPercentDrivenInteractiveTransition วิธีการนี้ช่วยให้สร้างการเปลี่ยนแบบโต้ตอบด้วยการเลื่อนนิ้วได้
import UIKit
"> แอนิเมเตอร์การเปลี่ยนผ่านที่กำหนดเอง
class ScaleTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting: Bool
init(presenting: Bool) { self.isPresenting = presenting }
func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.4
}
func animateTransition(using ctx: UIViewControllerContextTransitioning) {
guard let toView = ctx.view(forKey: .to),
let fromView = ctx.view(forKey: .from)
else { return }
let container = ctx.containerView
let initialScale: CGAffineTransform = CGAffineTransform(scaleX: 0.8, y: 0.8)
let finalFrame = ctx.finalFrame(for: toView)
if isPresenting {
toView.transform = initialScale
toView.alpha = 0
toView.frame = finalFrame
container.addSubview(toView)
}
UIView.animate(withDuration: transitionDuration(using: ctx),
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.3) {
if self.isPresenting {
toView.transform = .identity
toView.alpha = 1
fromView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
fromView.alpha = 0
} else {
fromView.transform = initialScale
fromView.alpha = 0
}
} completion: { _ in
fromView.transform = .identity
ctx.completeTransition(!ctx.transitionWasCancelled)
}
}
}
// การประยุกต์ใช้ใน UINavigationControllerDelegate
extension MainViewController: UINavigationControllerDelegate {
func navigationController(
_ nc: UINavigationController,
animationControllerFor operation: UINavigationController.Operation,
from: UIViewController, to: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return ScaleTransitionAnimator(presenting: operation == .push)
}
}
การเปลี่ยนแบบโต้ตอบ: เพิ่ม UIPanGestureRecognizer ไปที่หน้าจอต้นทาง เมื่อเจสเจอร์ถูกกระตุ้น ให้สร้าง UIPercentDrivenInteractiveTransition และเรียก update(CGFloat(percent)) หากต้องการยกเลิกเมื่อไม่ถึงเกณฑ์ (โดยปกติ 30%) ให้เรียก cancel() รูปแบบนี้ใช้โดยค่าเริ่มต้นใน UINavigationController สำหรับเจสเจอร์ pop และใน UIScrollView ที่เปิด pagingEnabled สำหรับเจสเจอร์แบบกำหนดเอง ให้implement UIViewControllerInteractiveTransitioning
Transition Framework (android.transition) เป็นระบบแอนิเมชันแบบประกาศที่ทำให้การเปลี่ยนแปลงระหว่างสองซีน (Scene) เคลื่อนไหวโดยอัตโนมัติ ซีนคือชุดของ Views ที่มีสถานะเฉพาะ เมื่อเปลี่ยนจากซีน A ไปยังซีน B Transition Framework จะวิเคราะห์ความแตกต่างและทำให้เคลื่อนไหว: การเปลี่ยนตำแหน่ง (ChangeBounds) การเปลี่ยนขนาด (ChangeClipBounds, ChangeTransform) การปรากฏ (Fade) และการหายไป (Fade)
import android.transition.Scene
import android.transition.TransitionManager
import android.transition.TransitionSet
import android.transition.AutoTransition
import android.transition.ChangeBounds
import android.transition.Fade
// TransitionSet: การรวมกันของ ChangeBounds + Fade
val transition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(Fade(Fade.IN))
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
// การเปลี่ยนระหว่างซีนใน ViewGroup
val sceneRoot = findViewById<ViewGroup>(R.id.scene_root)
val scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_initial)
val scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_expanded)
"> AutoTransition โดยไม่ต้องตั้งค่าด้วยตนเอง
TransitionManager.go(scene2, AutoTransition().apply {
duration = 350L
})
// TransitionManager.beginDelayedTransition — ไม่มีซีน
TransitionManager.beginDelayedTransition(sceneRoot, ChangeBounds())
itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.requestLayout() "> การเปลี่ยนแปลง layout ถูกอนิเมตอัตโนมัติ
ChangeBounds เป็นการเปลี่ยนที่ใช้มากที่สุดใน Android ทำให้การเปลี่ยนตำแหน่งและขนาดของ View เคลื่อนไหว ChangeTransform ทำให้การหมุนและสเกลเคลื่อนไหว ChangeImageTransform — สำหรับแอนิเมชัน ImageView (centerCrop → fitCenter) สำหรับการเปลี่ยนที่ซับซ้อนที่มีการเปลี่ยนแปลงโครงสร้างลำดับชั้น ให้ใช้ TransitionSet กับ ordering = ORDERING_SEQUENTIAL (แอนิเมชันทำงานตามลำดับ ไม่ใช่แบบขนาน) ตามเอกสารของ Android Developers (2026) Transition Framework รองรับ Views มาตรฐานทั้งหมด แต่ไม่รองรับแอนิเมชันพื้นผิว (SurfaceView, TextureView, VideoView)
คำถามที่พบบ่อย
Animation — ทำให้คุณสมบัติของวัตถุเดี่ยวเคลื่อนไหว (alpha, translation, scale) Transition — ทำให้การเปลี่ยนแปลงของชุดวัตถุหรือสถานะเคลื่อนไหว (fragment → fragment, scene → scene) Transition จัดการการเปลี่ยนแปลงหลายอย่างพร้อมกัน: การปรากฏ การหายไป การเคลื่อนที่ การเปลี่ยนขนาด — และทำให้ความแตกต่างระหว่างสองสถานะเคลื่อนไหวโดยอัตโนมัติ Animation ต้องการการจัดการด้วยตนเองสำหรับแต่ละคุณสมบัติ
ใช้ FragmentTransaction.setCustomAnimations กับ Transition Framework: fragment1.exitTransition = Fade(Fade.OUT), fragment2.enterTransition = Slide(Gravity.END) สำหรับการจัดลำดับ: setReorderingAllowed(true) — แฟรกเมนต์จะถูกจัดเรียงใหม่เพื่อการเปลี่ยนแบบขนานที่ราบรื่น สำหรับกรณีง่าย ๆ slide_in_right / slide_out_left จาก R.anim ก็เพียงพอ สำหรับการนำทางกลับ ให้ระบุ popEnter และ popExit
Transition Framework ทำให้เฉพาะคุณสมบัติที่ไม่ก่อให้เกิดการคำนวณเลย์เอาต์ใหม่เคลื่อนไหว สำหรับแอนิเมชันความสูง ให้ใช้ ChangeBounds — มันจะทำให้การเปลี่ยนแปลงขอบเขต View ระหว่างสองสถานะเลย์เอาต์เคลื่อนไหวโดยอัตโนมัติ เรียก TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds()) ก่อนเปลี่ยน LayoutParams หากความสูงเปลี่ยนผ่าน WRAP_CONTENT ตรวจสอบให้แน่ใจว่า layout_height แตกต่างก่อนและหลัง — ChangeBounds จะไม่ทำงานหากทั้งสองสถานะเป็น match_parent
ใช้ modifier .transition() กับ AnyTransition: .transition(.move(edge: .trailing).combined(with: .opacity)) สำหรับการเปลี่ยนแบบกำหนดเอง ให้implement AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)) สำหรับแอนิเมชันภายใน NavigationStack ให้ใช้ .navigationTransition(.slide) ใน iOS 18+ SwiftUI ไม่รองรับ UIViewControllerAnimatedTransitioning — เฉพาะการเปลี่ยนแบบประกาศผ่าน AnyTransition
ใช้ Profile GPU Rendering (Android) หรือ Core Animation Instrument (iOS) สำหรับ Android ให้เปิด Transition Profiling ผ่าน adb shell setprop debug.transition.profiling 1 — บันทึกจะแสดงระยะเวลาของแต่ละขั้นตอนการเปลี่ยน ใน iOS ให้เปิด Slow Animations ในซิมิวเลเตอร์ ตัวชี้วัดเป้าหมาย: ระยะเวลา < 400 ms, FPS >= 55, การดรอปเฟรม — ไม่เกิน 2 ต่อการเปลี่ยน สำหรับซีนที่ซับซ้อนที่มี Views เคลื่อนไหวพร้อมกัน 15+ รายการ ให้ใช้ ChangeBounds ด้วยระยะเวลา 300 ms
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ