Tái hợp lần đầu tiên
Giới thiệu
Trong gần 30 năm, trải nghiệm 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àm thiết bị nhập chính của người dùng. Tuy nhiên, trong 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: chạm. Với sự ra mắt của các máy Windows 8 hỗ trợ cảm ứng và giờ đây là sự ra mắt của Chromebook Pixel hỗ trợ cảm ứng tuyệt vời, tính năng cảm ứng hiện đang trở thành một phần của trải nghiệm trên máy tính mà người dùng mong đợi. 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 những thiết bị mà người dùng sẽ sử dụng cả hai phương thức nhập – đôi khi đồng thời!
Bài viết này sẽ giúp bạn hiểu cách các tính 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ó và cách cảm ứng có thể hoạt động tốt với phương thức nhập bằng chuột.
Trạng thái chạm trong nền tảng web
iPhone là nền tảng phổ biến đầu tiên có các API cảm ứng chuyên dụng được tích hợp sẵn 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. Hiện tại, các giao diện này được mô tả theo quy cách"Sự kiện chạm phiên bản 1". Chrome và Firefox trên máy tính, Safari trên iOS, Chrome và trình duyệt Android trên Android cũng như các trình duyệt di động khác như trình duyệt Blackberry đều hỗ trợ sự kiện chạm.
Đồng nghiệp của tôi, Boris Smus, đã viết một hướng dẫn tuyệt vời về sự kiện Chạm trên HTML5Rocks. Đây vẫn là một cách hay để bắt đầu nếu bạn chưa từng xem sự kiện Chạm. 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, hãy đọc bài viết đó ngay trước khi tiếp tục. Bạn cứ tiếp tục, tôi sẽ đợi.
Tất cả đã hoàn tất? Giờ đây, bạn đã có kiến thức cơ bản về sự kiện chạm. Thách thức khi viết các hoạt động tương tác hỗ trợ cảm ứng là các hoạt động tương tác cảm ứng có thể khác khá nhiều so với sự kiện chuột (và bàn di chuột mô phỏng chuột và bi xoay). Mặc dù giao diện cảm ứng thường cố gắng mô phỏng chuột, nhưng hoạt động mô phỏng đó không hoàn hảo hoặc đầy đủ; bạn thực sự cần phải xử lý cả hai kiểu tương tác và có thể phải hỗ trợ độc lập cho từng giao diện.
Quan trọng nhất: Người dùng có thể có thiết bị cảm ứng và chuột
Nhiều nhà phát triển đã xây dựng các trang web phát hiện tĩnh xem một môi trường có hỗ trợ sự kiện chạm hay không, sau đó giả định rằng họ chỉ cần hỗ trợ sự kiện chạm (chứ không phải sự kiện chuột). Đây hiện là một giả định không chính xác – thay vào đó, việc sự kiện chạm xuất hiện không có nghĩa là người dùng chủ yếu đang sử dụng thiết bị đầu vào cảm ứng đó. 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à phương thức nhập bằng Cảm ứng, và sẽ có nhiều thiết bị khác trong tương lai gần. Trên những thiết bị này, người dùng thườ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. Vì vậy, "hỗ trợ cảm ứng" không giống với "không cần hỗ trợ chuột". Bạn không thể xem vấn đề này 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", bạn cần suy nghĩ kỹ về cách cả hai kiểu 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 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 làm bất cứ điều gì cảm thấy tự nhiên nhất tại thời điểm đó. 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 hiếm khi sử dụng màn hình cảm ứng (nếu có). Vì vậy, việc có phương thức nhập bằng cảm ứng không được vô hiệu hoá hoặc cản trở việc điều khiển bằng chuột.
Rất tiếc, có thể khó biết liệu môi trường trình duyệt của người dùng có hỗ trợ phương thức nhập bằng thao tác chạm hay không; lý tưởng nhất là trình duyệt trên máy tính để bàn phải luôn cho biết hỗ trợ các sự kiện chạm để màn hình cảm ứng có thể được đính kèm bất cứ lúc nào (ví dụ: nếu màn hình cảm ứng được đính kèm 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 cố gắng 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à cảm ứng cùng nhau
#1 – Nhấp và nhấn – thứ tự "tự nhiên" của mọi thứ
Vấn đề đầu tiên là giao diện cảm ứng thường cố gắng mô phỏng các lượt nhấp chuột – rõ ràng là vì giao diện cảm ứng cần hoạt động trên các ứng dụng trước đây chỉ tương tác với các sự kiện chuột! Bạn có thể sử dụng mã 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 vào màn hình. Tuy nhiên, lối 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 hoạt động tương tác nâng cao hơn bằng thao tác chạm: khi người dùng sử dụng chuột, chuột sẽ phản hồi thông qua một 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à:
- touchstart
- touchmove
- touchend
- di chuột qua
- mousemove
- mousedown
- nhả chuột
- 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ư touchstart, bạn cần đảm bảo rằng bạn cũng không xử lý sự kiện mousedown và/hoặc sự kiện 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 của trình xử lý cảm ứng là:
Tuy nhiên, điều này cũng ngăn chặn các hành vi mặc định khác của trình duyệt (chẳng hạn như cuộn) – mặc dù thường thì bạn sẽ xử lý toàn bộ sự kiện chạm trong trình xử lý và bạn MUỐN tắt các thao tác mặc định. Nhìn chung, bạn nên xử lý và huỷ tất cả các sự kiện chạm hoặc tránh tạo 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ẽ có độ trễ ít nhất 300 mili giây giữa sự kiện touchstart và quá trình xử lý sự kiện chuột (mousedown). Bạn có thể thực hiện việc này bằng Chrome, bạn có thể bật tuỳ chọn "Mô phỏng sự kiện chạm" trong Công cụ cho nhà phát triển Chrome để giúp bạn kiểm thử giao diện cảm ứng trên hệ thống không có cảm ứng!
Độ trễ này là để cho 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à phóng to bằng thao tác nhấn đúp. Rõ ràng, điều này có thể gây ra vấn đề trong trường hợp bạn muốn phản hồi tức thì khi chạm ngón tay. Chúng tôi đang tiếp tục nỗ lực để cố gắng hạn chế các trường hợp độ trễ này tự động xảy ra.
Cách đầu tiên và dễ nhất để tránh độ trễ này là "cho" trình duyệt di động biết rằng trang của bạn 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 khung nhìn cố định, ví dụ: chèn vào trang của bạn:
<meta name="viewport" content="width=device-width,user-scalable=no">
Tất nhiên, điều 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 chụm để thu phóng, có thể cần thiết 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 tiết kiệm nếu có thể (nếu tắt tính năng điều chỉnh theo tỷ lệ người dùng, bạn nê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). Ngoài ra, đối với Chrome trên các thiết bị loại máy tính 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 sẽ không áp dụng.
#2: Sự kiện di chuột không được kích hoạt bằng thao tác chạm
Điều quan trọng cần lưu ý tại thời điểm này 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 đến việc mô phỏng các sự kiện mousemove – vì vậy, nếu bạn tạo một thành phần điều khiển đẹp mắt do chuột điều khiển sử dụng các sự kiện mousemove, thì thành phần điều khiển đó có thể 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ý touchmove.
Các trình duyệt thường tự động triển khai hoạt động tương tác thích hợp cho các hoạt động tương tác chạm trên các thành phần điều khiển HTML. Ví dụ: các thành phần điều khiển Phạm vi HTML5 sẽ chỉ hoạt động khi bạn sử dụng các hoạt động tương tác chạm. Tuy nhiên, nếu bạn đã triển khai các thành phần điều khiển của riêng mình, thì các thành phần này có thể sẽ không hoạt động trên các hoạt động 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ợ các hoạt động tương tác cảm ứng theo cách này (mặc dù đối với jQueryUI, có một số bản sửa lỗi vá khỉ 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 để hoạt động 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 thao tác tương tác nhấp và kéo. Tôi đã chuyển sang các chế độ điều khiển Phạm vi HTML5 và chúng hoạt động. Tất nhiên, tôi cũng có thể chỉ cần thêm trình xử lý touchmove để cập nhật thanh trượt, nhưng có một vấn đề với việc đó…
#3: Touchmove và MouseMove không giống nhau
Một cạm bẫy mà tôi thấy một số nhà phát triển mắc phải là các trình xử lý touchmove và mousemove gọi vào cùng một đường dẫn mã. Hành vi của các sự kiện này rất giống nhau, nhưng có sự khác biệt nhỏ – cụ thể là sự kiện chạm luôn nhắm đến phần tử mà thao tác chạm đó BẮT ĐẦU, trong khi sự kiện chuột nhắm đến phần tử hiện đang nằm dưới con trỏ chuột. Đây là lý do 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 tương ứng là chạm qua và chạm ra – chỉ có sự kiện chạm cuối.
Trường hợp phổ biến nhất có thể xảy ra là nếu 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ý cảm ứng trên toàn bộ băng chuyền để hỗ trợ hành vi cuộn tuỳ chỉnh. Khi hình ảnh có sẵn thay đổi, bạn sẽ xoá một số phần tử <img>
và thêm các phần tử khác. Nếu người dùng bắt đầu chạm vào một trong những hình ảnh đó rồi bạn xoá hình ảnh đó, thì trình xử lý (ở trên phần tử mẹ của phần tử img) sẽ chỉ ngừng nhận sự kiện chạm (vì các sự kiện đó đ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 vị trí mặc dù họ có thể đã di chuyển và cuối cùng xoá hình ảnh đó.
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ó phần tử 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. Ngoài ra, hướng dẫn tốt nhất là thay vì đăng ký trình xử lý touchend/touchmove tĩnh, hãy đợi đến khi bạn nhận được sự kiện touchstart rồi thêm trình xử lý touchmove/touchend/touchcancel vào target của sự kiện touchstart (và xoá các trình xử lý đó 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 thao tác chạm ngay cả khi phần tử mục tiêu bị di chuyển/xoá. Bạn có thể thử nghiệm một chút tại đây – chạm vào hộp màu đỏ và giữ phím thoát để xoá hộp đó khỏi DOM.
#4: Chạm và :Di chuyển
Hình ảnh con trỏ chuột tách biệt vị trí con trỏ với thao tác chọn chủ động, nhờ đó, nhà phát triển có thể sử dụng trạng thái di chuột để ẩn và hiển thị thông tin có thể liên quan đến 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 "lơ lửng" trên một mục tiêu – vì vậy, bạn không nên cung cấp thông tin quan trọng về ngữ nghĩa (ví dụ: cung cấp cửa sổ bật lên "đây là chế độ điều khiển gì?") dựa trên thao tác di chuột, trừ phi bạn cũng cung cấp một cách thân thiện với cảm ứng để truy cập vào thông tin này. Bạn cần cẩn thận về cách sử dụng tính năng di chuột để chuyển tiếp thông tin đến người dùng.
Tuy nhiên, điều thú vị là trong một số trường hợp, giao diện cảm ứng CÓ THỂ kích hoạt lớp giả :hover của CSS – thao tác nhấn vào một phần tử sẽ khiến phần tử đó :active trong khi ngón tay đang ở trạng thái nhấn xuống và phần tử đó cũng có trạng thái :hover. (Với Internet Explorer, :hover chỉ có hiệu lực khi ngón tay của người dùng chạm vào – các trình duyệt khác vẫn giữ hiệu lực của :hover 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 trình đơn bật lên hoạt động trên giao diện cảm ứng – hiệu ứng phụ của việc làm cho một phần tử hoạt động là trạng thái :hover 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>
Khi một phần tử khác được nhấn vào, phần tử đó sẽ không còn hoạt động và trạng thái di chuột sẽ biến mất, giống như khi người dùng đang sử dụng con trỏ chuột và di chuyển con trỏ ra khỏi phần tử đó. Bạn nên gói nội dung trong một phần tử <a>
để biến nội dung đó thành điểm dừng thẻ. 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 chuột, nhấn vào hoặc nhấn phím mà không cần 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 các giao diện cảm ứng mà trình đơn bật lên của tôi đã 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 thức trên hoạt động tốt cho các giao diện dựa trên con trỏ chuột cũng như cho các 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 "title" khi di chuột, thuộc tính này sẽ KHÔNG xuất hiện 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 thao tác bằng chuột
Mặc dù chuột có sự khác biệt về mặt khái niệm so với thực tế, nhưng thực tế là chuột lại 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 pixel cho con trỏ. Mặt khác, các nhà phát triển ứng dụng di động đã nhận thấy rằng thao tác chạm bằng ngón tay trên màn hình cảm ứng không chính xác bằng, chủ yếu là do kích thước của bề mặt ngón tay khi tiếp xúc với màn hình (và một phần là do ngón tay của bạn 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 để tìm hiểu cách thiết kế ứng dụng và trang web phù hợp với hoạt động tương tác bằng ngón tay. Nhiều cuốn sách cũng đã được viết về chủ đề này. Lời khuyên cơ bản là 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 đưa vào quá trình xử lý phát hiện lượt nhấn của các sự kiện chạm và nhấp, trong khi khoảng đệm thì được đưa vào.) Một trong những bản sửa lỗi chính mà tôi phải thực hiện cho Web Audio Playground là tăng kích thước của các điểm kết nối để dễ dàng chạm chính xác hơn.
Nhiều nhà cung cấp trình duyệt đang 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 chính xác phần tử khi người dùng chạm vào màn hình và giảm khả năng nhấp không chính xác – mặc dù điều này thường chỉ sửa các sự kiện nhấp chứ không phải các sự kiện di chuyển (mặc dù Internet Explorer dường như cũng sửa đổi các sự kiện mousedown/mousemove/mouseup).
#6: Giữ cho Trình xử lý cảm ứng được chứa, nếu không, chúng sẽ làm gián đoạn thao tác cuộn
Bạn cũng cần chỉ giới hạn trình xử lý cảm ứng ở những phần tử mà bạn cần; các phần tử cảm ứng có thể có băng thông rất cao, vì vậy, bạn cần tránh sử dụng 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ể can thiệp vào việc tối ưu hoá trình duyệt để cuộn cảm ứng nhanh và không bị giật – các trình duyệt hiện đại 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 ứng dụng có xử lý từng sự kiện chạm hay không). Bạn có thể xem ví dụ về hành vi này.
Một hướng dẫn để tránh vấn đề này là đảm bảo rằng nếu bạn chỉ xử lý các sự kiện chạm trong một phần nhỏ giao diện người dùng, thì bạn chỉ đính kèm trình xử lý chạm ở đó (chẳng hạn như không phải trên <body>
của trang); tóm lại, hãy giới hạn phạm vi của trình xử lý chạm nhiều nhất có thể.
#7: Cảm ứng đa điểm
Thách thức thú vị cuối cùng là mặc dù chúng ta gọi giao diện người dùng này là "Cảm ứng", nhưng gần như toàn bộ chức năng hỗ trợ thực sự là dành cho tính năng Cảm ứng đa điểm – tức là các API cung cấp nhiều phương thức nhập bằng thao tác chạm cùng một lúc. Khi bắt đầu hỗ trợ thao tác chạm trong ứng dụng, bạn nên cân nhắc mức độ ảnh hưởng của nhiều thao tác chạm đối với ứng dụng.
Nếu đã xây dựng ứng dụng chủ yếu bằng chuột, thì bạn đã quen với việc xây dựng với 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ào một giao diện con trỏ duy nhất, nhưng hầu hết phần cứng mà chúng ta đã thấy cho phương thức nhập bằng thao tác chạm trên máy tính đều có thể xử lý ít nhất 2 phương thức nhập đồng thời và hầu hết phần cứng mới đều hỗ trợ ít nhất 5 phương thức nhập đồng thời. Để phát triển bàn phím piano trên màn hình, tất nhiên, bạn muốn có thể hỗ trợ nhiều phương thức nhập bằng thao tác chạm đồng thời.
Các API cảm ứng W3C hiện được triển khai không có API để xác định số điểm chạm mà phần cứng hỗ trợ, vì vậy, bạn sẽ phải sử dụng số liệu ước tính tốt nhất về số điểm chạm mà người dùng muốn – hoặc tất nhiên, hãy chú ý đến số điểm chạm mà bạn thấy trong thực tế và điều chỉnh cho phù hợp. Ví dụ: trong ứng dụng piano, nếu không bao giờ thấy nhiều hơn hai điểm chạm, bạn nên thêm một số giao diện người dùng "hợp âm". PointerEvents API có một API để xác định chức năng của thiết bị.
Chỉnh sửa
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 thường gặp khi triển khai thao tác chạm cùng với các thao tác tương tác bằng chuột. Tất nhiên, quan trọng hơn mọi lời khuyên khác là bạn cần kiểm thử ứng dụng trên thiết bị di động, máy tính bảng và môi trường máy tính kết hợp chuột và cảm ứng. Nếu bạn không có phần cứng cảm ứng+chuột, hãy sử dụng tính năng "Mô phỏng sự kiện chạm" của Chrome để kiểm thử nhiều tình huống.
Bạn không chỉ có thể mà còn có thể dễ dàng làm theo các hướng dẫn này để tạo ra trải nghiệm tương tác hấp dẫn, hoạt động tốt với phương thức nhập bằng cảm ứng, nhập bằng chuột và thậm chí là cả hai kiểu tương tác cùng một lúc.