পাঠ ০৫ · ৫৭-এর মধ্যে · মডিউল ২
Home / Courses / Mobile App Development / টাচ ইন্টারঅ্যাকশন

টাচ ইন্টারঅ্যাকশন ফান্ডামেন্টাল — জেসচার ও ফিডব্যাক

Touch interaction fundamentals — gestures and feedback
৯ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • পাঁচটি মূল টাচ জেসচার — ট্যাপ, ডাবল-ট্যাপ, লং-প্রেস, সোয়াইপ, পিঞ্চ — এবং প্রতিটির চরিত্র
  • জেসচার রিকগনিশন কীভাবে raw টাচ ইভেন্টকে একটি নির্দিষ্ট জেসচারে রূপান্তর করে
  • ভিজ্যুয়াল ও হ্যাপটিক ফিডব্যাক কেন টাচ ইন্টারফেসে অপরিহার্য
  • coordinates ও timestamp থেকে genuinely গণনা করে জেসচার শনাক্ত করে এমন একটি Python ফাংশন

১ · পাঁচটি মূল টাচ জেসচার

একটি মোবাইল টাচস্ক্রিন OS-কে ক্রমাগত raw টাচ-পয়েন্ট ইভেন্ট পাঠায় — প্রতিটি ইভেন্টে একটি অবস্থান (x, y) ও একটি টাইমস্ট্যাম্প থাকে। এই raw ইভেন্টগুলোর সিকোয়েন্স থেকেই একটি নির্দিষ্ট, নাম-করা জেসচার শনাক্ত করা হয়।

ট্যাপ (Tap)
স্বল্প সময় (সাধারণত ~২০০ms-এর কম) এবং সামান্য নড়াচড়া — একবার ছুঁয়ে সরাসরি তুলে ফেলা।
ডাবল-ট্যাপ (Double-tap)
দ্রুত পরপর দুটি ট্যাপ, একটি সময়সীমার মধ্যে (যেমন ৩০০ms) — সাধারণত জুম বা "লাইক" অ্যাকশনের জন্য ব্যবহৃত হয়।
লং-প্রেস (Long-press)
দীর্ঘ সময় (সাধারণত ~৫০০ms বা বেশি) চেপে ধরে রাখা, কিন্তু সামান্য নড়াচড়া — প্রায়ই কনটেক্সট মেনু খোলে।
সোয়াইপ (Swipe)
একটি নির্দিষ্ট দিকে (উপর/নিচ/ডান/বাম) দ্রুত, বড় দূরত্বের নড়াচড়া — স্ক্রল বা নেভিগেশনের জন্য ব্যবহৃত হয়।
পিঞ্চ (Pinch)
দুটি আঙুল দিয়ে একসাথে — কাছে আনা (pinch-in, জুম আউট) বা দূরে সরানো (pinch-out, জুম ইন); দুটি পৃথক টাচ-পয়েন্টের মধ্যে দূরত্বের পরিবর্তন পরিমাপ করে শনাক্ত হয়।

২ · জেসচার রিকগনিশন কীভাবে কাজ করে

একটি জেসচার রিকগনাইজার raw টাচ ইভেন্ট থেকে দুটি মূল রাশি বের করে — duration (প্রথম ও শেষ ইভেন্টের টাইমস্ট্যাম্পের পার্থক্য) এবং movement (প্রথম ও শেষ পয়েন্টের মধ্যে সরল দূরত্ব)। এই দুটি রাশির উপর ভিত্তি করেই একটি নির্দিষ্ট থ্রেশহোল্ড-নিয়ম প্রয়োগ করে জেসচারের ধরন নির্ধারণ করা হয় — অনুমান নয়, একটি নির্ধারক (deterministic) গণনা।

raw টাচ ইভেন্ট (x, y, timestamp) duration + movement গণনা করা শনাক্তকৃত জেসচার tap / swipe / long_press
জেসচার রিকগনিশনের মূল পাইপলাইন — raw ইভেন্ট থেকে গণনা করা রাশি, তারপর থ্রেশহোল্ড-নিয়ম প্রয়োগ করে চূড়ান্ত জেসচার।

