Pinch-to-Zoom — การทำความเข้าใจท่าหยิบในแอปพลิเคชันมือถือ

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-27 เวลาอ่าน: 8 นาที
Pinch-to-Zoom (การหยิบนิ้ว) เป็นท่าภาษ์การซูมแบบมัลติทัชที่ผู้ใช้นำนิ้วสองนิ้วเข้าหากันหรือก้างออกบนหน้าจอเพื่อย่อหรือขยายเนื้อหา ท่าภาษ์นี้ได้กลายเป็นมาตรฐานโดยพุดแสละสำหรับการดูรูปภาพแผนที่หน้าเว็บและเอกสารบนอุปกรณ์มือถือ ตาม Apple HIG และ Google Material Design Pinch-to-Zoom เป็นท่าภาษ์พื้นฐานที่ผู้ใช้ทุกคนคาดหวังในทุกแอปพลิเคชั่นที่ทำงานกับเนื้อหาทางภาพ

ประเด็นสำคัญ

  • Pinch-to-Zoom เป็นท่าภาษ์แบบมัลติทัชสองนิ้วสำหรับการขยายหรือย่อขนาดเนื้อหาบนอุปกรณ์มือถือ
  • ใน iOS การจัดการทำผ่าน UIPinchGestureRecognizer ด้วยการติดตามแบบต่อเนื่องของ .began → .changed → .ended
  • Android ใช้ ScaleGestureDetector (จาก Support Library) กับคอลแบ็ค onScale เพื่ออัพเดตการซูมผ่าน Matrix
  • UIPinchGestureRecognizer มีคุณสมบัติ scale (อัตราส่วนสัมพันธ์กับสถานะก่อนหน้า) และ velocity (ความเร็ว)
  • เพื่อจำกัดการซูม จะใช้การคลัมป์ตัวประกอบมาตรฐานระหว่างค่า min/max ที่กำหนด (โดยปกติ 0.5–3.0)

Pinch-to-Zoom คืออะไร?

Pinch-to-Zoom (การหยิบ) เป็นท่าภาษ์แบบมัลติทัชที่ใช้สองนิ้วเพื่อเปลี่ยนมาตรฐานของเนื้อหาที่แสดงผล เมื่อนำนิ้วเข้าหากัน (pinch close) มาตรฐานจะลดลง เมื่อก้างออก (pinch open) จะเพิ่มขึ้น ท่าภาษ์นี้เป็นแบบต่อเนื่อง: ระบบติดตามระยะห่างระหว่างนิ้วแบบเรียลไทม์และสร้างเหตุการณ์ข้ั้นกลาง ทำให้แอปพลิเคชั่นสามารถอัพเดตมาตรฐานได้อย่างลื่นไหล

กลไกของท่าภาษ์อึงอิงการคำนวณระยะห่างระหว่างสองจุดสัมผัส iOS และ Android ใช้ระยะห่างแบบยุคลิด: sqrt((x2−x1)² + (y2−y1)²) มาตรฐานปัจจุบันคำนวณเป็นอัตราส่วนระหว่างระยะห่างปัจจุบันกับระยะห่างเริ่มต้น หากระยะห่างเริ่มต้นเป็น 100px และกลายเป็น 150px — scale = 1.5 (เพิ่มขึ้น 50%) iOS ส่งมาตรฐานที่สัมพันธ์กับสถานะก่อนหน้า (เดลต้า) Android ส่งมาตรฐานสะสมผ่าน ScaleGestureDetector

ตาม Google Material Design Guidelines การใช้งาน Pinch-to-Zoom เป็นข้อบังคับสำหรับแอปพลิเคชั่นที่แสดงรูปภาพ แผนที่ หรือเอกสาร ที่ IT Sectr เรานำไปใช้ท่าภาษ์นี้กับ UIPinchGestureRecognizer และ ScaleGestureDetector ในทุกโปรเจกต์ที่ทำงานกับเนื้อหาทางภาพ — ตั้งแต่แกลเลอรีจนถึงโปรแกรมแก้ไขรูปภาพ

