Chrome के साथ बनाएं

मल्टी-डिवाइस वेब पर LEGO® ब्रिक की सुविधा

Build with Chrome, डेस्कटॉप पर Chrome इस्तेमाल करने वालों के लिए एक मज़ेदार एक्सपेरिमेंट है. इसे शुरुआत में ऑस्ट्रेलिया में लॉन्च किया गया था. इसे 2014 में फिर से रिलीज़ किया गया था. इसमें दुनिया भर में उपलब्धता, THE LEGO® MOVIE™ के साथ टाई-इन, और मोबाइल डिवाइसों के लिए नई सुविधाएं जोड़ी गई थीं. इस लेख में, हम इस प्रोजेक्ट से मिली कुछ अहम जानकारी शेयर करेंगे. खास तौर पर, सिर्फ़ डेस्कटॉप पर काम करने वाले वर्शन को, एक से ज़्यादा स्क्रीन वाले ऐसे वर्शन में बदलने के बारे में जानकारी देंगे जो माउस और टच इनपुट, दोनों के साथ काम करता है.

Chrome के साथ Build का इतिहास

Build with Chrome का पहला वर्शन, साल 2012 में ऑस्ट्रेलिया में लॉन्च किया गया था. हम वेब की ताकत को एक नए तरीके से दिखाना चाहते थे और Chrome को नई ऑडियंस तक पहुंचाना चाहते थे.

इस साइट के दो मुख्य हिस्से थे: "बिल्ड" मोड, जहां उपयोगकर्ता लेगो ब्रिक का इस्तेमाल करके क्रिएशन बना सकते हैं और "एक्सप्लोर करें" मोड, जहां Google Maps के लेगो वर्शन पर क्रिएशन ब्राउज़ किए जा सकते हैं.

उपयोगकर्ताओं को लेगो बनाने का सबसे अच्छा अनुभव देने के लिए, इंटरैक्टिव 3D ज़रूरी था. साल 2012 में, WebGL सिर्फ़ डेस्कटॉप ब्राउज़र में सार्वजनिक तौर पर उपलब्ध था. इसलिए, Build को सिर्फ़ डेस्कटॉप पर इस्तेमाल करने के लिए बनाया गया था. Explore में क्रिएशन दिखाने के लिए Google Maps का इस्तेमाल किया जाता है. हालांकि, ज़ूम इन करने पर, यह मैप के WebGL वर्शन पर स्विच हो जाता है. इस वर्शन में क्रिएशन को 3D में दिखाया जाता है. हालांकि, इसमें Google Maps का इस्तेमाल बेसप्लेट टेक्स्चर के तौर पर किया जाता है. हमने ऐसा प्लैटफ़ॉर्म बनाने की कोशिश की है जहां LEGO के हर उम्र के प्रशंसक आसानी से और सहजता से अपनी क्रिएटिविटी दिखा सकें. साथ ही, एक-दूसरे के बनाए गए मॉडल भी देख सकें.

हमने 2013 में, Build with Chrome को नई वेब टेक्नोलॉजी के लिए उपलब्ध कराने का फ़ैसला लिया था. इनमें से एक टेक्नोलॉजी, Android के लिए Chrome में WebGL थी. इसकी मदद से, Build with Chrome को मोबाइल पर इस्तेमाल किया जा सकता था. शुरू करने के लिए, हमने "बिल्डर टूल" के लिए हार्डवेयर से जुड़े सवाल पूछने से पहले, टच प्रोटोटाइप बनाए. इससे हमें जेस्चर के व्यवहार और टैक्टाइल रिस्पॉन्सिवनेस को समझने में मदद मिली. मोबाइल ऐप्लिकेशन की तुलना में, ब्राउज़र में हमें इन समस्याओं का सामना करना पड़ सकता है.

रिस्पॉन्सिव फ़्रंट-एंड

हमें टच और माउस, दोनों तरह के इनपुट वाले डिवाइसों के साथ काम करने की ज़रूरत थी. हालांकि, छोटी टच स्क्रीन पर एक ही यूज़र इंटरफ़ेस (यूआई) का इस्तेमाल करना, जगह की कमी की वजह से सही नहीं था.

Build में बहुत सारी इंटरैक्टिविटी होती है: ज़ूम इन और ज़ूम आउट करना, ईंटों के रंग बदलना, और ज़ाहिर है कि ईंटों को चुनना, घुमाना, और रखना. यह एक ऐसा टूल है जिस पर उपयोगकर्ता अक्सर काफ़ी समय बिताते हैं. इसलिए, यह ज़रूरी है कि उनके पास उन सभी चीज़ों का तुरंत ऐक्सेस हो जिनका वे अक्सर इस्तेमाल करते हैं. साथ ही, उन्हें इस टूल का इस्तेमाल करने में आसानी होनी चाहिए.