৩ · ভিজ্যুয়াল ও হ্যাপটিক ফিডব্যাক

টাচস্ক্রিনে মাউস কার্সরের মতো কোনো স্থায়ী ভিজ্যুয়াল ইঙ্গিত নেই — তাই ব্যবহারকারীর টাচ "রেজিস্টার্ড" হয়েছে কিনা তা নিশ্চিত করতে অ্যাপকে সক্রিয়ভাবে ফিডব্যাক দিতে হয়: একটি বাটনে ট্যাপ করলে সাথে সাথে রঙ পরিবর্তন বা একটি "রিপল" অ্যানিমেশন (ভিজ্যুয়াল ফিডব্যাক), অথবা একটি সংক্ষিপ্ত কম্পন (হ্যাপটিক ফিডব্যাক)। এই ফিডব্যাক ছাড়া ব্যবহারকারী একই বাটনে বারবার চাপতে থাকতে পারেন, এই সন্দেহে যে প্রথম ট্যাপটি কাজ করেনি।

৪ · একটি সত্যিকারের জেসচার রিকগনাইজার

নিচের কোড সেলে classify_gesture() ফাংশনটি একটি raw টাচ-পয়েন্ট ইভেন্ট সিকোয়েন্স ((x, y, timestamp_ms) টিউপলের লিস্ট) থেকে প্রথমে duration ও movement genuinely গণনা করে, তারপর নির্দিষ্ট থ্রেশহোল্ড অনুযায়ী "tap", "long_press", বা "swipe_<direction>"-এ শ্রেণীবদ্ধ করে। তিনটি concrete সিকোয়েন্স দিয়ে পরীক্ষা করা হয়েছে, প্রতিটির প্রত্যাশিত ফলাফলসহ।

Python
# raw টাচ-পয়েন্ট ইভেন্ট থেকে জেসচার শনাক্তকরণ -- genuinely coordinates/timestamp থেকে গণনা করা

TAP_MAX_DURATION_MS = 200
TAP_MAX_MOVEMENT_PX = 10
LONG_PRESS_MIN_DURATION_MS = 500
SWIPE_MIN_DISTANCE_PX = 50

def euclidean_distance(p1, p2):
    return ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5

def classify_gesture(events):
    """events: [(x, y, timestamp_ms), ...] -- প্রথম ও শেষ পয়েন্ট থেকে duration/movement গণনা করে"""
    if len(events) < 2:
        return {"gesture": "unknown", "duration_ms": 0, "movement_px": 0.0}

    x0, y0, t0 = events[0]
    xn, yn, tn = events[-1]
    duration = tn - t0
    movement = euclidean_distance((x0, y0), (xn, yn))
    dx = xn - x0
    dy = yn - y0

    if duration <= TAP_MAX_DURATION_MS and movement < TAP_MAX_MOVEMENT_PX:
        gesture = "tap"
    elif duration >= LONG_PRESS_MIN_DURATION_MS and movement < TAP_MAX_MOVEMENT_PX:
        gesture = "long_press"
    elif movement >= SWIPE_MIN_DISTANCE_PX:
        if abs(dx) >= abs(dy):
            direction = "right" if dx > 0 else "left"
        else:
            direction = "down" if dy > 0 else "up"
        gesture = f"swipe_{direction}"
    else:
        gesture = "unknown"

    return {"gesture": gesture, "duration_ms": duration, "movement_px": round(movement, 1)}

# তিনটি concrete টেস্ট সিকোয়েন্স
sequence_tap = [(100, 100, 0), (101, 100, 50), (100, 101, 120)]
sequence_swipe = [(50, 300, 0), (150, 300, 40), (250, 300, 80), (350, 300, 120)]
sequence_long_press = [(200, 200, 0), (200, 201, 600), (201, 200, 750)]