Pinch-to-Zoom ใน iOS: UIPinchGestureRecognizer

UIPinchGestureRecognizer เป็นคลาสย่อแบบต่อเนื่องของ UIGestureRecognizer สำหรับการรู้จักท่าภาษ์การหยิบ ต่างจากตัวรู้จักแบบไม่ต่อเนื่อง (UITapGestureRecognizer) เราไม่เป็นต้องการ มันสร้างลำดับของเหตุการณ์: .began (นิ้วสัมผัสหน้าจอ) .changed (แต่ละการเคลื่อนไหวของนิ้ว) .ended (นิ้วถูกยกออก)

คุณสมบัติหลัก: scale (CGFloat) — การเปลี่ยนแปลงสัมพันธ์ของระยะห่างระหว่างนิ้วตั้งแต่เริ่มต้นท่าภาษ์ velocity (CGFloat) — อัตราการเปลี่ยนแปลงมาตรฐานต่อวินาที scale เริ่มต้น = 1.0 เมื่อก้างนิ้วออก scale เพิ่มขึ้น (1.0 → 1.5 → 2.0) เมื่อนำเข้าหากัน จะลดลง (1.0 → 0.8 → 0.5) คุณสมบัติ velocity ใช้สำหรับการเคลื่อนที่ตามแรงเชือง: หลัง .ended คุณสามารถเคลื่อนไหวการขยายขนาดต่อไปแบบค่อยๆ หลังเบา

UIPinchGestureRecognizer รองรับคุณสมบัติ delegate สำหรับการรู้จักพร้อมกันกับตัวรู้จักอื่นๆ (เช่น pan + pinch บนแผนที่) เมธอด gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) ส่งคืน true และท่าภาษ์ทั้งสองทำงานในแบบขนาน Apple แนะนำให้เพิ่ม UIPinchGestureRecognizer ไปยังคอนเทนเนอร์วิว แทนที่จะเป็นองค์ประกอบแต่ละตัว — ซึ่งช่วยให้การคำนวณจุดหมุนของการขยายง่ายขึ้น

Pinch-to-Zoom ใน Android: ScaleGestureDetector

ScaleGestureDetector เป็นคลาสจาก Android Support Library (android.view) ที่ออกแบบมาเพื่อรู้จักการขยายขนาดแบบมัลติทัช ต่างจาก GestureDetector.onFling ScaleGestureDetector ถูกสร้างขึ้นโดยเฉพาะสำหรับการซูมด้วยสองนิ้วและคำนวณ focusX focusY (จุดกึ่งกลางระหว่างนิ้ว) และ scaleFactor (เดลต้ามาตรฐานจากเหตุการณ์ก่อนหน้า) โดยอัตโนมัติ

ScaleGestureDetector ทำงานผ่านอินเทอร์เฟส OnScaleGestureListener ด้วยสามเมธอด: onScaleBegin(detector) (เสมือน .began) onScale(detector) (เสมือน .changed ส่งคืน boolean — true หากยอมรับมาตรฐาน) onScaleEnd(detector) (เสมือน .ended) คุณสมบัติ: scaleFactor — ตัวคูณมาตรฐานที่สัมพันธ์กับเหตุการณ์ก่อนหน้า (~1.01 สำหรับการก้างช้าๆ) focusX/focusY — พิกัดของจุดกึ่งกลางระหว่างนิ้ว

เพื่อใช้การขยายขนาดกับ View โดยปกติจะใช้ Matrix: view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY) Matrix รองรับการสะสมการแปลงรูป — แต่ละการเรียก postScale จะเพิ่มการแปลงรูปใหม่ใส่การแปลงรูปที่มีอยู่ สิ่งสำคัญที่ต้องจำ: ScaleGestureDetector ไม่จำกัดมาตรฐานต่ำสุด/สูงสุด — นั่นคือหน้าที่ของนักพัฒนา ที่ IT Sectr เราใช้ ScaleGestureDetector กับขอบเขตมาตรฐานที่กำหนดเอง (0.5–3.0) และแอนิเมชั่นการกลับสู่สถานะเดิมเมื่อเกินขอบเขต