ज़्यादा इंटरैक्टिव टच ऐप्लिकेशन डिज़ाइन करते समय, आपको पता चलेगा कि स्क्रीन तुरंत छोटी लगने लगती है. साथ ही, इंटरैक्ट करते समय उपयोगकर्ता की उंगलियां स्क्रीन के ज़्यादातर हिस्से को कवर कर लेती हैं. बिल्डर का इस्तेमाल करते समय, हमें यह बात साफ़ तौर पर पता चली. डिज़ाइन करते समय, आपको ग्राफ़िक के पिक्सल के बजाय, स्क्रीन के फ़िज़िकल साइज़ का ध्यान रखना होगा. असल कॉन्टेंट को ज़्यादा से ज़्यादा जगह देने के लिए, बटन और कंट्रोल की संख्या कम रखना ज़रूरी है.

हमारा मकसद, टच डिवाइसों पर Build को आसानी से इस्तेमाल करने लायक बनाना था. इसके लिए, हमने ओरिजनल डेस्कटॉप वर्शन में टच इनपुट की सुविधा जोड़ी. साथ ही, Build को इस तरह से डिज़ाइन किया कि यह टच डिवाइसों पर इस्तेमाल करने में आसान लगे. आखिर में, हमने यूज़र इंटरफ़ेस (यूआई) के दो वैरिएशन बनाए. पहला, बड़ी स्क्रीन वाले डेस्कटॉप और टैबलेट के लिए और दूसरा, छोटी स्क्रीन वाले मोबाइल डिवाइसों के लिए. जब भी हो सके, एक ही मोड का इस्तेमाल करें और मोड के बीच आसानी से ट्रांज़िशन करें. हमारे मामले में, हमने पाया कि इन दोनों मोड के बीच का अनुभव काफ़ी अलग था. इसलिए, हमने किसी खास ब्रेकपॉइंट पर भरोसा करने का फ़ैसला लिया. दोनों वर्शन में कई सुविधाएं एक जैसी हैं. हमने ज़्यादातर चीज़ों को सिर्फ़ एक कोड लागू करके करने की कोशिश की है. हालांकि, यूज़र इंटरफ़ेस (यूआई) के कुछ हिस्से, दोनों वर्शन में अलग-अलग तरीके से काम करते हैं.

हम मोबाइल डिवाइसों का पता लगाने के लिए, यूज़र-एजेंट डेटा का इस्तेमाल करते हैं. इसके बाद, हम व्यूपोर्ट के साइज़ की जांच करके यह तय करते हैं कि छोटी स्क्रीन वाले मोबाइल यूज़र इंटरफ़ेस का इस्तेमाल किया जाना चाहिए या नहीं. "बड़ी स्क्रीन" के लिए ब्रेकपॉइंट चुनना थोड़ा मुश्किल है, क्योंकि स्क्रीन के साइज़ की भरोसेमंद वैल्यू पाना मुश्किल होता है. सौभाग्य से, हमारे मामले में यह ज़रूरी नहीं है कि हम बड़ी स्क्रीन वाले टच डिवाइस पर, छोटी स्क्रीन वाला यूज़र इंटरफ़ेस दिखाएं. ऐसा करने पर भी टूल ठीक से काम करेगा. हालांकि, कुछ बटन थोड़े बड़े लग सकते हैं. आखिर में, हम ब्रेकपॉइंट को 1,000 पिक्सल पर सेट करते हैं. अगर साइट को 1,000 पिक्सल से ज़्यादा चौड़ी विंडो (लैंडस्केप मोड में) से लोड किया जाता है, तो आपको बड़ी स्क्रीन वाला वर्शन दिखेगा.

आइए, स्क्रीन के दो साइज़ और अनुभवों के बारे में थोड़ी बात करते हैं:

बड़ी स्क्रीन, माउस और टच की सुविधा के साथ

बड़ी स्क्रीन वाला वर्शन, माउस की सुविधा वाले सभी डेस्कटॉप कंप्यूटर और बड़ी स्क्रीन वाले टच डिवाइसों (जैसे, Google Nexus 10) पर दिखता है. इस वर्शन में, नेविगेशन कंट्रोल की सुविधाएं, डेस्कटॉप के ओरिजनल वर्शन जैसी ही हैं. हालांकि, हमने इसमें टच और कुछ जेस्चर की सुविधाएं जोड़ी हैं. हम विंडो के साइज़ के हिसाब से यूज़र इंटरफ़ेस (यूआई) में बदलाव करते हैं. इसलिए, जब कोई उपयोगकर्ता विंडो का साइज़ बदलता है, तो हो सकता है कि कुछ यूआई हट जाएं या उनका साइज़ बदल जाए. हम ऐसा सीएसएस मीडिया क्वेरी का इस्तेमाल करके करते हैं.

उदाहरण: अगर उपलब्ध ऊंचाई 730 पिक्सल से कम है, तो एक्सप्लोर मोड में ज़ूम-स्लाइडर कंट्रोल छिप जाता है:

@media only screen and (max-height: 730px) {
    .zoom-slider {
        display: none;
    }
}

छोटी स्क्रीन, सिर्फ़ टच की सुविधा

यह वर्शन, मोबाइल डिवाइसों और छोटे टैबलेट (टारगेट डिवाइस Nexus 4 और Nexus 7) पर दिखाया जाता है. इस वर्शन के लिए, मल्टी-टच की सुविधा ज़रूरी है.