test_cases = [
    ("ট্যাপ সিকোয়েন্স", sequence_tap, "tap"),
    ("সোয়াইপ সিকোয়েন্স", sequence_swipe, "swipe_right"),
    ("লং-প্রেস সিকোয়েন্স", sequence_long_press, "long_press"),
]

for label, events, expected in test_cases:
    result = classify_gesture(events)
    status = "ঠিক" if result["gesture"] == expected else "ভুল"
    print(f"{label:20s} -> {result['gesture']:12s} (duration={result['duration_ms']}ms, movement={result['movement_px']}px) | প্রত্যাশিত={expected} [{status}]")

    
লক্ষ্য করুন সোয়াইপ সিকোয়েন্সের duration (১২০ms) আসলে ট্যাপের থ্রেশহোল্ডের (২০০ms) মধ্যেই — কিন্তু movement (৩০০px) ট্যাপের সর্বোচ্চ movement (১০px)-এর চেয়ে অনেক বেশি হওয়ায় এটি ট্যাপ হিসেবে গণ্য হয় না, এবং movement সোয়াইপের ন্যূনতম দূরত্ব (৫০px) ছাড়িয়ে যাওয়ায় সোয়াইপ হিসেবে শনাক্ত হয় — dx=300, dy=0 হওয়ায় দিক নির্ধারিত হয় "right"।
মূল কথা · Key takeaway

একটি জেসচার কোনো জাদু নয় — এটি raw টাচ ইভেন্টের উপর প্রয়োগ করা একটি নির্ধারক গণনা: duration ও movement বের করা, তারপর থ্রেশহোল্ড-নিয়ম প্রয়োগ করা। এই ভিত্তির উপরই M5-এর UI কম্পোনেন্ট ও M6-এর নেভিগেশন জেসচার-ট্রিগার্ড ইন্টারঅ্যাকশন তৈরি করবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ একটি টাচ ইভেন্ট সিকোয়েন্সে duration ৫০ms আর movement ১৫px হলে, উপরের কোডের নিয়ম অনুযায়ী এটি কোন জেসচার হিসেবে শনাক্ত হবে?

এটি "unknown" হিসেবে শনাক্ত হবে। ট্যাপের শর্ত পূরণ হয় না কারণ movement (১৫px) ট্যাপের সর্বোচ্চ সীমা (১০px)-এর বেশি। লং-প্রেসের শর্তও পূরণ হয় না কারণ duration (৫০ms) লং-প্রেসের ন্যূনতম সীমা (৫০০ms)-এর অনেক কম। সোয়াইপের শর্তও পূরণ হয় না কারণ movement (১৫px) সোয়াইপের ন্যূনতম দূরত্ব (৫০px)-এর কম। এই মাঝামাঝি অঞ্চলটিই দেখায় কেন বাস্তব জেসচার রিকগনাইজারে থ্রেশহোল্ড সাবধানে বাছাই করতে হয়।

প্র ০২ ডাবল-ট্যাপ শনাক্ত করতে হলে উপরের classify_gesture() ফাংশনে অতিরিক্ত কী তথ্য দরকার হবে?

একটি একক ট্যাপ ইভেন্ট সিকোয়েন্সের তথ্য দিয়ে ডাবল-ট্যাপ শনাক্ত করা সম্ভব নয় — এর জন্য দুটি পৃথক ট্যাপের মধ্যেকার সময়ের ব্যবধান জানতে হবে। বাস্তবে রিকগনাইজার প্রথম ট্যাপ শনাক্ত হওয়ার পর একটি সংক্ষিপ্ত সময়সীমা (যেমন ৩০০ms) অপেক্ষা করে — যদি সেই সময়ের মধ্যে দ্বিতীয় একটি ট্যাপ (একই কাছাকাছি অবস্থানে) আসে, তখনই সেটিকে "double_tap" হিসেবে পুনঃশ্রেণীবদ্ধ করা হয়; নাহলে প্রথম ট্যাপটিই একক ট্যাপ হিসেবে থেকে যায়।

