Chạm và chuột

Gặp lại nhau lần đầu tiên

Giới thiệu

Gần 30 năm qua, trải nghiệm điện toán máy tính để bàn tập trung vào bàn phím và chuột hoặc bàn di chuột là thiết bị đầu vào chính của người dùng. Tuy nhiên, hơn thập kỷ qua, điện thoại thông minh và máy tính bảng đã mang đến một mô hình tương tác mới, đó là chạm. Với sự ra đời của máy Windows 8 có tính năng cảm ứng và giờ đây với sự ra mắt của chiếc Chromebook Pixel có hỗ trợ cảm ứng, cảm ứng đã trở thành một phần của trải nghiệm trên máy tính để bàn. Một trong những thách thức lớn nhất là xây dựng trải nghiệm không chỉ hoạt động trên thiết bị cảm ứng và thiết bị chuột mà còn trên các thiết bị này nơi người dùng sẽ sử dụng cả hai phương thức nhập (đôi khi xảy ra đồng thời)!

Bài viết này sẽ giúp bạn hiểu cách các chức năng cảm ứng được tích hợp vào trình duyệt, cách bạn có thể tích hợp cơ chế giao diện mới này vào các ứng dụng hiện có của mình cũng như cách thao tác chạm có thể hoạt động hiệu quả khi nhập bằng chuột.

Trạng thái cảm ứng trong nền tảng web

iPhone là nền tảng phổ biến đầu tiên được tích hợp API cảm ứng chuyên dụng vào trình duyệt web. Một số nhà cung cấp trình duyệt khác đã tạo các giao diện API tương tự được xây dựng để tương thích với cách triển khai iOS. Việc này hiện được mô tả trong thông số kỹ thuật"Sự kiện chạm phiên bản 1". Sự kiện chạm được Chrome và Firefox trên máy tính, Safari hỗ trợ trên iOS và Chrome, trình duyệt Android trên Android, cũng như các trình duyệt dành cho thiết bị di động khác như trình duyệt Blackberry.

Đồng nghiệp của tôi, Boris Smus đã viết một hướng dẫn HTML5Rocks về sự kiện Chạm rất hay để bắt đầu nếu bạn chưa từng xem các sự kiện Touch trước đây. Trên thực tế, nếu bạn chưa từng làm việc với các sự kiện chạm trước đây, hãy đọc bài viết đó ngay bây giờ trước khi tiếp tục. Thôi nào, tôi sẽ đợi.

Tất cả đã hoàn tất? Giờ đây, khi bạn đã có nền tảng cơ bản trong các sự kiện chạm, thách thức khi viết các tương tác hỗ trợ cảm ứng là các tương tác chạm có thể hơi khác so với các sự kiện chuột (và bàn di chuột và bi xoay) – và mặc dù giao diện cảm ứng thường cố gắng mô phỏng chuột, nhưng việc mô phỏng đó không hoàn hảo hoặc hoàn chỉnh; bạn thực sự cần phải làm việc qua cả hai kiểu tương tác và có thể phải hỗ trợ từng giao diện một cách độc lập.

Quan trọng nhất: Người dùng có thể chạm và sử dụng chuột

Nhiều nhà phát triển đã tạo các trang web dùng để phát hiện tĩnh xem một môi trường có hỗ trợ các sự kiện chạm hay không, sau đó giả định rằng họ chỉ cần hỗ trợ các sự kiện chạm (chứ không phải chuột). Hiện tại, đây là một giả định lỗi. Thay vào đó, việc hiện diện các sự kiện chạm không có nghĩa là người dùng chủ yếu sử dụng thiết bị nhập bằng cách chạm. Các thiết bị như Chromebook Pixel và một số máy tính xách tay Windows 8 hiện hỗ trợ CẢ phương thức nhập bằng Chuột và Chạm và nhiều phương thức nhập khác trong tương lai gần. Trên các thiết bị này, việc người dùng sử dụng cả chuột và màn hình cảm ứng để tương tác với các ứng dụng là khá tự nhiên, do đó, "hỗ trợ cảm ứng" không giống với "không cần hỗ trợ chuột". Bạn không thể coi vấn đề là "Tôi phải viết hai kiểu tương tác khác nhau và chuyển đổi giữa chúng", mà bạn cần suy nghĩ về cách cả hai tương tác sẽ hoạt động cùng nhau cũng như độc lập. Trên Chromebook Pixel, tôi thường xuyên sử dụng bàn di chuột, nhưng tôi cũng đưa tay lên và chạm vào màn hình - trên cùng một ứng dụng hoặc trang, tôi luôn làm mọi việc tự nhiên nhất vào lúc này. Mặt khác, một số người dùng máy tính xách tay có màn hình cảm ứng sẽ hiếm khi sử dụng màn hình cảm ứng. Vì vậy, sự hiện diện của tính năng nhập bằng cách chạm sẽ không tắt hoặc cản trở khả năng điều khiển bằng chuột.