การจำกัดการซูมและจุดหมุน

เมื่อนำ Pinch-to-Zoom ไปใช้ การจำกัดมาตรฐานต่ำสุดและสูงสุดเป็นสิ่งสำคัญยิ่ง — หากไม่มี ผู้ใช้สามารถซูมเข้าไปจนอนันต์หรือซูมออกจนมองไม่เห็น ค่ามาตรฐาน: min = 0.5 (50% ของขนาดเดิม) max = 3.0 (300%) ใน iOS การจำกัดทำผ่าน clamp: newScale = min(max(currentScale * gesture.scale, minScale), maxScale) ใน Android — ผ่านการตรวจสอบหลัง postScale

จุดหมุนคือจุดกึ่งกลางระหว่างนิ้ว ซึ่งการขยายขนาดจะดำเนินการสัมพันธ์กับ iOS คำนวณจุดเคนตรวรีย์ของสองการสัมผัสโดยอัตโนมัติและส่งผ่าน location(in:) — นักพัฒนาไม่ต้องคำนวณจุดหมุนด้วยตนเอง Android ให้ focusX/focusY ใน ScaleGestureDetector แต่เมื่อใช้งานผ่าน Matrix จุดหมุนจะถูกส่งเป็นอาร์กิวเมนที่สามและสี่ใน postScale(scaleFactor, scaleFactor, focusX, focusY) หากไม่มีจุดหมุน การขยายขนาดจะดำเนินการสัมพันธ์กับมุมบนซ้าย (0,0) ทำให้เนื้อหาเคลื่อนที่ในระหว่างการซูม

จากประสบการณ์ของ IT Sectr การคำนวณจุดหมุนที่ถูกต้องเป็นแหล่งของข้อผิดพลาดที่พบบ่อยที่สุดเมื่อนำ Pinch-to-Zoom ไปใช้ ใน 90% ของกรณี การใช้พิกัดในตัวของ GestureRecognizer และ ScaleGestureDetector ก็เพียงพอ — ไม่ต้องคำนวณจุดหมุนด้วยตนเอง หากเนื้อหาเคลื่อนที่ในระหว่างการขยายขนาด ให้ตรวจสอบว่าคุณกำลังส่ง focusX/focusY ไปยัง matrix.postScale() หรือไม่

แรงเชืองหลังการซูม

สำหรับ UX ที่ลื่นไหลหลังจากท่าภาษ์สิ้นสุด คุณสามารถเพิ่มแรงเชืองได้: ใน iOS — ใช้ velocity จาก UIPinchGestureRecognizer กับ UIView.animate และพารามิเตอร์ spring ใน Android — OverScroller หรือ ValueAnimator แรงเชืองไม่ควรเกินขอบเขตมาตรฐาน min/max — ค่าสุดท้ายจะถูกตัดด้วยแอนิเมชั่นการกลับคืนแบบ spring เมื่อเกินขอบเขต

ตัวอย่างโค้ดใน Swift และ Kotlin

ตัวอย่างที่ 1: Pinch-to-Zoom บน iOS (Swift)

เพิ่ม UIPinchGestureRecognizer ไปยัง UIImageView ด้วยการจำกัดมาตรฐานตั้งแต่ 0.5 ถึง 3.0 แสดงการนำไปใช้ที่น้อยที่สุดโดยการรักษามาตรฐานสะสม

swift
import UIKit

class ZoomableImageViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!
    private var currentScale: CGFloat = 1.0
    private let minScale: CGFloat = 0.5
    private let maxScale: CGFloat = 3.0

    override func viewDidLoad() {
        super.viewDidLoad()

        let pinch = UIPinchGestureRecognizer(
            target: self,
            action: #selector(handlePinch(_:))
        )
        imageView.addGestureRecognizer(pinch)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handlePinch(_: UIPinchGestureRecognizer) {
        if gestureRecognizer.state == .began
            || gestureRecognizer.state == .changed {

            let newScale = currentScale * gestureRecognizer.scale
            imageView.transform = CGAffineTransform(
                scaleX: min(max(newScale, minScale), maxScale),
                y: min(max(newScale, minScale), maxScale)
            )
        }
        if gestureRecognizer.state == .ended {
            currentScale = min(max(
                currentScale * gestureRecognizer.scale,
                minScale
            ), maxScale)
            gestureRecognizer.scale = 1.0
        }
    }
}

ตรรกะ: ใน .began/.changed มาตรฐานจะถูกนำไปใช้ผ่าน CGAffineTransform ด้วยการคลัมป์ ใน .ended scale จะถูกรีเซ็ตเป็น 1.0 และค่าสะสมจะถูกเก็บไว้ใน currentScale ซึ่งป้องกันการเลื่อนไหลของมาตรฐานในระหว่างท่าภาษ์ซ้ำๆ — แต่ละท่าภาษ์ใหม่จะเริ่มจากสถานะปัจจุบัน สำหรับรูปภาพ แนะนำ UIScrollView ด้วย minimumZoomScale/maximumZoomScale แทน CGAffineTransform

ตัวอย่างที่ 2: Pinch-to-Zoom บน Android (Kotlin)

นำไปใช้การขยายขนาด ImageView ผ่าน ScaleGestureDetector กับ Matrix แสดงการจัดการจุดหมุนที่ถูกต้องและการจำกัดมาตรฐาน

kotlin
import android.content.Context
import android.graphics.Matrix
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.widget.ImageView

class PinchZoomImageView(context: Context) : ImageView(context) {

    private val matrix = Matrix()
    private var currentScale = 1f
    private val minScale = 0.5f
    private val maxScale = 3.0f

    private val scaleDetector =
        ScaleGestureDetector(context, ScaleListener())

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            currentScale = (currentScale * detector.scaleFactor)
                .coerceIn(minScale, maxScale)
            matrix.postScale(
                currentScale,
                currentScale,
                detector.focusX,
                detector.focusY
            )
            imageMatrix = matrix
            return true
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        scaleDetector.onTouchEvent(event)
        return true
    }
}

ImageView ที่กำหนดเองจะเปลี่ยนค่า onTouchEvent เพื่อส่งต่อเหตุการณ์ไปยัง ScaleGestureDetector OnScaleListener.onScale() จะถูกเรียกในทุกการเคลื่อนไหวของนิ้ว detector.scaleFactor คือเดลต้า (1.01 สำหรับการก้างช้าๆ) currentScale สะสมด้วย coerceIn สำหรับการคลัมป์ focusX/focusY — จุดกึ่งกลางระหว่างนิ้วซึ่งส่งไปยัง postScale สำหรับการขยายขนาดรอบจุดหมุนที่ถูกต้อง

ตัวอย่างที่ 3: UIScrollView พร้อมรองรับการซูม (iOS)

UIScrollView มีการรองรับ Pinch-to-Zoom ในตัวผ่านตัวแทน นี่เป็นวิธีที่ต้องการสำหรับรูปภาพและเอกสาร — ScrollView จะจัดการมาตรฐาน แรงเชืองและการเลื่อนไปมาโดยอัตโนมัติ

swift
class ScrollableImageViewController: UIViewController,
                                    UIScrollViewDelegate {

    @IBOutlet private var scrollView: UIScrollView!
    @IBOutlet private var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.minimumZoomScale = 0.5
        scrollView.maximumZoomScale = 3.0
        scrollView.delegate = self
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
}