छोटी स्क्रीन वाले डिवाइसों पर, हमें कॉन्टेंट को ज़्यादा से ज़्यादा जगह देनी होती है. इसलिए, हमने जगह बढ़ाने के लिए कुछ बदलाव किए हैं. इनमें, अक्सर इस्तेमाल न होने वाले एलिमेंट को हटाना शामिल है:

  • ब्रिक चुनने वाला टूल, बिल्ड करते समय कलर सिलेक्टर में छोटा हो जाता है.
  • हमने ज़ूम और ओरिएंटेशन कंट्रोल को मल्टी-टच जेस्चर से बदल दिया है.
  • Chrome की फ़ुल-स्क्रीन सुविधा का इस्तेमाल करके, स्क्रीन पर ज़्यादा जगह भी मिल सकती है.
बड़ी स्क्रीन पर ऐप्लिकेशन बनाना
बड़ी स्क्रीन पर बनाएं. ब्रिक चुनने वाला टूल हमेशा दिखता है. साथ ही, दाईं ओर कुछ कंट्रोल होते हैं.
छोटी स्क्रीन पर ऐप्लिकेशन बनाना
छोटी स्क्रीन पर बनाएं. ब्रिक चुनने वाला टूल छोटा कर दिया गया है और कुछ बटन हटा दिए गए हैं.

WebGL की परफ़ॉर्मेंस और सहायता

आधुनिक टच डिवाइसों में काफ़ी बेहतर जीपीयू होते हैं, लेकिन वे अब भी डेस्कटॉप डिवाइसों के जीपीयू से काफ़ी पीछे हैं. इसलिए, हमें पता था कि हमें परफ़ॉर्मेंस से जुड़ी कुछ समस्याओं का सामना करना पड़ेगा. खास तौर पर, '3D में देखें' मोड में, जहां हमें एक साथ कई क्रिएशन रेंडर करने होते हैं.

हम क्रिएटिव तरीके से, जटिल आकार और पारदर्शिता वाली कुछ नई तरह की ईंट जोड़ना चाहते थे. ये ऐसी सुविधाएं हैं जो आम तौर पर जीपीयू पर बहुत ज़्यादा असर डालती हैं. हालांकि, हमें पहले वर्शन के साथ काम करने वाले क्रिएशन को भी इस्तेमाल करने की सुविधा देनी थी. इसलिए, हमने कोई नई पाबंदी नहीं लगाई. जैसे, क्रिएशन में ब्रिक की कुल संख्या को काफ़ी कम करना.

Build के पहले वर्शन में, एक क्रिएशन में ज़्यादा से ज़्यादा ब्रिक इस्तेमाल किए जा सकते थे. इसमें एक "ईंट-मीटर" था, जो दिखाता था कि कितनी ईंटें बची हैं. नए तरीके को लागू करने के दौरान, हमने कुछ नए ब्रिक जोड़े हैं. इनसे ब्रिक-मीटर पर स्टैंडर्ड ब्रिक से ज़्यादा असर पड़ता है. इसलिए, ब्रिक की कुल संख्या में थोड़ी कमी आई है. यह एक ऐसा तरीका था जिससे अच्छी परफ़ॉर्मेंस बनाए रखते हुए, नई ब्रिक शामिल की जा सकती थीं.

'3D में एक्सप्लोर करें' मोड में, एक साथ कई काम होते हैं. जैसे, बेस प्लेट के टेक्सचर लोड करना, क्रिएशन लोड करना, क्रिएशन को ऐनिमेट करना, और उन्हें रेंडर करना वगैरह. इसके लिए, जीपीयू और सीपीयू, दोनों की ज़रूरत होती है. इसलिए, हमने इन हिस्सों को ज़्यादा से ज़्यादा ऑप्टिमाइज़ करने के लिए, Chrome DevTools में काफ़ी फ़्रेम प्रोफ़ाइलिंग की. हमने मोबाइल डिवाइसों पर क्रिएशन को थोड़ा ज़ूम किया है, ताकि हमें एक साथ ज़्यादा क्रिएशन रेंडर न करने पड़ें.

कुछ डिवाइसों के लिए, हमें कुछ वेबजीएल शेडर को फिर से देखना पड़ा और उन्हें आसान बनाना पड़ा. हालांकि, हमने हमेशा इस समस्या को हल करने और आगे बढ़ने का तरीका ढूंढ लिया.

WebGL के बिना काम करने वाले डिवाइसों के लिए

हम चाहते थे कि साइट को कुछ हद तक इस्तेमाल किया जा सके, भले ही वेबसाइट पर आने वाले व्यक्ति के डिवाइस पर WebGL काम न करता हो. कभी-कभी कैनवस सलूशन या CSS3D सुविधाओं का इस्तेमाल करके, 3D को आसानी से दिखाया जा सकता है. माफ़ करें, हमें WebGL का इस्तेमाल किए बिना, 3D में बनाए गए आइटम को बनाने और एक्सप्लोर करने की सुविधाओं को दोहराने का कोई अच्छा तरीका नहीं मिला.

