استخدام مكعّبات LEGO® على الويب المتوافق مع الأجهزة المتعددة
إنّ تطبيق "إنشاء تطبيقات باستخدام Chrome" هو تجربة ممتعة لمستخدمي Chrome على أجهزة الكمبيوتر المكتبي تم إطلاقها في الأصل في أستراليا، وتم إعادة إصدارها في عام 2014 لتصبح متاحة على مستوى العالم، وتشمل محتوى مرتبطًا بفيلم "LEGO® MOVIE™"، وإمكانية استخدامها على الأجهزة الجوّالة. سنشارك في هذه المقالة بعض الدروس المستفادة من المشروع، لا سيما في ما يتعلّق بالانتقال من تجربة سطح المكتب فقط إلى حلّ متعدّد الشاشات يتيح استخدام كلّ من الماوس والشاشة اللمسية.
تاريخ تطبيق "إنشاء التطبيقات باستخدام Chrome"
تم إطلاق الإصدار الأول من تطبيق "إنشاء تطبيقات باستخدام Chrome" في أستراليا في عام 2012. أردنا إبراز قدرات الويب بطريقة جديدة تمامًا وتقديم Chrome إلى جمهور جديد تمامًا.
يتضمّن الموقع الإلكتروني جزءَين رئيسيَّين: وضع "الإنشاء" الذي يمكن للمستخدمين من خلاله إنشاء تصميمات باستخدام مكعبات LEGO، ووضع "الاستكشاف" لتصفّح التصميمات على نسخة من "خرائط Google" تم إنشاؤها باستخدام مكعبات LEGO.
كان من الضروري توفير رسومات ثلاثية الأبعاد تفاعلية لتقديم أفضل تجربة بناء باستخدام مكعبات LEGO. في عام 2012، كان WebGL متاحًا للجميع في متصفّحات أجهزة الكمبيوتر المكتبي فقط، لذلك كان تطبيق Build موجهًا كتجربة على أجهزة الكمبيوتر المكتبي فقط. كانت ميزة "استكشاف" تستخدِم "خرائط Google" لعرض التصاميم، ولكن عند التكبير بما يكفي، كانت تبدِّل إلى استخدام WebGL لعرض الخريطة التي تعرض التصاميم بتنسيق ثلاثي الأبعاد، مع الاستمرار في استخدام "خرائط Google" كنسيج القاعدة. أردنا إنشاء بيئة تتيح لعشاق LEGO من جميع الأعمار التعبير عن إبداعهم بسهولة وبشكلٍ بديهي واستكشاف إبداعات بعضهم البعض.
في عام 2013، قرّرنا توسيع نطاق أداة "الإنشاء باستخدام Chrome" لتشمل تقنيات الويب الجديدة. ومن بين هذه التقنيات، كانت تقنية WebGL في Chrome لأجهزة Android، ما كان سيسمح بتطوير ميزة "الإنشاء باستخدام Chrome" لتصبح تجربة على الأجهزة الجوّالة. للبدء، طوّرنا أولاً نماذج أوّلية تعمل باللمس قبل استكشاف الأجهزة في "أداة التصميم" لفهم سلوك الإيماءات والاستجابة للمس التي قد نواجهها من خلال المتصفّح مقارنةً بالتطبيق المتوافق مع الأجهزة الجوّالة.
واجهة أمامية سريعة الاستجابة
كان علينا إتاحة استخدام الأجهزة التي تتيح الإدخال باللمس والماوس. ومع ذلك، تبيّن أنّ استخدام واجهة المستخدم نفسها على الشاشات الصغيرة التي تعمل باللمس ليس الحلّ الأمثل بسبب القيود المفروضة على المساحة.
تتضمّن ميزة "الإنشاء" الكثير من التفاعلات، مثل تكبير المحتوى وتصغيره وتغيير ألوان الطوب، وبالطبع اختيار الطوب وتدويره ووضعه. إنّها أداة يقضي المستخدمون فيها عادةً الكثير من الوقت، لذا من المهم أن يتمكّنوا من الوصول بسرعة إلى كل ما يستخدمونه بشكل متكرر، وأن يشعروا بالراحة عند التفاعل معها.
عند تصميم تطبيق لمسي تفاعلي للغاية، ستلاحظ أنّ الشاشة تبدو صغيرة بسرعة وتميل أصابع المستخدم إلى تغطية جزء كبير من الشاشة أثناء التفاعل. وقد تبيّن لنا ذلك أثناء العمل مع "أداة إنشاء التقارير". عليك مراعاة حجم الشاشة الفعلي بدلاً من وحدات البكسل في الرسومات عند إجراء التصميم. لذلك، من المهم تقليل عدد الأزرار وعناصر التحكّم قدر الإمكان لتوفير أكبر مساحة ممكنة على الشاشة للمحتوى الفعلي.
كان هدفنا جعل تجربة استخدام تطبيق Build على الأجهزة التي تعمل باللمس طبيعية، وليس مجرد إضافة ميزة اللمس إلى الإصدار الأصلي المخصّص لأجهزة الكمبيوتر المكتبي، بل جعل التطبيق يبدو وكأنه مصمّم للاستخدام باللمس. انتهينا بتصميمَين لواجهة المستخدم، أحدهما لأجهزة الكمبيوتر المكتبي والأجهزة اللوحية ذات الشاشات الكبيرة والآخر للأجهزة الجوّالة ذات الشاشات الأصغر حجمًا. من الأفضل استخدام عملية تنفيذ واحدة وإجراء انتقال سلس بين الأوضاع كلما أمكن ذلك. في حالتنا، تبيّن لنا أنّ هناك فرقًا كبيرًا في التجربة بين هذين الوضعَين، لذا قرّرنا الاعتماد على نقطة توقّف محدّدة. يتشارك الإصداران الكثير من الميزات، وحاولنا تنفيذ معظم الإجراءات باستخدام رمز برمجي واحد فقط، ولكن تعمل بعض جوانب واجهة المستخدم بشكل مختلف في كل منهما.
نستخدم بيانات وكيل المستخدم لرصد الأجهزة الجوّالة، ثم نتحقّق من حجم إطار العرض لتحديد ما إذا كان يجب استخدام واجهة المستخدم المخصّصة للأجهزة الجوّالة ذات الشاشة الصغيرة. من الصعب اختيار نقطة توقّف لتحديد ما يجب أن يكون عليه "الشاشة الكبيرة"، لأنّه من الصعب الحصول على قيمة موثوقة لحجم الشاشة الفعلي. لحسن الحظ، في حالتنا، لا يهمّ حقًا ما إذا كنا نعرض واجهة المستخدم المخصّصة للشاشة الصغيرة على جهاز لمس مزوّد بشاشة كبيرة، لأنّ الأداة ستظل تعمل بشكل جيد، ولكن قد تبدو بعض الأزرار كبيرة جدًا. في النهاية، حدّدنا نقطة التوقف على 1, 000 بكسل. إذا حمّلت الموقع الإلكتروني من نافذة أوسع من 1,000 بكسل (في الوضع الأفقي)، ستظهر لك النسخة المخصّصة للشاشة الكبيرة.
لنطّلِع على بعض المعلومات عن حجمَي الشاشة والتجربتَين:
شاشة كبيرة تتيح استخدام الماوس واللمس
يتم عرض إصدار الشاشة الكبيرة على جميع أجهزة الكمبيوتر المكتبي المزوّدة بجهاز توجيه الماوس وعلى الأجهزة التي تعمل باللمس والمزوّدة بشاشات كبيرة (مثل Google Nexus 10). يشبه هذا الإصدار الحلّ الأصلي للكمبيوتر المكتبي من حيث نوع عناصر التحكّم في التنقّل المتاحة، ولكننا أضفنا ميزة اللمس وبعض الإيماءات. نحن نعدّل واجهة المستخدم استنادًا إلى حجم النافذة، لذا عندما يغيّر المستخدم حجم النافذة، قد تتم إزالة بعض واجهة المستخدم أو تغيير حجمها. ننفّذ ذلك باستخدام طلبات البحث عن الوسائط في CSS.
مثال: عندما يكون الارتفاع المتاح أقل من 730 بكسل، يتم إخفاء شريط التمرير للتكبير/التصغير في وضع "استكشاف":
@media only screen and (max-height: 730px) {
.zoom-slider {
display: none;
}
}
شاشة صغيرة، تتيح اللمس فقط
يتم عرض هذا الإصدار على الأجهزة الجوّالة والأجهزة اللوحية الصغيرة (الأجهزة المستهدَفة هي Nexus 4 وNexus 7). يتطلب هذا الإصدار إتاحة ميزة اللمس المتعدّد.
على الأجهزة التي تتضمّن شاشة صغيرة، نحتاج إلى تخصيص أكبر مساحة ممكنة للمحتوى، لذلك أجرينا بعض التعديلات لزيادة المساحة إلى أقصى حد، وذلك عن طريق نقل العناصر التي لا يتم استخدامها كثيرًا خارج نطاق الرؤية:
- يتم تصغير أداة اختيار الوحدات في "أداة البناء" إلى أداة اختيار الألوان أثناء البناء.
- استبدلنا عناصر التحكّم في التكبير/التصغير والاتجاه بإيماءات متعددة اللمس.
- وتُعدّ وظيفة ملء الشاشة في Chrome مفيدة أيضًا للحصول على مساحة إضافية على الشاشة.
أداء WebGL ومدى توافقه
تتضمّن الأجهزة الحديثة التي تعمل باللمس وحدات معالجة رسومات قوية جدًا، ولكنّها لا تزال أقل أداءً من نظيراتها في أجهزة الكمبيوتر المكتبي، لذلك علمنا أنّنا سنواجه بعض التحديات في الأداء، خاصةً في وضع "استكشاف الواقع الثلاثي الأبعاد" حيث نحتاج إلى عرض الكثير من التصاميم في الوقت نفسه.
من الناحية الإبداعية، أردنا إضافة نوعَين جديدَين من الطوب بأشكال معقّدة وحتى شفافة، وهي ميزات تستهلك عادةً موارد وحدة معالجة الرسومات بشكل كبير. ومع ذلك، كان علينا أن نتوافق مع الإصدارات السابقة ونواصل إتاحة التصاميم من الإصدار الأول، لذا لم نتمكّن من فرض أي قيود جديدة، مثل تقليل إجمالي عدد الأشكال في التصاميم بشكل كبير.
في الإصدار الأول من ميزة "الإنشاء"، كان هناك حدّ أقصى لعدد المكعبات التي يمكن استخدامها في تصميم واحد. كان هناك "مقياس لرصّ الطوب" يشير إلى عدد الطوب المتبقي. في عملية التنفيذ الجديدة، كان لدينا بعض الوحدات الجديدة التي تؤثر في مقياس الوحدات أكثر من الوحدات العادية، ما أدّى إلى خفض إجمالي الحد الأقصى لعدد الوحدات قليلاً. كانت هذه إحدى الطرق لتضمين وحدات جديدة مع الحفاظ على مستوى أداء جيد.
في وضع "استكشاف الواقع الثلاثي الأبعاد"، يتم تنفيذ العديد من الإجراءات في الوقت نفسه، مثل تحميل مواد عرض القاعدة وتحميل التصاميم وإضافة الحركة إليها وعرضها، وما إلى ذلك. يتطلّب ذلك الكثير من كلّ من وحدة معالجة الرسومات ووحدة المعالجة المركزية، لذا أجرينا الكثير من عمليات تحليل أداء اللقطات في "أدوات مطوّري البرامج في Chrome" لتحسين هذه الأجزاء قدر الإمكان. على الأجهزة الجوّالة، قرّرنا تكبير التصاميم قليلاً حتى لا نضطر إلى عرض عدد كبير من التصاميم في الوقت نفسه.
وقد اضطرتنا بعض الأجهزة إلى إعادة النظر في بعض أدوات تظليل WebGL وتبسيطها، ولكننا دائمًا ما نجد طريقة لحلّ المشكلة والمضي قدمًا.
التوافق مع الأجهزة غير المزوّدة بتقنية WebGL
أردنا أن يكون الموقع الإلكتروني قابلاً للاستخدام إلى حدّ ما حتى إذا كان جهاز الزائر لا يتيح استخدام WebGL. في بعض الأحيان، تتوفّر طرق لعرض المحتوى الثلاثي الأبعاد بطريقة مبسطة باستخدام حلّ لوحة أو ميزات CSS3D. لم نعثر على حلّ جيد بما يكفي لإعادة إنشاء ميزتَي "الإنشاء" و"الاستكشاف" الثلاثي الأبعاد بدون استخدام WebGL.
يجب أن يكون النمط المرئي للتصميمات متسقًا على جميع المنصات. كان بإمكاننا تجربة حلّ 2.5D، ولكن كان سيؤدي ذلك إلى ظهور التصاميم بشكل مختلف في بعض النواحي. كان علينا أيضًا التفكير في كيفية التأكّد من أنّ التصاميم التي تم إنشاؤها باستخدام الإصدار الأول من ميزة "الإنشاء باستخدام Chrome" ستظهر بالشكل نفسه وستتمكّن من العمل بسلاسة في الإصدار الجديد من الموقع الإلكتروني كما في الإصدار الأول.
لا يزال بإمكانك استخدام وضع "استكشاف" ثنائي الأبعاد على الأجهزة غير المتوافقة مع WebGL، ولكن لا يمكنك إنشاء تصميمات جديدة أو استكشاف المحتوى بتقنية 3D. وبالتالي، سيظل بإمكان المستخدمين الحصول على فكرة عن عمق المشروع وما يمكنهم إنشاؤه باستخدام هذه الأداة إذا كانوا يستخدمون جهازًا مزوّدًا بتكنولوجيا WebGL. قد لا يكون الموقع الإلكتروني ذا قيمة كبيرة للمستخدمين الذين لا يتوفّر لديهم WebGL، ولكن يجب أن يشكّل على الأقلّ إعلانًا تشويقيًا ويشجّعهم على تجربته.
في بعض الأحيان، لا يمكن الاحتفاظ بإصدارات احتياطية لحلول WebGL. هناك العديد من الأسباب المحتمَلة، مثل الأداء والأسلوب المرئي وتكاليف التطوير والصيانة وما إلى ذلك. ومع ذلك، عند اتخاذ قرار عدم تنفيذ بديل، يجب على الأقل الاهتمام بالزائرين الذين لا يتوفّر لديهم WebGL، وشرح سبب عدم تمكّنهم من الوصول إلى الموقع الإلكتروني بالكامل، وتقديم تعليمات حول كيفية حلّ المشكلة باستخدام متصفّح متوافق مع WebGL.
إدارة مواد العرض
في عام 2013، طرحت Google إصدارًا جديدًا من "خرائط Google" يتضمّن أهم التغييرات على واجهة المستخدم منذ إطلاق التطبيق. لذلك، قرّرنا إعادة تصميم ميزة "الإنشاء باستخدام Chrome" لتتلاءم مع واجهة مستخدم "خرائط Google" الجديدة، وأخذنا في الاعتبار عوامل أخرى عند إعادة التصميم. التصميم الجديد مسطّح نسبيًا ويتميز بألوان موحّدة وأشكال بسيطة. وقد سمح لنا ذلك باستخدام CSS الخالص في الكثير من عناصر واجهة المستخدم، ما قلّل من استخدام الصور.
في ميزة "استكشاف"، نحتاج إلى تحميل الكثير من الصور، مثل الصور المصغّرة للتصميمات وصور نسيج الخرائط للقواعد الأساسية، وأخيراً التصميمات الثلاثية الأبعاد الفعلية. نحرص بشكلٍ خاص على عدم تسرب الذاكرة عند تحميل صور جديدة باستمرار.
يتم تخزين الابتكارات الثلاثية الأبعاد بتنسيق ملف مخصّص مُجمَّع كصورة بتنسيق PNG. من خلال إبقاء بيانات التصاميم الثلاثية الأبعاد مخزّنة كصورة، تمكّنا من نقل البيانات مباشرةً إلى أدوات تظليل الألوان التي تعرِض التصاميم.
بالنسبة إلى جميع الصور التي ينشئها المستخدمون، سمح لنا التصميم باستخدام أحجام الصور نفسها في جميع المنصات، ما أدّى إلى تقليل مساحة التخزين واستخدام النطاق الترددي إلى الحد الأدنى.
إدارة اتجاه الشاشة
من السهل نسيان مقدار تغيُّر نسبة العرض إلى الارتفاع للشاشة عند الانتقال من الوضع العمودي إلى الوضع الأفقي أو العكس. عليك مراعاة ذلك منذ البداية عند التعديل لتتوافق مع الأجهزة الجوّالة.
في الموقع الإلكتروني التقليدي الذي تم تفعيل ميزة الانتقال للأعلى أو للأسفل فيه، يمكنك تطبيق قواعد CSS للحصول على موقع إلكتروني سريع الاستجابة يعيد ترتيب المحتوى والقوائم. طالما أنّه يمكنك استخدام وظيفة الانتقال للأعلى أو للأسفل، يمكنك التعامل مع هذا الأمر بسهولة.
استخدمنا هذه الطريقة أيضًا في تطبيق Build، ولكن كانت لدينا قيودًا في ما يتعلّق بحلّ مشكلة التنسيق، لأنّنا أردنا أن يكون المحتوى مرئيًا في جميع الأوقات وأن يظل بإمكان المستخدمين الوصول بسرعة إلى عدد من عناصر التحكّم والأزرار. بالنسبة إلى المواقع الإلكترونية التي تعرض محتوى فقط، مثل المواقع الإخبارية، يكون التنسيق السلس مناسبًا جدًا، ولكن بالنسبة إلى تطبيق ألعاب مثل تطبيقنا، كان من الصعب تطبيقه. أصبح من الصعب العثور على تنسيق يناسب الوضع الأفقي والعمودي مع الحفاظ على نظرة عامة جيدة على المحتوى وطريقة مريحة للتفاعل. في النهاية، قرّرنا إبقاء "التصميم" في الوضع الأفقي فقط وطلبنا من المستخدم تدوير جهازه.
كان من الأسهل بكثير حلّ المشاكل في "استكشاف" في كلا الاتجاهين. لقد احتاجنا فقط إلى تعديل مستوى التكبير أو التصغير للصورة الثلاثية الأبعاد حسب الاتجاه للحصول على تجربة متّسقة.
يتم التحكّم في معظم تنسيق المحتوى من خلال CSS، ولكن كان من الضروري تنفيذ بعض العناصر المتعلّقة بالاتجاه باستخدام 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.
في وضع "الاستكشاف" الثلاثي الأبعاد، أردنا توفير ميزة التنقّل نفسها في "خرائط Google" العادية، أي استخدام إصبع واحد للتنقّل في الخريطة واستخدام إصبعين لتكبيرها أو تصغيرها. وبما أنّ التصاميم ثلاثية الأبعاد، أضفنا أيضًا إيماءة التدوير باستخدام إصبعين. يتطلّب ذلك عادةً استخدام أحداث اللمس.
من الممارسات الجيدة تجنُّب العمليات الحسابية المكثفة، مثل تعديل المحتوى الثلاثي الأبعاد أو عرضه في معالجات الأحداث. بدلاً من ذلك، يمكنك تخزين الإدخال باللمس في متغيّر والتفاعل مع الإدخال في حلقة عرض 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;
}
}
عند إنشاء صور متحركة استنادًا إلى لمسة الشاشة، غالبًا ما يكون من المفيد أيضًا تخزين حركة الاختلاف منذ الحدث الأخير. على سبيل المثال، استخدمنا هذه القيمة كمَعلمة لسرعة الكاميرا عند التنقّل في جميع لوحات القاعدة في ميزة "استكشاف"، لأنّك لا تسحب لوحات القاعدة بل تتحرّك الكاميرا.
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": 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;
}
في حدث touchmove، نقيس بعد ذلك المسافة والزاوية بين الإصبعين باستمرار. بعد ذلك، يتم استخدام الفرق بين المسافة عند البدء والمسافة الحالية لضبط المقياس، ويتم استخدام الفرق بين الزاوية عند البدء والزاوية الحالية لضبط الزاوية.
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;
}
يمكنك أيضًا تفعيل سحب الجسم أثناء تنفيذ إيماءات التصغير والتكبير والدوران إذا أردت ذلك. في هذه الحالة، يمكنك استخدام النقطة الوسطى بين الإصبعَين كمدخل إلى معالِج السحب.
مثال مضمّن: تدوير كائن وتغيير حجمه في رسومات ثنائية الأبعاد على غرار طريقة تنفيذ الخريطة في ميزة "استكشاف": http://cdpn.io/izloq
إتاحة الماوس واللمس على الجهاز نفسه
تتوفّر حاليًا العديد من أجهزة الكمبيوتر المحمول، مثل Chromebook Pixel، التي تتيح استخدام الماوس واللمس. وقد يؤدي ذلك إلى حدوث بعض السلوكيات غير المتوقّعة في حال عدم الانتباه.
من المهم عدم رصد إمكانية استخدام اللمس فقط ثم تجاهل إدخال الماوس، بل يجب إتاحة استخدامهما معًا في الوقت نفسه.
إذا كنت لا تستخدم event.preventDefault()
في معالجات أحداث اللمس، سيتم أيضًا تشغيل بعض أحداث الماوس المحاكية، وذلك للحفاظ على عمل معظم المواقع الإلكترونية المحسّنة غير المتوافقة مع اللمس. على سبيل المثال، عند النقر مرة واحدة على الشاشة، قد يتم تنشيط هذه الأحداث في تسلسل سريع وبالترتيب التالي:
- touchstart
- touchmove
- touchend
- تمرير الماوس
- mousemove
- mousedown
- mouseup
- نقرة
إذا كانت لديك تفاعلات أكثر تعقيدًا، قد تؤدي أحداث الماوس هذه إلى حدوث بعض السلوكيات غير المتوقّعة وتؤدي إلى إفساد عملية التنفيذ. من الأفضل غالبًا استخدام event.preventDefault()
في معالجات أحداث اللمس وإدارة إدخال الماوس في معالجات أحداث منفصلة. يُرجى العِلم أنّ استخدام event.preventDefault()
في معالجات أحداث اللمس سيؤدي أيضًا إلى منع بعض السلوك التلقائي، مثل الانتقال للأعلى أو للأسفل وحدث النقر.
"في تطبيق "الإنشاء باستخدام Chrome"، لم نريد أن يحدث تكبير/تصغير عند النقر مرّتين على الموقع الإلكتروني، على الرغم من أنّ ذلك إجراء عادي في معظم المتصفّحات. لذلك، نستخدم العلامة الوصفية لإطار العرض لإخبار المتصفّح بعدم التكبير عندما ينقر المستخدم مرّتين. ويؤدي ذلك أيضًا إلى إزالة تأخّر النقرة الذي يبلغ 300 ملي ثانية، ما يُحسِّن من استجابة الموقع الإلكتروني. (يتم استخدام ميزة تأخير النقر للتمييز بين النقر مرة واحدة والنقر مرّتين عند تفعيل ميزة التكبير/التصغير بنقرة مرّتين).
<meta name="viewport" content="width=device-width,user-scalable=no">
عند استخدام هذه الميزة، عليك ضمان إمكانية قراءة الموقع الإلكتروني على جميع أحجام الشاشة، لأنّ المستخدم لن يتمكّن من تكبير المحتوى.
إدخال الماوس واللمس ولوحة المفاتيح
في وضع "الاستكشاف الثلاثي الأبعاد"، أردنا توفير ثلاث طرق للتنقّل في الخريطة: الماوس (السحب) والشاشة التي تعمل باللمس (السحب والتصغير/التكبير والدوران) ولوحة المفاتيح (التنقّل باستخدام مفاتيح الأسهم). تعمل جميع طرق التنقّل هذه بشكلٍ مختلف قليلاً، ولكنّنا استخدمنا النهج نفسه في جميعها، وهو ضبط المتغيّرات في معالجات الأحداث والتصرّف وفقًا لذلك في حلقة 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
ملخّص
لقد كانت تجربة ممتعة هي تكييف ميزة "الإنشاء باستخدام Chrome" لتتوافق مع الأجهزة التي تعمل باللمس والتي تتضمّن العديد من أحجام الشاشات المختلفة. لم يكن لدى الفريق خبرة كبيرة في توفير هذا المستوى من التفاعل على الأجهزة التي تعمل باللمس، وتعلمنا الكثير خلال هذه العملية.
تبيّن أنّ أكبر تحدّي هو كيفية حلّ مشكلة تجربة المستخدم والتصميم. كانت التحديات الفنية هي إدارة العديد من أحجام الشاشات وأحداث اللمس ومشاكل الأداء.
على الرغم من أنّه كانت هناك بعض التحديات في مخطّطات الإضاءة WebGL على الأجهزة التي تعمل باللمس، إلا أنّ هذا الإجراء كان أفضل مما توقّعنا. أصبحت الأجهزة أكثر فعالية، كما تحسّنت عمليات تنفيذ WebGL بشكل سريع. نعتقد أنّنا سنستخدم WebGL على الأجهزة بشكلٍ أكبر في المستقبل القريب.
الآن، يمكنك إنشاء محتوى رائع إذا لم يسبق لك ذلك.