Tuy nhiên, rất khó để biết liệu môi trường trình duyệt của người dùng có hỗ trợ tính năng nhập bằng cách chạm hay không; lý tưởng nhất là trình duyệt trên máy tính sẽ luôn hỗ trợ sự kiện chạm để có thể đính kèm màn hình cảm ứng bất cứ lúc nào (ví dụ: nếu có màn hình cảm ứng thông qua KVM). Vì tất cả những lý do này, ứng dụng của bạn không nên tìm cách chuyển đổi giữa thao tác chạm và chuột – chỉ cần hỗ trợ cả hai!

Hỗ trợ chuột và thao tác chạm cùng nhau

#1 – Nhấp và nhấn – Thứ tự sự vật "tự nhiên"

Vấn đề đầu tiên là giao diện cảm ứng thường cố gắng mô phỏng các thao tác nhấp chuột – hiển nhiên là vì giao diện cảm ứng cần phải hoạt động trên các ứng dụng trước đây chỉ tương tác với sự kiện chuột! Bạn có thể dùng cụm từ này làm lối tắt – vì các sự kiện "nhấp" sẽ tiếp tục được kích hoạt, cho dù người dùng nhấp bằng chuột hay nhấn ngón tay trên màn hình. Tuy nhiên, phím tắt này có một vài vấn đề.

Trước tiên, bạn phải cẩn thận khi thiết kế các loại tương tác chạm nâng cao hơn: khi người dùng sử dụng chuột, chuột sẽ phản hồi thông qua sự kiện nhấp, nhưng khi người dùng chạm vào màn hình, cả sự kiện chạm và sự kiện nhấp sẽ xảy ra. Đối với một lượt nhấp, thứ tự của các sự kiện là:

  1. khởi động bằng thao tác chạm
  2. di chuyển cảm ứng
  3. điểm cuối
  4. di chuột qua
  5. mousemove
  6. di chuột xuống
  7. chuột lên
  8. click

Tất nhiên, điều này có nghĩa là nếu đang xử lý các sự kiện chạm như khởi động bằng thao tác chạm, thì bạn cần đảm bảo rằng bạn cũng không xử lý sự kiện nhấp chuột xuống và/hoặc nhấp tương ứng. Nếu bạn có thể huỷ các sự kiện chạm (gọi preventDefault() bên trong trình xử lý sự kiện), thì sẽ không có sự kiện chuột nào được tạo cho thao tác chạm. Một trong những quy tắc quan trọng nhất đối với trình xử lý thao tác chạm là:

Tuy nhiên, điều này cũng ngăn hành vi mặc định khác của trình duyệt (như cuộn) – mặc dù thông thường, bạn sẽ xử lý sự kiện chạm hoàn toàn trong trình xử lý và bạn MUỐN tắt các hành động mặc định. Nói chung, bạn nên xử lý và huỷ tất cả các sự kiện chạm hoặc tránh việc sử dụng trình xử lý cho sự kiện đó.