सभी प्लैटफ़ॉर्म पर क्रिएशन का विज़ुअल स्टाइल एक जैसा होना चाहिए. हम 2.5D सलूशन आज़मा सकते थे, लेकिन इससे क्रिएशन कुछ अलग दिखते. हमें यह भी पक्का करना था कि 'Chrome से बनाएं' टूल के पहले वर्शन से बनाए गए क्रिएशन, साइट के नए वर्शन में भी पहले की तरह ही दिखें और आसानी से काम करें.

WebGL के बिना काम करने वाले डिवाइसों पर, अब भी 2D मोड में 'एक्सप्लोर करें' टैब को ऐक्सेस किया जा सकता है. हालांकि, इन डिवाइसों पर नए क्रिएशन नहीं बनाए जा सकते या 3D में एक्सप्लोर नहीं किया जा सकता. इसलिए, उपयोगकर्ताओं को अब भी प्रोजेक्ट के बारे में जानकारी मिल सकती है. साथ ही, वे यह भी जान सकते हैं कि वेबजीएल की सुविधा वाले डिवाइस पर, इस टूल का इस्तेमाल करके क्या-क्या बनाया जा सकता है. ऐसा हो सकता है कि WebGL की सुविधा के बिना, उपयोगकर्ताओं के लिए साइट उतनी काम की न हो. हालांकि, यह कम से कम एक टीज़र के तौर पर काम करनी चाहिए और उन्हें इसे आज़माने के लिए जोड़ना चाहिए.

कभी-कभी, WebGL के समाधानों के लिए फ़ॉलबैक वर्शन उपलब्ध नहीं कराए जा सकते. इसकी कई वजहें हो सकती हैं. जैसे, परफ़ॉर्मेंस, विज़ुअल स्टाइल, डेवलपमेंट और रखरखाव की लागत वगैरह. हालांकि, अगर आपको फ़ॉलबैक लागू नहीं करना है, तो कम से कम उन लोगों का ध्यान रखें जिनके ब्राउज़र पर WebGL काम नहीं करता. उन्हें बताएं कि वे साइट को पूरी तरह से क्यों ऐक्सेस नहीं कर पा रहे हैं. साथ ही, वे WebGL के साथ काम करने वाले ब्राउज़र का इस्तेमाल करके, इस समस्या को कैसे हल कर सकते हैं, इसके बारे में निर्देश दें.

ऐसेट मैनेजमेंट

Google ने 2013 में, Google Maps का नया वर्शन लॉन्च किया था. इसमें लॉन्च के बाद से, यूज़र इंटरफ़ेस (यूआई) में सबसे ज़्यादा अहम बदलाव किए गए थे. इसलिए, हमने Google Maps के नए यूज़र इंटरफ़ेस (यूआई) के साथ काम करने के लिए, 'Chrome की मदद से बनाएं' टूल को फिर से डिज़ाइन करने का फ़ैसला लिया. साथ ही, इस टूल को फिर से डिज़ाइन करते समय, हमने अन्य बातों का भी ध्यान रखा. नया डिज़ाइन, साफ़-साफ़ दिखने वाले रंगों और आसान शेप के साथ अपेक्षाकृत फ़्लैट है. इससे, हमने कई यूज़र इंटरफ़ेस (यूआई) एलिमेंट पर सिर्फ़ सीएसएस का इस्तेमाल किया. इससे इमेज का इस्तेमाल कम हो गया.

'एक्सप्लोर करें' में हमें कई इमेज लोड करनी होती हैं. जैसे, क्रिएशन के लिए थंबनेल इमेज, बेसप्लेट के लिए मैप टेक्सचर, और आखिर में असल 3D क्रिएशन. हम लगातार नई इमेज लोड करते समय, यह पक्का करने के लिए ज़्यादा ध्यान रखते हैं कि कोई मेमोरी लीक न हो.

3D क्रिएशन, पसंद के मुताबिक बनाए गए फ़ाइल फ़ॉर्मैट में सेव किए जाते हैं. इन्हें PNG इमेज के तौर पर पैकेज किया जाता है. 3D क्रिएशन का डेटा, इमेज के तौर पर सेव रखने की वजह से, हम डेटा को सीधे तौर पर क्रिएशन को रेंडर करने वाले शेडर को पास कर पाए.

उपयोगकर्ता की ओर से जनरेट की गई सभी इमेज के लिए, डिज़ाइन की मदद से सभी प्लैटफ़ॉर्म पर एक ही साइज़ की इमेज का इस्तेमाल किया जा सकता है. इससे स्टोरेज और बैंडविड्थ का इस्तेमाल कम होता है.

स्क्रीन ओरिएंटेशन मैनेज करना

पोर्ट्रेट मोड से लैंडस्केप मोड या लैंडस्केप मोड से पोर्ट्रेट मोड में स्विच करने पर, स्क्रीन का आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) कितना बदलता है, यह याद रखना मुश्किल होता है. मोबाइल डिवाइसों के लिए कॉन्टेंट बनाते समय, आपको इस बात का ध्यान रखना होगा.

स्क्रोल की सुविधा वाली किसी पारंपरिक वेबसाइट पर, सीएसएस नियम लागू करके, ऐसी रिस्पॉन्सिव साइट बनाई जा सकती है जो कॉन्टेंट और मेन्यू को फिर से व्यवस्थित करती है. जब तक स्क्रोल करने की सुविधा का इस्तेमाल किया जा सकता है, तब तक यह समस्या आसानी से हल की जा सकती है.

हमने इस तरीके का इस्तेमाल Build में भी किया था. हालांकि, लेआउट से जुड़ी समस्या को हल करने में हम थोड़े सीमित थे. इसकी वजह यह थी कि हमें कॉन्टेंट को हर समय दिखाना था और साथ ही, कई कंट्रोल और बटन का तुरंत ऐक्सेस भी रखना था. समाचार साइटों जैसी सिर्फ़ कॉन्टेंट वाली साइटों के लिए, फ़्लूइड लेआउट काफ़ी सही होता है. हालांकि, हमारे जैसे गेम ऐप्लिकेशन के लिए यह मुश्किल था. ऐसा लेआउट ढूंढना एक चुनौती थी जो लैंडस्केप और पोर्ट्रेट, दोनों ओरिएंटेशन में काम करता हो. साथ ही, इसमें कॉन्टेंट की अच्छी खासी जानकारी हो और उसे आसानी से इंटरैक्ट किया जा सके. आखिर में, हमने Build को सिर्फ़ लैंडस्केप मोड में रखने का फ़ैसला किया. साथ ही, हमने उपयोगकर्ता को अपने डिवाइस को घुमाने के लिए कहा.

एक्सप्लोर करने की सुविधा को दोनों ओरिएंटेशन में आसानी से हल किया जा सकता था. बेहतर अनुभव देने के लिए, हमें सिर्फ़ ओरिएंटेशन के हिसाब से 3D के ज़ूम लेवल में बदलाव करना पड़ा.

ज़्यादातर कॉन्टेंट लेआउट को सीएसएस से कंट्रोल किया जाता है. हालांकि, ओरिएंटेशन से जुड़े कुछ कॉन्टेंट को JavaScript में लागू करना ज़रूरी था. हमें पता चला है कि ऑरिएंटेशन की पहचान करने के लिए, window.orientation का इस्तेमाल करने का कोई अच्छा क्रॉस-डिवाइस समाधान नहीं था. इसलिए, आखिर में हम डिवाइस के ऑरिएंटेशन की पहचान करने के लिए, सिर्फ़ window.innerWidth और window.innerHeight की तुलना कर रहे थे.

if( window.innerWidth > window.innerHeight ){
  //landscape
} else {
  //portrait
}

टच सपोर्ट जोड़ना

वेब कॉन्टेंट में टच की सुविधा जोड़ना आसान है. क्लिक इवेंट जैसी बुनियादी इंटरैक्टिविटी, डेस्कटॉप और टच-सक्षम डिवाइसों पर एक जैसी काम करती है. हालांकि, ज़्यादा बेहतर इंटरैक्शन के लिए, आपको टच इवेंट को भी मैनेज करना होगा: touchstart, touchmove, और touchend. इस लेख में, इन इवेंट को इस्तेमाल करने के बुनियादी तरीके के बारे में बताया गया है. Internet Explorer, टच इवेंट के साथ काम नहीं करता. इसके बजाय, यह पॉइंटर इवेंट (pointerdown, pointermove, pointerup) का इस्तेमाल करता है. पॉइंटर इवेंट को स्टैंडर्ड बनाने के लिए, W3C को सबमिट कर दिया गया है. हालांकि, फ़िलहाल इन्हें सिर्फ़ Internet Explorer में लागू किया गया है.

'3D में देखें' मोड में, हमें Google Maps के स्टैंडर्ड वर्शन में इस्तेमाल होने वाले नेविगेशन का इस्तेमाल करना था. जैसे, मैप को पैन करने के लिए एक उंगली का इस्तेमाल करना और ज़ूम करने के लिए दो उंगलियों से पिंच करना. क्रिएशन 3D में होने की वजह से, हमने दो उंगलियों से घुमाने का जेस्चर भी जोड़ा है. आम तौर पर, इसके लिए टच-इवेंट का इस्तेमाल करना होगा.

ज़्यादा कंप्यूटिंग से बचना अच्छा होता है. जैसे, इवेंट हैंडलर में 3D को अपडेट या रेंडर करना. इसके बजाय, टच इनपुट को किसी वैरिएबल में सेव करें और requestAnimationFrame रेंडर लूप में इनपुट पर प्रतिक्रिया दें. इससे एक ही समय पर माउस को लागू करना भी आसान हो जाता है. इसके लिए, आपको माउस की वैल्यू को उसी वैरिएबल में सेव करना होता है.

इनपुट को सेव करने के लिए, किसी ऑब्जेक्ट को शुरू करके, touchstart इवेंट लिसनर जोड़ें. हर इवेंट हैंडलर में, हम event.preventDefault() को कॉल करते हैं. ऐसा इसलिए किया जाता है, ताकि ब्राउज़र टच इवेंट को प्रोसेस करना जारी न रख सके. इससे, पूरे पेज को स्क्रोल करने या स्केल करने जैसी कुछ अनचाही कार्रवाइयां हो सकती हैं.

var input = {dragStartX:0, dragStartY:0, dragX:0, dragY:0, dragDX:0, dragDY:0, dragging:false};
plateContainer.addEventListener('touchstart', onTouchStart);

function onTouchStart(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragStart(event.touches[0].clientX , event.touches[0].clientY);
    //start listening to all needed touchevents to implement the dragging
    document.addEventListener('touchmove', onTouchMove);
    document.addEventListener('touchend', onTouchEnd);
    document.addEventListener('touchcancel', onTouchEnd);
  }
}

function onTouchMove(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragging(event.touches[0].clientX, event.touches[0].clientY);
  }
}

function onTouchEnd(event) {
  event.preventDefault();
  if( event.touches.length === 0){
    handleDragStop();
    //remove all eventlisteners but touchstart to minimize number of eventlisteners
    document.removeEventListener('touchmove', onTouchMove);
    document.removeEventListener('touchend', onTouchEnd);
    //also listen to touchcancel event to avoid unexpected behavior when switching tabs and some other situations
    document.removeEventListener('touchcancel', onTouchEnd);
  }
}

हम इवेंट हैंडलर में इनपुट का असल स्टोरेज नहीं कर रहे हैं. इसके बजाय, हम अलग-अलग हैंडलर में स्टोरेज कर रहे हैं: handleDragStart, handleDragging, और handleDragStop. ऐसा इसलिए है, क्योंकि हम इन्हें माउस इवेंट हैंडलर से भी कॉल करना चाहते हैं. ध्यान रखें कि उपयोगकर्ता एक ही समय पर टच और माउस का इस्तेमाल कर सकता है. हालांकि, ऐसा होने की संभावना कम होती है. हम उस मामले को सीधे तौर पर मैनेज करने के बजाय, सिर्फ़ यह पक्का करते हैं कि कोई समस्या न हो.

function handleDragStart(x ,y ){
  input.dragging = true;
  input.dragStartX = input.dragX = x;
  input.dragStartY = input.dragY = y;
}

function handleDragging(x ,y ){
  if(input.dragging) {
    input.dragDX = x - input.dragX;
    input.dragDY = y - input.dragY;
    input.dragX = x;
    input.dragY = y;
  }
}

function handleDragStop(){
  if(input.dragging) {
    input.dragging = false;
    input.dragDX = 0;
    input.dragDY = 0;
  }
}

टचमूव पर आधारित ऐनिमेशन करते समय, पिछले इवेंट के बाद के डेल्टा मूव को सेव करना अक्सर फ़ायदेमंद होता है. उदाहरण के लिए, हमने Explore में सभी बेस प्लेट पर जाने के दौरान, कैमरे के वेग के लिए पैरामीटर के तौर पर इसका इस्तेमाल किया है. ऐसा इसलिए किया गया है, क्योंकि बेस प्लेट को खींचकर नहीं, बल्कि कैमरे को घुमाकर एक से दूसरी प्लेट पर पहुंचा जाता है.

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );

  //execute animation based on input.dragDX, input.dragDY, input.dragX or input.dragY
 /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX=0;
  input.dragDY=0;
}

एम्बेड किया गया उदाहरण: टच इवेंट का इस्तेमाल करके किसी ऑब्जेक्ट को खींचकर छोड़ना. यह सुविधा, Chrome के साथ Build में 3D मैप को खींचने और छोड़ने की सुविधा की तरह ही काम करती है: http://cdpn.io/qDxvo

मल्टी-टच जेस्चर

Hammer या QuoJS जैसे कई फ़्रेमवर्क या लाइब्रेरी हैं, जो मल्टी-टच जेस्चर को मैनेज करने की प्रोसेस को आसान बना सकती हैं. हालांकि, अगर आपको कई जेस्चर को जोड़ना है और उनका पूरा कंट्रोल पाना है, तो कभी-कभी इसे शुरू से शुरू करना सबसे अच्छा होता है.

पिंच और घुमाने के जेस्चर को मैनेज करने के लिए, हम दूसरी उंगली को स्क्रीन पर रखने पर, दो उंगलियों के बीच की दूरी और कोण सेव करते हैं:

//variables representing the actual scale/rotation of the object we are affecting
var currentScale = 1;
var currentRotation = 0;

function onTouchStart(event) {
  event.preventDefault();
  if( event.touches.length === 1){
    handleDragStart(event.touches[0].clientX , event.touches[0].clientY);
  }else if( event.touches.length === 2 ){
    handleGestureStart(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY );
  }
}

function handleGestureStart(x1, y1, x2, y2){
  input.isGesture = true;
  //calculate distance and angle between fingers
  var dx = x2 - x1;
  var dy = y2 - y1;
  input.touchStartDistance=Math.sqrt(dx*dx+dy*dy);
  input.touchStartAngle=Math.atan2(dy,dx);
  //we also store the current scale and rotation of the actual object we are affecting. This is needed to support incremental rotation/scaling. We can't assume that an object is always the same scale when gesture starts.
  input.startScale=currentScale;
  input.startAngle=currentRotation;
}

इसके बाद, टचमूव इवेंट में हम उन दो उंगलियों के बीच की दूरी और कोण को लगातार मेज़र करते हैं. इसके बाद, शुरुआती दूरी और मौजूदा दूरी के बीच के अंतर का इस्तेमाल स्केल सेट करने के लिए किया जाता है. साथ ही, शुरुआती ऐंगल और मौजूदा ऐंगल के बीच के अंतर का इस्तेमाल ऐंगल सेट करने के लिए किया जाता है.

function onTouchMove(event) {
  event.preventDefault();
  if( event.touches.length  === 1){
    handleDragging(event.touches[0].clientX, event.touches[0].clientY);
  }else if( event.touches.length === 2 ){
    handleGesture(event.touches[0].clientX, event.touches[0].clientY, event.touches[1].clientX, event.touches[1].clientY );
  }
}

function handleGesture(x1, y1, x2, y2){
  if(input.isGesture){
    //calculate distance and angle between fingers
    var dx = x2 - x1;
    var dy = y2 - y1;
    var touchDistance = Math.sqrt(dx*dx+dy*dy);
    var touchAngle = Math.atan2(dy,dx);
    //calculate the difference between current touch values and the start values
    var scalePixelChange = touchDistance - input.touchStartDistance;
    var angleChange = touchAngle - input.touchStartAngle;
    //calculate how much this should affect the actual object
    currentScale = input.startScale + scalePixelChange*0.01;
    currentRotation = input.startAngle+(angleChange*180/Math.PI);
    //upper and lower limit of scaling
    if(currentScale<0.5) currentScale = 0.5;
    if(currentScale>3) currentScale = 3;
  }
}

हर टचमूव इवेंट के बीच की दूरी में हुए बदलाव का इस्तेमाल, खींचने और छोड़ने के उदाहरण की तरह ही किया जा सकता है. हालांकि, यह तरीका तब ज़्यादा काम का होता है, जब आपको लगातार किसी ऑब्जेक्ट को एक जगह से दूसरी जगह ले जाना हो.

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );
  //execute transform based on currentScale and currentRotation
  /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX=0;
  input.dragDY=0;
}

अगर आप चाहें, तो पिंच और घुमाने के जेस्चर के दौरान, ऑब्जेक्ट को खींचने और छोड़ने की सुविधा भी चालू की जा सकती है. ऐसे में, आपको दोनों उंगलियों के बीच के सेंटर पॉइंट का इस्तेमाल, खींचने और छोड़ने वाले हैंडलर के इनपुट के तौर पर करना होगा.

एम्बेड किए गए उदाहरण: 2D में किसी ऑब्जेक्ट को घुमाना और उसका स्केल बदलना. Explore में मैप को लागू करने के तरीके से मिलता-जुलता: http://cdpn.io/izloq

एक ही हार्डवेयर पर माउस और टच की सुविधा

आजकल कई लैपटॉप कंप्यूटर, जैसे कि Chromebook Pixel, माउस और टच इनपुट, दोनों के साथ काम करते हैं. अगर सावधानी नहीं बरती जाती है, तो इसकी वजह से कुछ अनचाहे व्यवहार हो सकते हैं.

एक अहम बात यह है कि आपको सिर्फ़ टच की सुविधा का पता लगाकर, माउस इनपुट को अनदेखा नहीं करना चाहिए. इसके बजाय, आपको दोनों सुविधाओं को एक साथ इस्तेमाल करने की अनुमति देनी चाहिए.

अगर टच इवेंट हैंडलर में event.preventDefault() का इस्तेमाल नहीं किया जा रहा है, तो कुछ इम्यूलेट किए गए माउस इवेंट भी ट्रिगर होंगे. ऐसा इसलिए किया जाता है, ताकि टच के लिए ऑप्टिमाइज़ नहीं की गई ज़्यादातर साइटें अब भी काम करती रहें. उदाहरण के लिए, स्क्रीन पर एक बार टैप करने पर, ये इवेंट तेज़ी से और इस क्रम में ट्रिगर हो सकते हैं:

  1. touchstart
  2. touchmove
  3. touchend
  4. माउसओवर
  5. mousemove
  6. mousedown
  7. mouseup
  8. क्लिक

अगर आपके इंटरैक्शन थोड़े ज़्यादा जटिल हैं, तो इन माउस इवेंट की वजह से कुछ अनचाहा व्यवहार हो सकता है. साथ ही, आपके लागू करने की प्रोसेस में गड़बड़ी हो सकती है. आम तौर पर, टच इवेंट हैंडलर में event.preventDefault() का इस्तेमाल करना और अलग-अलग इवेंट हैंडलर में माउस इनपुट को मैनेज करना सबसे अच्छा होता है. आपको यह पता होना चाहिए कि टच इवेंट हैंडलर में event.preventDefault() का इस्तेमाल करने से, कुछ डिफ़ॉल्ट व्यवहार भी रुक जाएंगे. जैसे, स्क्रोल करना और क्लिक इवेंट.

