Nghiên cứu điển hình - Xây dựng hình tượng trưng trên Google của Stanisław Lem

Marcin Wichary
Marcin Wichary

Trang chủ của Google là một môi trường thú vị để lập trình. Công việc này đi kèm với nhiều quy định hạn chế đầy thách thức: tập trung đặc biệt vào tốc độ và độ trễ, phải phục vụ mọi loại trình duyệt và hoạt động trong nhiều trường hợp, và… vâng, phải làm người dùng ngạc nhiên và hài lòng.

Tôi đang nói về hình tượng trưng của Google, những hình minh hoạ đặc biệt đôi khi thay thế biểu trưng của chúng tôi. Mặc dù mối quan hệ của tôi với bút và cọ vẽ từ lâu đã có hương vị đặc trưng của lệnh cấm tiếp xúc, nhưng tôi thường đóng góp vào các tác phẩm tương tác.

Mọi bức vẽ nguệch ngoạc tương tác mà tôi đã lập trình (Pac-Man, Jules Verne, World's Fair) – và nhiều bức vẽ mà tôi đã giúp tạo – đều có cả yếu tố tương lai và lỗi thời: cơ hội tuyệt vời cho các ứng dụng không tưởng của các tính năng Web tiên tiến… và tính thực dụng rõ ràng của khả năng tương thích trên nhiều trình duyệt.

Chúng tôi học hỏi được nhiều điều từ mỗi bức ảnh động tương tác, và trò chơi mini Stanisław Lem gần đây cũng không ngoại lệ, với 17.000 dòng mã JavaScript,lần đầu tiên thử nghiệm nhiều điều trong lịch sử ảnh động. Hôm nay, tôi muốn chia sẻ mã đó với bạn – có thể bạn sẽ tìm thấy điều gì đó thú vị hoặc chỉ ra lỗi của tôi – và nói một chút về mã đó.

Xem mã vẽ nguệch ngoạc của Stanisław Lem »

Điều đáng lưu ý là trang chủ của Google không phải là nơi để giới thiệu công nghệ. Thông qua các bức vẽ nguệch ngoạc, chúng tôi muốn tôn vinh những người và sự kiện cụ thể, đồng thời muốn làm điều đó bằng cách sử dụng những hình ảnh nghệ thuật và công nghệ tốt nhất mà chúng tôi có thể huy động – nhưng không bao giờ tôn vinh công nghệ chỉ vì công nghệ. Điều này có nghĩa là bạn phải xem xét kỹ mọi phần của HTML5 được hiểu rộng rãi và liệu phần đó có giúp chúng ta vẽ nguệch ngoạc tốt hơn mà không làm mất tập trung hoặc làm lu mờ bản vẽ nguệch ngoạc hay không.

Vì vậy, hãy cùng xem một số công nghệ Web hiện đại đã xuất hiện – và một số công nghệ không xuất hiện – trong bức vẽ nguệch ngoạc của Stanisław Lem.

Đồ hoạ thông qua DOM và canvas

Canvas rất mạnh mẽ và được tạo ra để thực hiện chính xác những việc chúng ta muốn làm trong bức vẽ nguệch ngoạc này. Tuy nhiên, một số trình duyệt cũ mà chúng tôi quan tâm không hỗ trợ tính năng này – và mặc dù tôi đang chia sẻ văn phòng với người đã tạo ra một excanvas tuyệt vời, nhưng tôi quyết định chọn một cách khác.

Tôi đã tạo một công cụ đồ hoạ tóm tắt các đối tượng đồ hoạ gốc có tên là "rects", sau đó hiển thị các đối tượng đó bằng canvas hoặc DOM nếu không có canvas.

Phương pháp này đi kèm với một số thách thức thú vị – ví dụ: việc di chuyển hoặc thay đổi một đối tượng trong DOM sẽ có hậu quả ngay lập tức, trong khi đối với canvas, có một thời điểm cụ thể khi mọi thứ được vẽ cùng một lúc. (Tôi quyết định chỉ có một canvas, xoá canvas đó và vẽ lại từ đầu với mọi khung hình. Một mặt, có quá nhiều phần chuyển động – và mặt khác, không đủ độ phức tạp để đảm bảo việc chia thành nhiều canvas chồng chéo và cập nhật các canvas đó một cách có chọn lọc.)

Rất tiếc, việc chuyển sang canvas không đơn giản như việc chỉ phản chiếu nền CSS bằng drawImage(): bạn sẽ mất một số thứ miễn phí khi kết hợp các thứ với nhau thông qua DOM – quan trọng nhất là phân lớp bằng chỉ mục z và sự kiện chuột.

Tôi đã trừu tượng hoá chỉ mục z bằng một khái niệm có tên là "mặt phẳng". Hình vẽ nguệch ngoạc đã xác định một số mặt phẳng – từ bầu trời ở phía sau xa, đến con trỏ chuột ở phía trước mọi thứ – và mọi đối tượng trong hình vẽ nguệch ngoạc phải quyết định mặt phẳng nào thuộc về đối tượng đó (có thể sửa đổi cộng/trừ nhỏ trong một mặt phẳng bằng cách sử dụng planeCorrection).

Khi kết xuất thông qua DOM, các mặt phẳng chỉ được dịch thành chỉ mục z. Tuy nhiên, nếu kết xuất qua canvas, chúng ta cần sắp xếp các hình chữ nhật dựa trên các mặt phẳng của chúng trước khi vẽ. Vì việc này tốn kém mỗi khi thực hiện, nên thứ tự chỉ được tính toán lại khi thêm một đối tượng hoặc khi đối tượng đó chuyển sang một mặt phẳng khác.

Đối với các sự kiện chuột, tôi cũng đã trừu tượng hoá điều đó… theo cách nào đó. Đối với cả DOM và canvas, tôi đã sử dụng thêm các phần tử DOM nổi hoàn toàn trong suốt có chỉ mục z cao, chức năng của các phần tử này chỉ là phản ứng với thao tác di chuột qua/ra, nhấp và nhấn.

Một trong những điều chúng tôi muốn thử với bức vẽ nguệch ngoạc này là phá vỡ bức tường thứ tư. Công cụ trên cho phép chúng ta kết hợp các thành phần dựa trên canvas với các thành phần dựa trên DOM. Ví dụ: các vụ nổ trong phần cuối đều nằm trong canvas cho các đối tượng trong vũ trụ và trong DOM cho phần còn lại của trang chủ Google. Con chim, thường bay xung quanh và bị cắt bởi mặt nạ lởm chởm của chúng ta như mọi diễn viên khác, quyết định tránh rắc rối trong cấp độ chụp và ngồi trên nút I’m Feeling Lucky (Tôi đang may mắn). Cách thực hiện là để chim rời khỏi canvas và trở thành một phần tử DOM (và ngược lại sau này). Tôi hy vọng điều này sẽ hoàn toàn minh bạch với khách truy cập.

Tốc độ khung hình

Việc biết tốc độ khung hình hiện tại và phản ứng khi tốc độ khung hình quá chậm (và quá nhanh!) là một phần quan trọng trong công cụ của chúng tôi. Vì trình duyệt không báo cáo lại tốc độ khung hình, nên chúng ta phải tự tính toán tốc độ khung hình.

Tôi bắt đầu bằng cách sử dụng requestAnimationFrame, quay lại setTimeout kiểu cũ nếu không có phương thức trước đó. requestAnimationFrame tiết kiệm CPU một cách thông minh trong một số trường hợp – mặc dù chúng ta đang tự thực hiện một số việc đó, như sẽ được giải thích bên dưới – nhưng cũng chỉ cho phép chúng ta có tốc độ khung hình cao hơn setTimeout.

Việc tính toán tốc độ khung hình hiện tại rất đơn giản, nhưng có thể thay đổi đáng kể – ví dụ: tốc độ khung hình có thể giảm nhanh khi một ứng dụng khác chiếm dụng máy tính trong một thời gian. Do đó, chúng tôi chỉ tính tốc độ khung hình "lăn" (trung bình) trên mỗi 100 kim đánh dấu nhịp độ khung hình thực và đưa ra quyết định dựa trên đó.

Loại quyết định nào?

  • Nếu tốc độ khung hình cao hơn 60 khung hình/giây, chúng tôi sẽ điều tiết tốc độ khung hình. Hiện tại, requestAnimationFrame trên một số phiên bản Firefox không có giới hạn trên về tốc độ khung hình và không có lý do gì để lãng phí CPU. Xin lưu ý rằng chúng ta thực sự giới hạn ở tốc độ 65 khung hình/giây, do các lỗi làm tròn khiến tốc độ khung hình chỉ cao hơn một chút so với 60 khung hình/giây trên các trình duyệt khác – chúng ta không muốn bắt đầu điều tiết tốc độ đó do nhầm lẫn.

  • Nếu tốc độ khung hình thấp hơn 10 khung hình/giây, chúng ta chỉ cần làm chậm công cụ thay vì bỏ khung hình. Đây là một đề xuất không có lợi cho cả hai bên, nhưng tôi cảm thấy việc bỏ qua quá nhiều khung hình sẽ gây nhầm lẫn hơn so với việc trò chơi chạy chậm hơn (nhưng vẫn nhất quán). Việc này còn có một hiệu ứng phụ thú vị khác – nếu hệ thống tạm thời bị chậm, người dùng sẽ không gặp phải tình trạng chuyển tiếp kỳ lạ khi công cụ đang cố gắng bắt kịp. (Tôi đã thực hiện một cách khác cho Pac-Man, nhưng tốc độ khung hình tối thiểu là một phương pháp tốt hơn.)

  • Cuối cùng, chúng ta có thể nghĩ đến việc đơn giản hoá đồ hoạ khi tốc độ khung hình xuống thấp một cách nguy hiểm. Chúng ta sẽ không làm điều này cho bức vẽ nguệch ngoạc của Lem, ngoại trừ con trỏ chuột (xem thêm ở bên dưới), nhưng giả sử chúng ta có thể mất một số ảnh động thừa chỉ để bức vẽ nguệch ngoạc có cảm giác mượt mà ngay cả trên những máy tính chạy chậm hơn.

Chúng ta cũng có khái niệm về kim đánh dấu nhịp độ khung hình thực và kim đánh dấu nhịp độ khung hình logic. Giá trị trước đây đến từ requestAnimationFrame/setTimeout. Tỷ lệ trong trò chơi thông thường là 1:1, nhưng để tua nhanh, chúng ta chỉ cần thêm nhiều kim đánh dấu nhịp độ khung hình logic hơn cho mỗi kim đánh dấu nhịp độ khung hình thực (tối đa là 1:5). Điều này cho phép chúng ta thực hiện tất cả các phép tính cần thiết cho mỗi kim đánh dấu nhịp độ khung hình logic, nhưng chỉ chỉ định kim đánh dấu nhịp độ khung hình cuối cùng là kim cập nhật các nội dung trên màn hình.

Đo điểm chuẩn

Bạn có thể giả định (và thực sự là từ đầu) rằng canvas sẽ nhanh hơn DOM bất cứ khi nào có sẵn. Điều này không phải lúc nào cũng đúng. Trong quá trình kiểm thử, chúng tôi nhận thấy rằng Opera 10.0–10.1 trên máy Mac và Firefox trên Linux thực sự nhanh hơn khi di chuyển các phần tử DOM.

Trong thế giới lý tưởng, hình vẽ nguệch ngoạc sẽ tự động đo điểm chuẩn cho nhiều kỹ thuật đồ hoạ – các phần tử DOM được di chuyển bằng style.leftstyle.top, vẽ trên canvas và thậm chí có thể cả các phần tử DOM được di chuyển bằng phép biến đổi CSS3

– rồi chuyển sang bất kỳ chế độ nào có tốc độ khung hình cao nhất. Tôi bắt đầu viết mã cho việc đó, nhưng nhận thấy rằng ít nhất thì cách đo điểm chuẩn của tôi khá không đáng tin cậy và tốn nhiều thời gian. Thời gian mà chúng tôi không có trên trang chủ – chúng tôi rất quan tâm đến tốc độ và muốn bức vẽ nguệch ngoạc xuất hiện ngay lập tức và trò chơi bắt đầu ngay khi bạn nhấp hoặc nhấn.

Cuối cùng, đôi khi việc phát triển web chỉ là phải làm những việc bạn phải làm. Tôi nhìn qua vai để đảm bảo không có ai đang nhìn, sau đó tôi chỉ mã hoá cứng Opera 10 và Firefox ra khỏi canvas. Trong kiếp sau, tôi sẽ trở lại dưới dạng thẻ <marquee>.

Tiết kiệm CPU

Bạn có biết người bạn đến nhà bạn, xem tập cuối của Breaking Bad, tiết lộ nội dung cho bạn rồi xoá tập đó khỏi DVR không? Bạn không muốn trở thành người đó, phải không?

Vì vậy, đây là phép so sánh tệ nhất từ trước đến nay. Tuy nhiên, chúng ta cũng không muốn bức vẽ nguệch ngoạc của mình trở thành một kẻ như vậy. Việc được phép truy cập vào thẻ trình duyệt của người dùng là một đặc quyền, và việc chiếm dụng chu kỳ CPU hoặc làm người dùng mất tập trung sẽ khiến chúng ta trở thành một vị khách không được chào đón. Do đó, nếu không có ai chơi với hình vẽ nguệch ngoạc (không có thao tác nhấn, nhấp chuột, di chuyển chuột hoặc nhấn phím), thì cuối cùng chúng ta muốn hình vẽ nguệch ngoạc đó chuyển sang trạng thái ngủ.

Thời gian?

  • sau 18 giây trên trang chủ (các trò chơi arcade gọi đây là chế độ thu hút)
  • sau 180 giây nếu thẻ có tiêu điểm
  • sau 30 giây nếu thẻ không có tiêu điểm (ví dụ: người dùng đã chuyển sang một cửa sổ khác, nhưng có thể vẫn đang xem hình vẽ nguệch ngoạc trong một thẻ không hoạt động)
  • ngay lập tức nếu thẻ không hiển thị (ví dụ: người dùng chuyển sang một thẻ khác trong cùng một cửa sổ – không cần lãng phí chu kỳ nếu chúng ta không thể hiển thị)

Làm cách nào để biết thẻ hiện có tiêu điểm? Chúng ta đính kèm vào window.focuswindow.blur. Làm cách nào để biết thẻ đó hiển thị? Chúng ta đang sử dụng Page Visibility API (API chế độ hiển thị trang) mới và phản ứng với sự kiện thích hợp.

Thời gian chờ nêu trên sẽ được nới lỏng hơn so với thông thường. Tôi đã điều chỉnh các lớp này cho phù hợp với bức vẽ nguệch ngoạc cụ thể này, trong đó có nhiều ảnh động xung quanh (chủ yếu là bầu trời và chim). Lý tưởng nhất là thời gian chờ sẽ được kiểm soát bằng hoạt động tương tác trong trò chơi – ví dụ: ngay sau khi hạ cánh, chim có thể báo cáo lại cho hình vẽ nguệch ngoạc rằng nó có thể ngủ ngay bây giờ – nhưng cuối cùng tôi đã không triển khai điều đó.

Vì bầu trời luôn chuyển động, nên khi ngủ và thức dậy, hình vẽ nguệch ngoạc không chỉ dừng lại hoặc bắt đầu – mà còn chậm lại trước khi tạm dừng và ngược lại khi tiếp tục, tăng hoặc giảm số lượng kim đánh dấu thời gian logic trên mỗi kim đánh dấu thời gian thực tế nếu cần.

Hiệu ứng chuyển đổi, biến đổi, sự kiện

Một trong những thế mạnh của HTML luôn là bạn có thể tự cải thiện nó: nếu có gì đó chưa đủ tốt trong danh mục đầu tư thông thường của HTML và CSS, bạn có thể sử dụng JavaScript để mở rộng nó. Rất tiếc, điều này thường có nghĩa là bạn phải bắt đầu lại từ đầu. Các hiệu ứng chuyển đổi CSS3 rất tuyệt vời, nhưng bạn không thể thêm loại hiệu ứng chuyển đổi mới hoặc sử dụng hiệu ứng chuyển đổi để làm gì khác ngoài việc tạo kiểu cho các phần tử. Một ví dụ khác: các phép biến đổi CSS3 rất phù hợp với DOM, nhưng khi chuyển sang canvas, bạn sẽ phải tự mình làm mọi thứ.

Những vấn đề này và nhiều vấn đề khác là lý do khiến Lem doodle có công cụ chuyển đổi và chuyển đổi riêng. Vâng, tôi biết, những năm 2000 đã gọi, v.v. – các tính năng mà tôi tích hợp không mạnh bằng CSS3, nhưng bất kỳ việc gì mà công cụ này thực hiện, nó đều thực hiện một cách nhất quán và cho phép chúng ta kiểm soát nhiều hơn.

Tôi bắt đầu với một hệ thống hành động (sự kiện) đơn giản – một tiến trình kích hoạt các sự kiện trong tương lai mà không cần sử dụng setTimeout, vì tại bất kỳ thời điểm nào, thời gian vẽ nguệch ngoạc có thể tách biệt với thời gian thực khi thời gian vẽ nguệch ngoạc nhanh hơn (chuyển nhanh), chậm hơn (tốc độ khung hình thấp hoặc chuyển sang chế độ ngủ để tiết kiệm CPU) hoặc dừng hoàn toàn (chờ hình ảnh tải xong).

Hiệu ứng chuyển đổi chỉ là một loại thao tác khác. Ngoài các chuyển động và xoay cơ bản, chúng tôi cũng hỗ trợ các chuyển động tương đối (ví dụ: di chuyển một đối tượng 10 pixel sang phải), các hiệu ứng tuỳ chỉnh như run rẩy và cả ảnh động khung hình chính.

Tôi đã đề cập đến việc xoay và những thao tác này cũng được thực hiện theo cách thủ công: chúng ta có các sprite cho nhiều góc độ cho các đối tượng cần xoay. Lý do chính là cả CSS3 và thao tác xoay canvas đều tạo ra các cấu phần hiển thị mà chúng tôi không chấp nhận được – ngoài ra, các cấu phần hiển thị đó còn khác nhau tuỳ theo nền tảng.

Do một số đối tượng xoay được đính kèm vào các đối tượng xoay khác – ví dụ: tay của rô-bốt được kết nối với cánh tay dưới, cánh tay dưới này được đính kèm vào cánh tay trên xoay – điều này có nghĩa là tôi cũng cần tạo một transform-origin của người nghèo ở dạng trục xoay.

Tất cả những điều này là một khối lượng công việc vững chắc, cuối cùng sẽ bao gồm những nền tảng đã được HTML5 xử lý – nhưng đôi khi, tính năng hỗ trợ gốc không đủ tốt và đã đến lúc cần phải phát minh lại.

Xử lý hình ảnh và sprite

Công cụ không chỉ dùng để chạy hình vẽ nguệch ngoạc mà còn dùng để xử lý hình vẽ nguệch ngoạc. Tôi đã chia sẻ một số tham số gỡ lỗi ở trên: bạn có thể tìm thấy các tham số còn lại trong engine.readDebugParams.

Tạo ảnh động là một kỹ thuật nổi tiếng mà chúng ta cũng sử dụng cho các bức vẽ nguệch ngoạc. Điều này cho phép chúng ta tiết kiệm byte và giảm thời gian tải, đồng thời giúp tải trước dễ dàng hơn. Tuy nhiên, điều này cũng khiến quá trình phát triển trở nên khó khăn hơn – mọi thay đổi đối với hình ảnh sẽ yêu cầu phải tạo lại ảnh động (chủ yếu là tự động, nhưng vẫn cồng kềnh). Do đó, công cụ này hỗ trợ chạy trên hình ảnh thô để phát triển cũng như ảnh động để sản xuất thông qua engine.useSprites – cả hai đều có trong mã nguồn.

Hình tượng trưng Pac-Man
Các sprite được sử dụng trong hình vẽ Pac-Man.

Chúng tôi cũng hỗ trợ tải trước hình ảnh trong khi vẽ và tạm dừng vẽ nếu hình ảnh không tải kịp – hoàn chỉnh với một thanh tiến trình giả! (Giả vì thật không may, ngay cả HTML5 cũng không thể cho chúng ta biết lượng tệp hình ảnh đã tải.)

Ảnh chụp màn hình đồ hoạ đang tải với thanh tiến trình bị can thiệp.
Ảnh chụp màn hình đồ hoạ đang tải với thanh tiến trình bị can thiệp.

Đối với một số cảnh, chúng ta sử dụng nhiều sprite không phải để tăng tốc tải bằng cách sử dụng các kết nối song song, mà chỉ đơn giản là do giới hạn 3/5 triệu pixel đối với hình ảnh trên iOS.

HTML5 phù hợp với tất cả những điều này ở đâu? Không có nhiều nội dung ở trên, nhưng công cụ mà tôi viết để tạo ảnh cắt/cắt ảnh đều là công nghệ Web mới: canvas, blob, a[download]. Một trong những điều thú vị về HTML là ngôn ngữ này dần dần bao gồm những việc trước đây phải làm bên ngoài trình duyệt; phần duy nhất chúng ta cần làm là tối ưu hoá tệp PNG.

Lưu trạng thái giữa các trò chơi

Thế giới của Lem luôn rộng lớn, sống động và chân thực. Các câu chuyện của ông thường bắt đầu mà không có nhiều nội dung giải thích, trang đầu tiên bắt đầu từ giữa câu chuyện, trong đó người đọc phải tự tìm hiểu.

Cyberiad cũng không ngoại lệ và chúng tôi muốn tái hiện cảm giác đó cho bức vẽ nguệch ngoạc. Chúng ta bắt đầu bằng cách cố gắng không giải thích quá nhiều về câu chuyện. Một phần lớn khác là tính ngẫu nhiên mà chúng tôi cảm thấy phù hợp với bản chất cơ học của vũ trụ trong cuốn sách; chúng tôi có một số hàm trợ giúp xử lý tính ngẫu nhiên mà chúng tôi sử dụng ở rất nhiều nơi.

Chúng tôi cũng muốn tăng khả năng chơi lại theo những cách khác. Để làm được điều đó, chúng ta cần biết số lần bức vẽ nguệch ngoạc đã hoàn thành trước đó. Giải pháp công nghệ chính xác về mặt lịch sử cho vấn đề đó là cookie, nhưng giải pháp này không phù hợp với trang chủ của Google – mỗi cookie đều làm tăng tải trọng của mỗi trang và xin nhắc lại rằng chúng tôi rất quan tâm đến tốc độ và độ trễ.

May mắn thay, HTML5 cung cấp cho chúng ta Bộ nhớ web, dễ sử dụng, cho phép chúng ta lưu và gọi lại số lượt phát chung và cảnh cuối cùng mà người dùng đã phát – với nhiều tính năng hơn so với cookie.

Chúng tôi làm gì với thông tin này?

  • chúng ta hiển thị một nút tua nhanh, cho phép xem nhanh các đoạn cắt cảnh mà người dùng đã xem trước đó
  • chúng ta hiển thị nhiều mục N trong phần kết thúc
  • chúng tôi tăng nhẹ độ khó của cấp độ chụp
  • chúng tôi sẽ hiển thị một con rồng xác suất trứng phục sinh nhỏ trong một câu chuyện khác vào lần chơi thứ ba và các lần chơi tiếp theo

Có một số tham số gỡ lỗi kiểm soát việc này:

  • ?doodle-debug&doodle-first-run – giả vờ đây là lần chạy đầu tiên
  • ?doodle-debug&doodle-second-run – giả vờ đây là lần chạy thứ hai
  • ?doodle-debug&doodle-old-run – giả vờ đó là một lần chạy cũ

Thiết bị cảm ứng

Chúng tôi muốn trò chơi này hoạt động tốt trên các thiết bị cảm ứng – những thiết bị hiện đại nhất có đủ sức mạnh để trò chơi chạy mượt mà và trải nghiệm trò chơi bằng cách nhấn vào màn hình sẽ thú vị hơn nhiều so với việc nhấp chuột.

Cần thực hiện một số thay đổi trước đối với trải nghiệm người dùng. Ban đầu, con trỏ chuột là nơi duy nhất cho biết một cảnh cắt/phần không tương tác đang diễn ra. Sau đó, chúng tôi đã thêm một chỉ báo nhỏ ở góc dưới bên phải để không phải chỉ dựa vào con trỏ chuột (vì những con trỏ này không tồn tại trên các thiết bị cảm ứng).

Bình thường Bận Nhấp vào được Đã nhấp
Đang tiến hành
Con trỏ thông thường đang trong quá trình xử lý
Con trỏ đang bận của công việc đang tiến hành
Con trỏ có thể nhấp vào đang trong quá trình xử lý
Con trỏ đã nhấp vào công việc đang tiến hành
Chung kết
Con trỏ pháp tuyến cuối cùngv
Con trỏ bận cuối cùng
Con trỏ nhấp được cuối cùng
Con trỏ được nhấp cuối cùng
Con trỏ chuột trong quá trình phát triển và các con trỏ tương đương cuối cùng.

Hầu hết các tính năng đều hoạt động ngay từ đầu. Tuy nhiên, các thử nghiệm nhanh về khả năng hữu dụng của trải nghiệm cảm ứng cho thấy hai vấn đề: một số mục tiêu quá khó nhấn và các thao tác nhấn nhanh bị bỏ qua vì chúng ta chỉ ghi đè các sự kiện nhấp chuột.

Việc có các phần tử DOM trong suốt có thể nhấp riêng biệt đã giúp ích rất nhiều ở đây, vì tôi có thể đổi kích thước các phần tử đó độc lập với hình ảnh. Tôi đã thêm khoảng đệm 15 pixel cho các thiết bị cảm ứng và sử dụng khoảng đệm này bất cứ khi nào tạo các phần tử có thể nhấp. (Tôi cũng thêm khoảng đệm 5 pixel cho môi trường chuột, chỉ để làm hài lòng ông Fitts.)

Đối với vấn đề khác, tôi chỉ cần đảm bảo đính kèm và kiểm thử trình xử lý bắt đầu và kết thúc thao tác chạm thích hợp, thay vì dựa vào thao tác nhấp chuột.

Chúng tôi cũng đang sử dụng các thuộc tính kiểu hiện đại hơn để xoá một số tính năng chạm mà trình duyệt WebKit thêm theo mặc định (nhấn vào điểm nổi bật, nhấn vào chú thích).

Và làm cách nào để phát hiện xem một thiết bị cụ thể đang chạy hình vẽ nguệch ngoạc có hỗ trợ tính năng chạm hay không? Theo cách lười biếng. Thay vì tìm hiểu trước, chúng ta chỉ sử dụng chỉ số IQ kết hợp để suy ra rằng thiết bị hỗ trợ cảm ứng… sau khi nhận được sự kiện bắt đầu chạm đầu tiên.

Tuỳ chỉnh con trỏ chuột

Tuy nhiên, không phải mọi thứ đều dựa trên thao tác chạm. Một trong những nguyên tắc chỉ đạo của chúng tôi là đưa nhiều nội dung nhất có thể vào trong bức vẽ nguệch ngoạc. Giao diện người dùng thanh bên nhỏ (tiến nhanh, dấu chấm hỏi), chú giải công cụ và thậm chí là con trỏ chuột.

Làm cách nào để tuỳ chỉnh con trỏ chuột? Một số trình duyệt cho phép thay đổi con trỏ chuột bằng cách liên kết đến một tệp hình ảnh tuỳ chỉnh. Tuy nhiên, tính năng này không được hỗ trợ tốt và cũng có phần hạn chế.

Nếu không phải là cách này thì là cách nào? Vậy tại sao không biến con trỏ chuột thành một đối tượng khác trong bức vẽ nguệch ngoạc? Cách này hoạt động, nhưng có một số lưu ý, chủ yếu là:

  • bạn cần có thể xoá con trỏ chuột gốc
  • bạn cần phải đồng bộ hoá con trỏ chuột với con trỏ "thực"

Trường hợp trước khá phức tạp. CSS3 cho phép cursor: none, nhưng thuộc tính này cũng không được hỗ trợ trong một số trình duyệt. Chúng tôi cần phải sử dụng một số thao tác: sử dụng tệp .cur trống làm phương án dự phòng, chỉ định hành vi cụ thể cho một số trình duyệt và thậm chí là mã hoá cứng các trình duyệt khác khỏi trải nghiệm bất kỳ.

Vấn đề còn lại tương đối nhỏ, nhưng với con trỏ chuột chỉ là một phần khác của vũ trụ trong hình vẽ nguệch ngoạc, nó cũng sẽ kế thừa tất cả các vấn đề của hình vẽ nguệch ngoạc. Lớn nhất? Nếu tốc độ khung hình của hình vẽ nguệch ngoạc thấp, thì tốc độ khung hình của con trỏ chuột cũng sẽ thấp. Điều này sẽ gây ra hậu quả nghiêm trọng vì con trỏ chuột là một phần mở rộng tự nhiên của bàn tay, cần phải phản hồi nhanh chóng bất kể điều gì. (Những người từng sử dụng Commodore Amiga hiện đang gật đầu mạnh mẽ.)

Một giải pháp hơi phức tạp cho vấn đề đó là tách rời con trỏ chuột khỏi vòng lặp cập nhật thông thường. Chúng tôi đã làm như vậy – trong một vũ trụ thay thế nơi tôi không cần ngủ. Có giải pháp đơn giản hơn cho vấn đề này không? Bạn chỉ cần quay lại con trỏ chuột gốc nếu tốc độ khung hình cuộn giảm xuống dưới 20 khung hình/giây. (Đây là lúc tốc độ khung hình lăn phát huy tác dụng. Nếu chúng ta phản ứng với tốc độ khung hình hiện tại và nếu tốc độ khung hình dao động xung quanh 20 khung hình/giây, thì người dùng sẽ thấy con trỏ chuột tuỳ chỉnh ẩn và hiển thị mọi lúc.) Điều này đưa chúng ta đến:

Phạm vi tốc độ khung hình Hành vi
>10 khung hình/giây Làm chậm trò chơi để không bị bỏ lỡ nhiều khung hình hơn.
10–20 khung hình/giây Sử dụng con trỏ chuột gốc thay vì con trỏ tuỳ chỉnh.
20–60 khung hình/giây Hoạt động bình thường.
>60 khung hình/giây Điều tiết để tốc độ khung hình không vượt quá giá trị này.
Tóm tắt hành vi phụ thuộc vào tốc độ khung hình.

À, con trỏ chuột của chúng ta có màu tối trên máy Mac, nhưng có màu trắng trên máy tính. Tại sao? Vì cuộc chiến giữa các nền tảng cần có nhiên liệu ngay cả trong vũ trụ hư cấu.

Kết luận

Đây không phải là một công cụ hoàn hảo, nhưng nó không cố gắng trở thành một công cụ hoàn hảo. Biểu tượng này được phát triển cùng với hình vẽ nguệch ngoạc Lem và rất phù hợp với hình vẽ đó. Đó là điều bình thường. "Tối ưu hoá sớm là nguồn gốc của mọi điều xấu", như Don Knuth từng nói, và tôi không tin rằng việc viết một công cụ riêng biệt trước rồi chỉ áp dụng công cụ đó sau này là hợp lý – thực hành cung cấp thông tin cho lý thuyết cũng nhiều như lý thuyết cung cấp thông tin cho thực hành. Trong trường hợp của tôi, mã đã bị loại bỏ, một số phần được viết lại nhiều lần và nhiều phần phổ biến đã được ghi nhận sau khi sự kiện xảy ra, thay vì trước khi sự kiện xảy ra. Nhưng cuối cùng, những gì chúng tôi có ở đây đã cho phép chúng tôi làm những gì mình muốn – kỷ niệm sự nghiệp của Stanisław Lem và các bức vẽ của Daniel Mróz theo cách tốt nhất mà chúng tôi có thể nghĩ ra.

Tôi hy vọng những thông tin trên đã làm sáng tỏ một số lựa chọn thiết kế và sự đánh đổi mà chúng tôi cần thực hiện, cũng như cách chúng tôi sử dụng HTML5 trong một tình huống cụ thể, thực tế. Bây giờ, hãy thử nghiệm với mã nguồn, sử dụng mã nguồn và cho chúng tôi biết ý kiến của bạn.

Tôi đã tự làm việc đó – hình ảnh bên dưới được phát trực tiếp trong những ngày cuối cùng, đếm ngược đếnrạng sáng ngày 23 tháng 11 năm 2011 ở Nga, múi giờ đầu tiên nhìn thấy hình minh hoạ Lem. Có thể đây là một điều ngớ ngẩn, nhưng giống như những bức vẽ nguệch ngoạc, những thứ có vẻ không quan trọng đôi khi lại có ý nghĩa sâu sắc hơn – bộ đếm này thực sự là một "kiểm thử tải" hiệu quả cho công cụ.

Ảnh chụp màn hình đồng hồ đếm ngược trong vũ trụ của bức vẽ nguệch ngoạc Lem.
Ảnh chụp màn hình đồng hồ đếm ngược trong vũ trụ của bức vẽ nguệch ngoạc Lem.

Đó là một cách nhìn về vòng đời của một bức ảnh động của Google – hàng tháng trời làm việc, hàng tuần thử nghiệm, 48 giờ để hoàn thiện, tất cả chỉ để tạo ra một trò chơi mà mọi người chơi trong 5 phút. Mỗi một trong hàng nghìn dòng JavaScript đó đều hy vọng rằng 5 phút đó sẽ là thời gian được sử dụng hiệu quả. Chúc bạn vui vẻ!