Thứ hai, khi người dùng nhấn vào một phần tử trong trang web trên thiết bị di động, các trang chưa được thiết kế để tương tác trên thiết bị di động sẽ phải chờ ít nhất 300 mili giây giữa sự kiện chạm và quá trình xử lý sự kiện chuột (chuột xuống). Bạn có thể thực hiện việc này bằng Chrome, bạn có thể bật tính năng "Mô phỏng sự kiện chạm" trong Công cụ cho nhà phát triển Chrome để giúp kiểm thử giao diện cảm ứng trên một hệ thống không cảm ứng!

Độ trễ này nhằm cho phép trình duyệt có thời gian xác định xem người dùng có đang thực hiện một cử chỉ khác hay không, cụ thể là thu phóng bằng cách nhấn đúp. Rõ ràng, điều này có thể rắc rối trong trường hợp bạn muốn có phản hồi tức thì với thao tác chạm ngón tay. Chúng tôi đang triển khai công việc để cố gắng hạn chế các trường hợp trong đó độ trễ này tự động xảy ra.

Chrome dành cho Android Trình duyệt Android Opera Mobile dành cho Android) Firefox dành cho Android Safari dành cho iOS
Khung nhìn không thể mở rộng Không có độ trễ 300 mili giây 300 mili giây Không có độ trễ 300 mili giây
Không có khung nhìn 300 mili giây 300 mili giây 300 mili giây 300 mili giây 300 mili giây

Cách đầu tiên và dễ dàng nhất để tránh sự chậm trễ này là "cho trình duyệt trên thiết bị di động biết" rằng trang của bạn sẽ không cần thu phóng - bạn có thể thực hiện việc này bằng cách sử dụng một khung nhìn cố định, ví dụ: bằng cách chèn vào trang của bạn:

<meta name="viewport" content="width=device-width,user-scalable=no">

Tất nhiên, thao tác này không phải lúc nào cũng phù hợp - thao tác này sẽ tắt tính năng thu phóng bằng tính năng chụm, có thể bắt buộc vì lý do hỗ trợ tiếp cận, vì vậy hãy sử dụng tính năng này một cách thận trọng nếu có (nếu bạn tắt tính năng điều chỉnh tỷ lệ người dùng, bạn có thể muốn cung cấp một số cách khác để tăng khả năng đọc văn bản trong ứng dụng của mình). Ngoài ra, đối với Chrome trên các thiết bị lớp máy tính có hỗ trợ cảm ứng và các trình duyệt khác trên nền tảng di động khi trang có khung nhìn không thể mở rộng, độ trễ này không áp dụng.

#2: Sự kiện Mousemove không được kích hoạt bằng thao tác chạm

Tại thời điểm này, điều quan trọng cần lưu ý là việc mô phỏng các sự kiện chuột trong giao diện cảm ứng thường không mở rộng sang việc mô phỏng các sự kiện di chuyển bằng chuột. Vì vậy, nếu bạn tạo một chế độ điều khiển bằng chuột đẹp mắt có sử dụng các sự kiện di chuyển bằng chuột, thì có thể tính năng này sẽ không hoạt động với thiết bị cảm ứng trừ phi bạn cũng thêm trình xử lý di chuyển bằng cách chạm.

Các trình duyệt thường tự động triển khai tương tác thích hợp cho tương tác chạm trên điều khiển HTML - ví dụ: Điều khiển phạm vi HTML5 sẽ chỉ hoạt động khi bạn sử dụng tương tác chạm. Tuy nhiên, nếu bạn đã triển khai các điều khiển của riêng mình, chúng có thể sẽ không hoạt động trên các tương tác kiểu nhấp và kéo; trên thực tế, một số thư viện thường dùng (như jQueryUI) chưa hỗ trợ sẵn các tương tác chạm theo cách này (mặc dù đối với jQueryUI, có một số bản sửa lỗi khỉ-patch cho vấn đề này). Đây là một trong những vấn đề đầu tiên tôi gặp phải khi nâng cấp ứng dụng Web Audio Playground để tương tác với thao tác chạm - các thanh trượt dựa trên jQueryUI, vì vậy chúng không hoạt động với các tương tác nhấp và kéo. Tôi đã đổi sang Kiểm soát Phạm vi HTML5 và chúng đã hoạt động. Hoặc, tất nhiên là tôi có thể chỉ cần thêm trình xử lý di chuyển cảm ứng để cập nhật thanh trượt, nhưng có một vấn đề xảy ra với tính năng đó...