UIScrollView จะสร้าง UIPinchGestureRecognizer โดยอัตโนมัติ จัดการมาตรฐานผ่านการแปลง CALayer และเพิ่มผลกระทบการกระเด้งเมื่อเกินขอบเขต โค้ดจะลดลงเพียงการตั้งค่า min/maxZoomScale และส่งคืน viewForZooming ที่ IT Sectr เราใช้ UIScrollView สำหรับรูปภาพเต็มจอ — ซึ่งช่วยลดปริมาตร์โค้ดได้ถึง 5 เท่าเมื่อเปรียบเทียบกับการดำเนินการด้วยตนเองผ่าน UIPinchGestureRecognizer

คำถามที่พบบ่อย

การหยิบแตกต่างจากการหยิบบนแพ็ดทรากเป็ดอย่างไร?

บนหน้าจอสัมผัส การหยิบ ใช้สองนิ้วโดยการติดตามระยะห่างระหว่างนิ้ว บนแพ็ดทรากเป็ด MacBook การซูมจะถูกนำไปใช้ผ่าน UIPinchGestureRecognizer ด้วยสถานะเดียวกัน (.began → .changed → .ended) แต่แหล่งของการสัมผัสคือแพ็ดทรากเป็ด Force Touch ไม่ใช่หน้าจอแบบเคปาซิเตีฟ API เหมือนกัน กลไกเหมือนกัน

จะจำกัดการซูมต่ำสุดและสูงสุดอย่างไร?

ใน iOS: หลังได้รับ scale จาก UIPinchGestureRecognizer ให้คูณด้วย currentScale และใช้ clamp(min: 0.5, max: 3.0) ใน Android: ใช้ coerceIn(minScale, maxScale) ก่อน matrix.postScale() ใน UIScrollView — ผ่านคุณสมบัติ minimumZoomScale และ maximumZoomScale หากไม่มีการจำกัด มาตรฐานอาจเพิ่มขึ้นไปจนอนันต์หรือลดลงเป็นศูนย์

Pinch-to-Zoom ทำงานกับนิ้วเดียวหรือไม่?

ไม่ การหยิบต้องการอย่างน้อย สองนิ้ว สำหรับการซูมด้วยนิ้วเดียว (ดับเบิ้ลแบบคู่ + ลากขึ้น/ลง) จำเป็นต้องมีการนำไปใช้แบบกำหนดเองผ่าน UIPanGestureRecognizer หรือ GestureDetector.onScroll นี่เป็นคนละท่าภาษ์ที่แตกต่างกันโดยมีตรรกะที่แตกต่างกัน — โดยปกติใช้สำหรับการซูมในกล้องหรือแผนที่

สรุป

  • Pinch-to-Zoom เป็นท่าภาษ์แบบมัลติทัชสองนิ้วสำหรับการขยายหรือย่อขนาดเนื้อหา มาตรฐานสำหรับรูปภาพ แผนที่และเอกสาร
  • UIPinchGestureRecognizer ใน iOS เป็นตัวรู้จักแบบต่อเนื่องด้วย scale (เดลต้า) และ velocity (ความเร็ว)
  • ScaleGestureDetector ใน Android เป็นตัวตรวจจับเฉพาะทางด้วย onScale() focusX/focusY สำหรับจุดหมุน
  • สำหรับ UIScrollView การซูมถูกสร้างไว้ในตัวผ่าน minimumZoomScale/maximumZoomScale — ง่ายกว่าการดำเนินการด้วยตนเอง
  • จำกัดมาตรฐานเสมอ (0.5–3.0) ผ่าน clamp — หากไม่มี เนื้อหาอาจมองไม่เห็น
  • จุดหมุนของการขยายขนาดคือจุดกึ่งกลางระหว่างนิ้ว หากไม่มี เนื้อหาจะเคลื่อนที่ในระหว่างการขยาย
  • ที่ IT Sectr เราใช้ UIScrollView สำหรับรูปภาพและ ScaleGestureDetector ที่กำหนดเองสำหรับโปรแกรมแก้ไข — ทางเลือกขึ้นอยู่กับความซับซ้อนของการแปลงรูป

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม