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 việc triển khai iOS. Các giao diện này hiện được mô tả trong thông số kỹ thuật của "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 hỗ trợ, 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.
Đồng nghiệp của tôi, Boris Smus đã viết một hướng dẫn HTML5Rocks tuyệt vời về sự kiện Chạm. Đây vẫn là một cách hay để bắt đầu nếu bạn chưa từng xem các sự kiện Chạm 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, hãy đọc bài viết đó ngay trước khi tiếp tục. Tiếp tục đi, tôi sẽ chờ.
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ợ 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à dùng 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à Chạm và nhiều phương thức khác trong tương lai gần. Trên các 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 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 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 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ể dùng lệnh 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, có một vài vấn đề với phím tắt này.
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 cách 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
- điểm chạm
- 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 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ý của mình và bạn sẽ MUỐN tắt các hành động 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 có 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ẽ bị trễ ít nhất 300 mili giây giữa sự kiện bắt đầu chạm 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ụ dành 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. Có một công việc đang diễn ra để cố gắng hạn chế các trường hợp mà sự chậm 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, việ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 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 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 gần nhau, nhưng khác nhau một cách tinh tế - cụ thể là các sự kiện chạm luôn nhắm mục tiêu phần tử mà chạm vào BẮT ĐẦU, trong khi các sự kiện chuột nhắm mục tiêu 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ó các sự kiện chạm và chạm tương ứng mà chỉ có các sự kiện chạm.
Trường hợp phổ biến nhất có thể khiến bạn gặp rắc rối 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
Phép ẩn dụ con trỏ chuột ngăn không cho vị trí con trỏ chủ động chọn, đồng thời cho phép nhà phát triển sử dụng các trạng thái di chuột để ẩn và hiển thị thông tin có thể cần thiết cho 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à lớp giả CSS :hover CÓ THỂ được kích hoạt bởi các giao diện cảm ứng trong một số trường hợp – thao tác nhấn vào một phần tử sẽ khiến nó :active trong khi ngón tay hướng xuống và cũng nhận đượ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 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 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 "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ó khái niệm tách biệt 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 người dùng sâu rộng về cách thiết kế các ứng dụng và trang web hỗ trợ tương tác dựa trên ngón tay, đồng thời nhiều cuốn sách đã đượ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 bao gồm trong quá trình xử lý phát hiện lượt truy cập của các sự kiện chạm và nhấp, trong khi khoảng đệm thì được tính.) 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 cần tuân thủ để 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 đính kèm 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 bạn đang tạo các ứng dụng chủ yếu được điều khiển bằng chuột, thì bạn sẽ 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 một ứng dụng đàn piano, nếu không bao giờ thấy nhiều hơn hai điểm tiếp xúc, thì bạn có thể thêm một số giao diện người dùng "chords". PointerEvents API có một API để xác định chức năng của thiết bị.
Dặm lại
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.