#3: Touchmove và MouseMove không phải là điều tương tự

Một sai lầm tôi đã thấy một số nhà phát triển gặp phải là việc trình xử lý di chuyển bằng thao tác di chuyển chuột và di chuyển chuột được gọi vào cùng các đường dẫn mã. Hành vi của những sự kiện này rất gần nhau, nhưng có sự khác biệt tinh tế - cụ thể là các sự kiện chạm luôn nhắm đến phần tử khi thao tác chạm BẮT ĐẦU, trong khi các sự kiện chuột nhắm đến phần tử hiện ở bên dưới con trỏ chuột. Đây là lý do tại sao chúng ta có các sự kiện di chuột qua và di chuột ra, nhưng không có sự kiện chạm và di chuột tương ứng mà chỉ có sự kiện chạm.

Việc phổ biến nhất có thể gây hại cho bạn là khi bạn vô tình xoá (hoặc di chuyển) phần tử mà người dùng bắt đầu chạm vào. Ví dụ: hãy tưởng tượng một băng chuyền hình ảnh có trình xử lý thao tác chạm trên toàn bộ băng chuyền để hỗ trợ hành vi cuộn tuỳ chỉnh. Khi hình ảnh hiện có thay đổi, bạn sẽ xoá một số thành phần <img> và thêm các thành phần khác. Nếu người dùng bắt đầu chạm vào một trong các hình ảnh đó rồi bạn xoá hình ảnh đó, thì trình xử lý (nằm trên đối tượng cấp trên của phần tử img) sẽ ngừng nhận các sự kiện chạm (vì chúng đang được gửi đến một mục tiêu không còn ở trong cây) – có vẻ như người dùng đang giữ ngón tay ở một nơi mặc dù chúng có thể đã di chuyển và cuối cùng đã xoá nó đi.

Tất nhiên, bạn có thể tránh vấn đề này bằng cách tránh xoá các phần tử có (hoặc có đối tượng cấp trên có) trình xử lý thao tác chạm trong khi thao tác chạm đang hoạt động. Mặt khác, tốt nhất là bạn nên đăng ký trình xử lý chạm/Touchmove tĩnh, chờ cho đến khi bạn nhận được một sự kiện Touchstart rồi thêm trình xử lý Touchmove/Touchend/Touchcancel vào target của sự kiện chạm (và xoá chúng khi kết thúc/huỷ). Bằng cách này, bạn sẽ tiếp tục nhận được các sự kiện cho lần chạm ngay cả khi phần tử đích đã bị di chuyển/xoá. Bạn có thể thực hiện điều này một chút tại đây – chạm vào hộp màu đỏ và trong khi giữ phím Escape để xoá nó khỏi DOM.

#4: Chạm và :Di chuột