"Build with Chrome में, हम नहीं चाहते थे कि कोई व्यक्ति साइट पर दो बार टैप करके उसे ज़ूम कर सके. भले ही, ज़्यादातर ब्राउज़र में यह सुविधा स्टैंडर्ड है. इसलिए, हम व्यूपोर्ट मेटा टैग का इस्तेमाल करके ब्राउज़र को यह बताते हैं कि उपयोगकर्ता के दो बार टैप करने पर, ज़ूम न करें. इससे क्लिक में लगने वाला 300 मिलीसेकंड का समय भी कम हो जाता है. इससे साइट की परफ़ॉर्मेंस बेहतर होती है. (डबल-टैप करके ज़ूम करने की सुविधा चालू होने पर, क्लिक में लगने वाला समय, एक बार टैप करने और दो बार टैप करने के बीच का अंतर बताने के लिए होता है.)

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

ध्यान रखें कि इस सुविधा का इस्तेमाल करते समय, यह आपके ऊपर है कि साइट को सभी स्क्रीन साइज़ पर पढ़ने लायक बनाया जाए. ऐसा इसलिए, क्योंकि उपयोगकर्ता ज़ूम करके नहीं देख पाएगा.

माउस, टच, और कीबोर्ड इनपुट

'3D में देखें' मोड में, हमने मैप पर नेविगेट करने के तीन तरीके उपलब्ध कराए हैं: माउस (खींचकर छोड़ना), टच (खींचकर छोड़ना, ज़ूम करने और घुमाने के लिए पिंच करना) और कीबोर्ड (ऐरो बटन का इस्तेमाल करके नेविगेट करना). नेविगेशन के ये सभी तरीके थोड़े अलग तरीके से काम करते हैं. हालांकि, हमने इन सभी पर एक ही तरीके का इस्तेमाल किया है. इसमें, इवेंट हैंडलर में वैरिएबल सेट किए जाते हैं और requestAnimationFrame लूप में उस पर कार्रवाई की जाती है. requestAnimationFrame लूप को यह जानने की ज़रूरत नहीं है कि नेविगेट करने के लिए किस तरीके का इस्तेमाल किया जाता है.

उदाहरण के लिए, हम इन तीनों इनपुट तरीकों से मैप की मूवमेंट (dragDX और dragDY) सेट कर सकते हैं. कीबोर्ड को लागू करने का तरीका यहां बताया गया है:

document.addEventListener('keydown', onKeyDown );
document.addEventListener('keyup', onKeyUp );

function onKeyDown( event ) {
  input.keyCodes[ "k" + event.keyCode ] = true;
  input.shiftKey = event.shiftKey;
}

function onKeyUp( event ) {
  input.keyCodes[ "k" + event.keyCode ] = false;
  input.shiftKey = event.shiftKey;
}

//this needs to be called every frame before animation is executed
function handleKeyInput(){
  if(input.keyCodes.k37){
    input.dragDX = -5; //37 arrow left
  } else if(input.keyCodes.k39){
    input.dragDX = 5; //39 arrow right
  }
  if(input.keyCodes.k38){
    input.dragDY = -5; //38 arrow up
  } else if(input.keyCodes.k40){
    input.dragDY = 5; //40 arrow down
  }
}

function onAnimationFrame() {
  requestAnimationFrame( onAnimationFrame );
  //because keydown events are not fired every frame we need to process the keyboard state first
  handleKeyInput();
  //implement animations based on what is stored in input
   /*
  /
  */

  //because touchmove is only fired when finger is actually moving we need to reset the delta values each frame
  input.dragDX = 0;
  input.dragDY = 0;
}

एम्बेड किया गया उदाहरण: नेविगेट करने के लिए माउस, टच, और कीबोर्ड का इस्तेमाल करना: http://cdpn.io/catlf

खास जानकारी

अलग-अलग स्क्रीन साइज़ वाले टच डिवाइसों पर काम करने के लिए, Build with Chrome को अडैप्ट करना एक सीखने का अनुभव रहा. टच डिवाइसों पर इस लेवल की इंटरैक्टिविटी बनाने का हमारी टीम के पास ज़्यादा अनुभव नहीं था. हालांकि, इस प्रोसेस के दौरान हमने बहुत कुछ सीखा.

सबसे बड़ी चुनौती यह थी कि उपयोगकर्ता अनुभव और डिज़ाइन को कैसे बेहतर बनाया जाए. तकनीकी चुनौतियों में कई स्क्रीन साइज़, टच इवेंट, और परफ़ॉर्मेंस से जुड़ी समस्याओं को मैनेज करना शामिल था.

टच डिवाइसों पर WebGL शेडर के साथ कुछ समस्याएं थीं. हालांकि, यह सुविधा उम्मीद से ज़्यादा बेहतर तरीके से काम की है. डिवाइसों की परफ़ॉर्मेंस लगातार बेहतर हो रही है और WebGL को लागू करने के तरीकों में भी तेज़ी से सुधार हो रहा है. हमें लगता है कि आने वाले समय में, हम डिवाइसों पर WebGL का ज़्यादा इस्तेमाल करेंगे.

अगर आपने अब तक ऐसा नहीं किया है, तो जाकर कुछ शानदार बनाएं!