প্র ০৩ পিঞ্চ জেসচার শনাক্ত করতে কেন একটি একক (x, y, timestamp) সিকোয়েন্স যথেষ্ট নয়?

পিঞ্চের জন্য দুটি আঙুলের অবস্থান, অর্থাৎ দুটি পৃথক টাচ-পয়েন্ট সিকোয়েন্স, একসাথে ট্র্যাক করতে হয় — এবং জেসচারটি নির্ধারিত হয় দুটি পয়েন্টের মধ্যেকার দূরত্ব সময়ের সাথে কীভাবে পরিবর্তিত হচ্ছে তার উপর ভিত্তি করে (দূরত্ব কমলে pinch-in, বাড়লে pinch-out)। উপরের ফাংশন শুধু একটি একক টাচ-পয়েন্টের সিকোয়েন্স নিয়ে কাজ করে বলে এটি single-touch জেসচার (ট্যাপ, সোয়াইপ, লং-প্রেস)-এর জন্যই প্রযোজ্য।

অনুশীলন

  1. চিন্তা করুন: আপনার ব্যবহৃত কোনো অ্যাপে এমন একটি মুহূর্তের কথা মনে করুন যখন একটি জেসচার (হয়তো একটি সোয়াইপ-টু-ডিলিট বা লং-প্রেস কনটেক্সট মেনু) ঠিকমতো শনাক্ত হয়নি বলে মনে হয়েছিল — থ্রেশহোল্ডের ধারণা জানার পর এখন সেই সমস্যাটি কীভাবে ব্যাখ্যা করবেন?

    সম্ভবত আপনার আঙুলের movement বা duration নির্ধারিত থ্রেশহোল্ডের ঠিক সীমানার কাছাকাছি পড়েছিল — সামান্য বেশি নড়াচড়া একটি লং-প্রেসকে "unknown" বা এমনকি একটি ছোট সোয়াইপে পরিণত করতে পারে। এটিই দেখায় কেন থ্রেশহোল্ড খুব কড়া হলে ব্যবহারকারীর অভিজ্ঞতা "অ্যাপটি আমার টাচ ঠিকমতো ধরছে না" বলে মনে হতে পারে।

  2. পরীক্ষা করুন: উপরের কোড সেলে একটি নতুন সিকোয়েন্স যোগ করুন — sequence_vertical_swipe = [(300, 500, 0), (300, 400, 30), (300, 380, 60)] — এবং test_cases-এ এটি যোগ করে classify_gesture() দিয়ে চালিয়ে দেখুন এটি কোন দিক (up/down) শনাক্ত করে।

    এই সিকোয়েন্সে dy = 380 - 500 = -120 (ঋণাত্মক, অর্থাৎ y কমেছে) আর dx = 0, তাই abs(dy) > abs(dx) শর্ত সত্য হয় এবং dy < 0 হওয়ায় দিক নির্ধারিত হবে "up" — ফলাফল "swipe_up"। movement = ১২০px, যা সোয়াইপের ন্যূনতম সীমা (৫০px) ছাড়িয়ে যায়, তাই এটি সঠিকভাবে সোয়াইপ হিসেবে ধরা পড়বে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ অ্যাপ লাইফসাইকেল, মোবাইল UI/UX, MVVM/MVI আর্কিটেকচার, নেটিভ বনাম ক্রস-প্ল্যাটফর্ম, নেভিগেশন, স্টেট ম্যানেজমেন্ট, লোকাল স্টোরেজ, ডিভাইস ফিচার ও ডিপ্লয়মেন্ট — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • Full-Stack Web Frameworks কোর্স সহোদর কোর্স মাউস/কীবোর্ড-নির্ভর ওয়েব ইন্টারঅ্যাকশন সেই কোর্সে কভার হয়েছে — এই পাঠ দেখায় মোবাইলে ইনপুট মডেলটি কতটা ভিন্ন।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks ও Mobile App Development — সব এক জায়গায়।
আগের পাঠ
মোবাইল অ্যাপ লাইফসাইকেল — স্টেট ও ট্রানজিশন