Phép ẩn dụ con trỏ chuột tách biệt vị trí con trỏ khỏi việc chủ động chọn, và điều này cho phép các nhà phát triển sử dụng trạng thái di chuột để ẩn và hiển thị thông tin có thể phù hợp với người dùng. Tuy nhiên, hầu hết các giao diện cảm ứng hiện không phát hiện được ngón tay đang "di chuột" trên mục tiêu. Vì vậy, việc cung cấp thông tin quan trọng về mặt ngữ nghĩa (ví dụ: cung cấp cửa sổ bật lên "điều khiển này là gì?) dựa trên thao tác di chuột là không, trừ phi bạn cũng đưa ra một cách thức thân thiện với thao tác chạm để truy cập thông tin này. Bạn cần cẩn thận về cách sử dụng thao tác di chuột để chuyển tiếp thông tin đến người dùng.

Tuy nhiên, điều thú vị là CSS :hover pseudoclass có thể được kích hoạt bằng giao diện cảm ứng trong một số trường hợp – khi nhấn vào một phần tử sẽ khiến phần tử đó :hoạt động trong khi ngón tay thả xuống và thành phần này cũng có được trạng thái :hover. (Với Internet Explorer, :di chuột chỉ có hiệu lực khi ngón tay người dùng thả xuống - các trình duyệt khác vẫn giữ :di chuột cho đến lần nhấn hoặc di chuột tiếp theo.) Đây là một phương pháp hay để làm cho các trình đơn bật ra hoạt động trên giao diện cảm ứng - ảnh hưởng phụ của việc làm cho một phần tử hoạt động là trạng thái :di chuột cũng được áp dụng. Ví dụ:

<style>
img ~ .content {
  display:none;
}

img:hover ~ .content {
  display:block;
}
</style>

<img src="/awesome.png">
<div class="content">This is an awesome picture of me</div>

Sau khi người dùng nhấn vào một phần tử khác, phần tử đó sẽ không hoạt động nữa và trạng thái di chuột sẽ biến mất, giống như khi người dùng sử dụng con trỏ chuột và di chuyển nó ra khỏi phần tử đó. Có thể bạn cần bao bọc nội dung trong một phần tử <a> để biến phần tử đó thành một thẻ dừng. Bằng cách đó, người dùng có thể bật/tắt thông tin bổ sung khi di chuột hoặc nhấp vào, thao tác nhấn bằng cách chạm hoặc nhấn phím mà không cần có JavaScript. Tôi rất ngạc nhiên khi bắt đầu làm việc để Web Audio Playground hoạt động tốt với giao diện cảm ứng mà các trình đơn mở ra đã hoạt động tốt khi chạm vào, vì tôi đã sử dụng loại cấu trúc này!

Phương pháp trên phù hợp với giao diện dựa trên con trỏ chuột cũng như giao diện cảm ứng. Điều này trái ngược với việc sử dụng thuộc tính "tiêu đề" khi di chuột, vì thuộc tính này sẽ KHÔNG hiển thị khi phần tử được kích hoạt:

<img src="/awesome.png" title="this doesn't show up in touch">

#5: Độ chính xác của thao tác chạm so với chuột

Mặc dù chuột có sự tách biệt về mặt khái niệm so với thực tế, nhưng hoá ra chúng cực kỳ chính xác vì hệ điều hành cơ bản thường theo dõi độ chính xác chính xác của điểm ảnh cho con trỏ. Mặt khác, các nhà phát triển thiết bị di động nhận thấy rằng thao tác chạm ngón tay trên màn hình cảm ứng không chính xác, chủ yếu là do kích thước của diện tích bề mặt ngón tay khi tiếp xúc (và một phần là do ngón tay che khuất màn hình).

Nhiều cá nhân và công ty đã tiến hành nghiên cứu sâu rộng về người dùng về cách thiết kế các ứng dụng và trang web phù hợp với tương tác dựa trên ngón tay, và nhiều cuốn sách đã được viết về chủ đề này. Bạn nên tăng kích thước của đích chạm bằng cách tăng khoảng đệm và giảm khả năng nhấn không chính xác bằng cách tăng khoảng cách giữa các phần tử. (Lề không được bao gồm trong quá trình xử lý việc phát hiện lượt truy cập của các sự kiện chạm và nhấp chuột, trong khi khoảng đệm được tính.) Một trong những biện pháp sửa lỗi chính mà tôi phải thực hiện đối với Web Audio Playground là tăng kích thước của các điểm kết nối để chúng có thể được chạm chính xác hơn.

Nhiều nhà cung cấp trình duyệt xử lý giao diện dựa trên cảm ứng cũng đã đưa logic vào trình duyệt để giúp nhắm mục tiêu đúng phần tử khi người dùng chạm vào màn hình và giảm khả năng nhấp chuột không chính xác – mặc dù việc này thường chỉ sửa các sự kiện nhấp chứ không phải di chuyển (mặc dù Internet Explorer cũng có thể sửa đổi các sự kiện di chuột xuống/chuột/màn hình).

#6: Duy trì các trình xử lý thao tác chạm, nếu không, chúng sẽ làm giật gân

Điều quan trọng là trình xử lý cảm ứng chỉ được giới hạn ở các phần tử cần thiết; các phần tử cảm ứng có thể có băng thông rất cao, vì vậy cần tránh các trình xử lý cảm ứng trên các phần tử cuộn (vì quá trình xử lý của bạn có thể ảnh hưởng đến quá trình tối ưu hoá trình duyệt để cuộn nhanh mà không bị giật. Các trình duyệt hiện đại sẽ cố gắng cuộn trên luồng GPU, nhưng điều này là không thể nếu trước tiên chúng phải kiểm tra bằng javascript để xem liệu mỗi sự kiện chạm có được xử lý bởi ứng dụng không). Bạn có thể xem ví dụ về hành vi này.

Để tránh vấn đề này, bạn cần đảm bảo rằng nếu chỉ xử lý các sự kiện chạm trong một phần nhỏ của giao diện người dùng, thì bạn chỉ đính kèm trình xử lý cảm ứng vào đó (không phải trên <body> của trang); nói ngắn gọn là giới hạn phạm vi của trình xử lý cảm ứng nhiều nhất có thể.

#7: Đa cảm ứng

Thách thức thú vị cuối cùng là mặc dù chúng tôi đã gọi giao diện này là giao diện người dùng "Chạm", nhưng gần như sự hỗ trợ thực sự dành cho chế độ Đa điểm chạm – tức là API cung cấp nhiều tính năng nhập bằng cách chạm tại một thời điểm. Khi bạn bắt đầu hỗ trợ chức năng chạm trong ứng dụng của mình, bạn nên cân nhắc xem nhiều thao tác chạm có thể ảnh hưởng như thế nào đến ứng dụng của mình.

Nếu bạn đang xây dựng các ứng dụng chủ yếu hoạt động bằng chuột, thì bạn đã quen với việc tạo tối đa một điểm con trỏ – các hệ thống thường không hỗ trợ nhiều con trỏ chuột. Đối với nhiều ứng dụng, bạn sẽ chỉ ánh xạ các sự kiện chạm với một giao diện con trỏ, nhưng hầu hết các phần cứng mà chúng ta đã thấy cho nhập bằng cách chạm trên máy tính để bàn có thể xử lý ít nhất 2 đầu vào đồng thời và hầu hết phần cứng mới dường như hỗ trợ ít nhất 5 đầu vào đồng thời. Tất nhiên là để phát triển bàn phím đàn piano ảo, bạn nên có thể hỗ trợ đồng thời nhiều thao tác nhập bằng cách chạm.

API cảm ứng W3C hiện đã được triển khai không có API để xác định số điểm tiếp xúc mà phần cứng hỗ trợ. Vì vậy, bạn sẽ phải sử dụng ước tính tốt nhất cho số điểm tiếp xúc mà người dùng sẽ muốn – hoặc tất nhiên là chú ý đến số điểm tiếp xúc bạn nhìn thấy trong thực tế và điều chỉnh. Ví dụ: trong một ứng dụng piano, nếu bạn không thấy nhiều hơn hai điểm chạm, bạn có thể muốn thêm một số giao diện người dùng " hợp âm". PointerEvents API không có API để xác định các khả năng của thiết bị.

Cải thiện

Hy vọng bài viết này đã cung cấp cho bạn một số hướng dẫn về các thách thức phổ biến trong việc triển khai thao tác chạm và tương tác bằng chuột. Tất nhiên, quan trọng hơn bất kỳ lời khuyên nào khác là bạn cần thử nghiệm ứng dụng của mình trên thiết bị di động, máy tính bảng cũng như môi trường kết hợp giữa chuột và máy tính để bàn. Nếu bạn không có phần cứng cảm ứng và chuột, hãy sử dụng tính năng "Mô phỏng sự kiện chạm" của Chrome để giúp bạn kiểm thử các trường hợp khác nhau.

Bạn không chỉ có thể làm theo các hướng dẫn này mà còn tương đối dễ dàng để xây dựng trải nghiệm tương tác hấp dẫn hoạt động tốt với tính năng nhập bằng cách chạm, nhập bằng chuột và thậm chí cả hai kiểu tương tác cùng lúc.