ربما سمع المستخدمون النشطون في Telegram ، وخاصةً المشتركين في Pavel Durov ، شيئًا عن حقيقة أن Telegram قد نظمت منافسة لمطوري iOS و Android و JavaScript وكذلك للمصممين على مواقع الإنترنت الخاصة بك هذه. على الرغم من حقيقة أنه كان حدثًا ملحميًا مع توزيع الجوائز الصلبة (حصل أحد المشاركين على 50 ألف دولار في المقام الأول ، بعد أن كتب أسرع وأسرع تطبيق Android) ، إلا أنهم بطريقة ما كتبوا القليل عنه ، على الأقل على Runet. سيحاول منشوري الأول إصلاح الموقف.
نظرًا لأنني مطور جافا سكريبت كامل (لأكون مطورًا دقيقًا ، مطور برنامج TypeScript) ، فقد قررت اختبار نفسي. مانيلا ليست مجرد صندوق جائزة ، ولكن أيضًا التنسيق نفسه: هذه ليست مسابقة برمجة حيث يكون التجريد وسرعة التفكير مهمين. كان كل شيء في المجمع مهمًا هنا: التجربة ، وسرعة التطوير على المدى المتوسط ، والذوق في قضايا واجهة المستخدم ، ومعرفة علوم الكمبيوتر ككل ، والنقد الذاتي. وفقًا لشروط المسابقة ، كان من الضروري تطوير مكتبة لعرض الرسوم البيانية لأحد المنصات: iOS أو Android أو Web.
لم يتنافس مطورو الأنظمة الأساسية المختلفة مع بعضهم البعض ، وكان لكل منصة فائزين خاصين بها. كانت المعايير الرئيسية هي: سرعة العمل (بما في ذلك على الأجهزة القديمة) ، والاتساق مع التصميم ، والرسوم المتحركة السلسة والحد الأدنى لحجم التطبيق. لا يمكن استخدام الحلول والمكتبات الموجودة بالفعل ، فكل شيء كان يجب كتابته من البداية.
قبل ذلك ، شاركت في مسابقات للمطورين ، حيث لم يتم تخصيص أكثر من 5 ساعات لجميع المهام ، وكان لا بد من قضاء هذه الساعات في ضغوط هائلة. على الرغم من حقيقة أن Telegram لم تتطلب مثل هذه الضغوط لإكمال المهمة ، فهي واحدة من أصعب المسابقات التي اضطررت للمشاركة فيها. اتضح أن المهمة غير المعقدة على ما يبدو معقدة للغاية لدرجة أنني لو كنت قد دفعت مقابلها ، سأكون قادرًا على قطع هذه "الرسوم البيانية" لعدة أشهر ، في محاولة لإيجاد حل وسط بين أداء الكود والوئام المعماري. لقد ساعد ذلك في تخصيص ثلاثة (
محدث: اثنان ، وذلك بفضل
vlad2711 للتعديل) لمدة أسبوع. أخذ بعض المنافسين على وجه التحديد إجازة لتخصيص المزيد من الوقت للمسابقة ، وقررت الجمع بين التطوير للمسابقة في المساء وفي عطلات نهاية الأسبوع مع العمل في "
Ontanta " كالمعتاد.
CANVAS مقابل SVG
كانت القضية المعمارية الأكثر أهمية التي واجهتنا جميعًا هي اختيار أداة تقديم الرسومات. في الوقت الحالي ، توفر لنا معايير الويب طريقتين: من خلال إنشاء رسومات svg سريعة الحركة وقماش قماش قديم جيد. وهنا إيجابيات وسلبيات كل.
قماش
+ براعة مطلقة - مع القدرة على تغيير لون أي بكسل على القماش ، يمكنك رسم أي شيء تريده.
+ [المحتملة] عالية الأداء - إذا كنت تستطيع إعداد قماش ، يمكن أن تظهر أداء جيدا. سيكون من الرائع استخدام webgl ، لكن دعمه على الهواتف الذكية ضعيف.
- جميع العمليات الحسابية وكل عمليات التجسيد باليد - على عكس SVG ، حيث يمكن ضبط النقاط الوسيطة للخط متعدد الطبقات مرة واحدة ، وبعد ذلك يمكنك التعامل مع صندوق العرض لتحريك "الكاميرا" على طول أقسام الخطوط المتعددة ، مع وجود كل شيء على القماش أكثر تعقيدًا: لا توجد "كاميرات" هنا ، إحداثيات فقط من الزاوية اليسرى العليا ؛ إذا كنت بحاجة إلى "نقل" منطقة العرض الحالية على الرسم البياني ، فيجب عليك إعادة حساب جميع إحداثيات جميع نقاطها بالنسبة للموضع الجديد لمنطقة المشاهدة. وبعبارة أخرى ، فإن viewbox ، والذي في svg خارج الصندوق ، يجب تنفيذه في قماش يدوياً.
- الرسوم المتحركة بأكملها يدوية - بناءً على الفقرة السابقة ، يتم تحقيق جميع الرسوم المتحركة الممكنة من خلال إعادة حساب الإحداثيات وقيم اللون والشفافية وإعادة رسم المشهد بأكمله عدد المرات في الثانية الواحدة ، وكلما زاد عدد مرات إعادة فرز المشهد وإعادة رسمه ، زادت سلاسة الحركة.
SVG
+ رسم بسيط - ما عليك سوى إضافة الخطوط والأشكال الضرورية والمزيد إلى SVG مرة واحدة ، من خلال التعامل مع معلمات إطار العرض واللون والشفافية ، مما يوفر التنقل في الرسم البياني.
+ تطبيق بسيط للرسوم المتحركة - مرة أخرى ، استنادًا إلى الفقرة السابقة ، يكفي تحديد قيم جديدة لصندوق العرض ولون وشفافية عدد المرات في الثانية ، وسيتم إعادة رسم الصورة بنفسها ، وسوف يعتني المستعرض بهذا. بالإضافة إلى ذلك ، لا تنسَ أن الأشكال والأشكال البدائية في SVG يمكن تصميمها في CSS ، بحيث يمكن تحريكها باستخدام الرسوم المتحركة CSS3 ، والتي تفتح أوسع إمكانيات للحصول على رسوم متحركة رائعة بأقل جهد.
+ أداء جيد بشكل افتراضي - إذا كان يمكنك بسهولة وضع شيء بطيء واستهلاك مئات الموارد على قماش ، فإن النتيجة المعتمدة على SVG ستبدو دائمًا خفيفة الوزن ولائقة وسلسة.
ولكن هناك جانب الآخر للعملة.
- فرص متواضعة للتحسين - نظرًا لأننا لا نرسم svg ، ولكن المستعرض ، فمن المستحيل التحكم في هذه العملية - إذا كنت ترغب في زيادة الأداء ، على سبيل المثال ، من خلال تخزين العناصر الفردية المرسومة بالفعل ، لا يمكنك القيام بذلك بأي طريقة. غالبًا ما يتم ذلك بالفعل بواسطة المستعرض ، لكن لا يمكننا التأكد من ذلك حتى النهاية.
- الأدوات المحدودة - في SVG ، لم نعد نتحكم في كل بكسل من اللوحة القماشية ، لكن فكر في الكود في إطار بدائل المتجهات. ومع ذلك ، بالنسبة إلى هذه المهمة ، يعتبر هذا ناقصًا ضئيلًا ، حيث يفرض بعضًا من القيود ، مرة أخرى ، في سياق مهمة المنافسة.
لم أضطر أبدًا إلى تعذيب نفسي باختيار أداة ، حيث إن لديّ سمة شخصية مثيرة للاشمئزاز - أنا الحد الأقصى وكنت معتادًا على استخدام أداةي المفضلة فقط في عملي. لقد حدث أنه منذ أيام طلابي ، عندما كنت أشعر بالتسلية مع DirectDraw ، كانت أداتي المفضلة دائمًا لوحة قماشية "تقوم بما تريد". لقد تحولت اللوحة القماشية لحل مشكلة تنافسية إلى نتائج جيدة حقًا ، لكنها لعبت في يدي واحدة فقط منها فقط: أكبر الفرص للتحسينات ، لأن المعيار الرئيسي كان أداء التطبيق.
رمز جيد ليست جيدة
المهمة واضحة: تحتاج إلى رسم نقاط على القماش في المكان المناسب وفي الوقت المناسب. يبقى أن يكتب الرمز. مرة أخرى ، كان من الضروري الاختيار ، هذه المرة بين كتابة كود مضغوط منتج مع "قاعدة قدم" واحدة بأسلوب إجرائي أو غير منتجة للغاية وحتى أقل ضغطًا في وجهة نظري المفضلة. ربما تكون قد خمنت بالفعل أنني اخترت الخيار الثاني ، وقم بتتبيله مع خيار آخر من المفضلة - TypeScript.
وهذا الاختيار لم يكن صحيحا جدا. نظرًا لاستخدام التجريد والتعبئة ، لا يمكن دائمًا حفظ نتائج الحساب الوسيطة وإرسالها وإعادة استخدامها ، مما يؤثر على الأداء بشكل سيئ. وبسبب الاستخدام الواسع النطاق لهذا ، والذي بدونه يكون OOP في JS مستحيلًا ، فإن الرمز غير صحيح ، بينما الحجم مهم أيضًا.
حان الوقت لإعطاء رابط إلى github:
github.com/native-elements/telechart . إذا كنت مهتمًا ، أوصي بإيلاء الاهتمام لتاريخ التعهدات ؛ فهو يحتفظ بذاكرة محنة التحسين والمحاولات غير الناجحة للضغط على عدة إطارات تجسيد إضافية في الثانية.
حسنًا ، في المسابقة لم أحصل على الجائزة. والمشكلة ، كما يحدث غالبًا مع المبرمجين لدينا ، تبين أنها ليست تجربة غير كافية ، أو خفة دم سريعة أو سرعة ، ولكنها غير كافية للنقد الذاتي: حقيقة أني تمكنت من القيام بذلك تعمل وتبدو كما في الصورة ، لقد سررت ، ولكن بالنسبة للفرامل التقديرية ، فقد ظننت أنني فعلت كل ما بوسعي ، وربما الباقي فعل الشيء نفسه. أشعر بالخجل من الحديث عن هذا ، لكنني كنت متأكدًا من أنني سأحتل المركز الأول أو الثاني. في الواقع ، اتضح أنني كتبت برنامجًا للكبح وعربات التي تجرها الدواب ، ليس الأسوأ ، ولكن بعيدًا عن الأفضل. عندما رأيت عمل المطورين الآخرين ، أدركت أنه ليس لدي أي فرصة ولا يمكنني سوى عض المرفقين. إذا كنت محايدًا في عملي ، فسأشارك في الإنتاجية ، وهو الجزء الأكثر أهمية في مهمة المنافسة.
أحد أكثر الدروس قيمة في حياتي المهنية التي لا أشعر بالتعب من الحصول عليها هو أن المهندس الجيد ، بخلاف الفنان ، على سبيل المثال ، ملزم بإجراء تقييم موضوعي لجودة عمله وتجاهل الثقة بالنفس ، لأن نتيجة عمله يجب ألا ترضي العين فقط ولكن يجب أن تعمل بشكل صحيح وجيد.
كانت هذه هي المرحلة الأولى من المسابقة. تم مكافأة الفائزين بسخاء. لفرحي الذي لا يوصف ، لم تنته القصة هناك ، لأنه تم الإعلان عن المرحلة الثانية:
كان من الضروري تحسين مهارتك ، خلال أسبوع واحد فقط من تنفيذ أنواع إضافية من المخططات. سأري على الفور ما حدث ، وسأخبرك أدناه كيف حدث ذلك.
في حالتي ، قبل إضافة وظيفة جديدة ، كان علي أن أفهم أداء الوظيفة القديمة. المشكلة الأولى أنا حلها
الوخز الرسوم المتحركةحتى إذا كان لديك طاقة كافية لإنتاج 60 إطارًا في الثانية ، فلن تكون الرسوم المتحركة سلسة إذا لم يتم تحديد موضع العنصر أو شفافيته بحلول الوقت المنقضي منذ بداية الحركة. يرجع ذلك إلى فترات زمنية غير متساوية بين علامات التجزئة: على سبيل المثال ، تعمل علامة واحدة بعد 10 مللي ثانية ، والثانية بعد 40 ، بينما بالنسبة للعلامات الأولى والثانية ، انتقل الكائن إلى اليسار بمقدار بكسل واحد ، أي أن سرعة حركته تطفو باستمرار ، بصريا يبدو وكأنه "نشل". بمعنى آخر ، عليك أن تفعل شيئًا خاطئًا:
let left = 10, interval = setInterval(() => { left += 1 if (left >= 90) { clearInterval(interval) } }, 10)
و هكذا:
let left = 10, startLeft = 10, targetLeft = 90, startTime = Date.now(), duration = 1000, interval = setInterval(() => { left = startLeft + (targetLeft - startLeft) * (Date.now() - startTime) / duration if (left >= targetLeft) { left = targetLeft clearInterval(interval) } })
نظرًا لوجود الكثير من المعلمات المتحركة في التعليمات البرمجية ، قمت بتصوير
فئة عالمية تسهل المهمة ، وتضيف أيضًا إلى الرسوم المتحركة. إنه سهل الاستخدام:
let left = Telemation.create(10, 90, 1000) … drawVerticalLine(left.value)
ثم يأتي دور قاعدة 60 إطارًا في الثانية. سوف يفهمني اللاعبون على أجهزة الكمبيوتر: لكي تبدو الرسوم المتحركة مثالية ، يجب تقديمها بسرعة لا تقل عن 60 إطارًا في الثانية. وفقًا لذلك ، يجب ألا يستغرق كل تجسيد للإطار أكثر من 1/60 من الثانية. هذا يتطلب أجهزة قوية ورمز جيد.
مزيد من البحوث أظهرت ذلك
يبطئ رسم اللوحة القماشية إذا كانت هناك عناصر html أعلى اللوحة القماشية .
في البداية ، استخدمت عناصر html "فارغة" من أجل تطبيق التحكم في إطار العرض الحالي:
وضعت هذه العناصر أعلى اللوحة ، وعلى الرغم من عدم وجود محتوى لها ، إلا أنها كانت تستخدم فقط لتتبع أحداث الماوس ، ونتيجة للتجارب تبين أن وجودها يقلل من أداء العرض. عن طريق إزالتها وتعقيد منطق تحديد الأحداث للتحكم في مساحة المشاهدة قليلاً ، قمت بزيادة سرعة تقديم الإطار.
بقي لسحب المسمار الأخير من غطاء نعش الأداء: فعلت
Minimap التخزين المؤقتقبل هذا ، لخطوط minimap تم رسمها كل إطار مرة أخرى. هذه عملية مكلفة لأنها عرضت الجدول بأكمله للعام (365 نقطة لكل سطر). كان الحل الواضح ، الذي كنت كسولًا جدًا لا يمكن تنفيذه من البداية ، هو رسم خطوط المخطط لمصغر الصورة المصغرة مرة واحدة ، وحفظ النتيجة في ذاكرة التخزين المؤقت واستخدام ذاكرة التخزين المؤقت هذه في المستقبل. بعد هذا التحسين ، لم يعد أداء التطبيق محرجًا.
ماذا بعد؟
لا يزال هناك العديد من المعارك الناجحة وليست صعبة للغاية من أجل الأداء: محاولات للتخزين المؤقت لنتائج حسابات الإحداثيات ، والتجارب مع معلمات CanvasRenderingContext2D lineJoin (أسرع في الاختبار) ، لكنها ليست مثيرة للاهتمام للغاية ، لأنها لم تقدم مكاسب ملحوظة في الأداء أو لم تقدمها على الإطلاق.
من الأيام الثمانية ، أمضيت خمسة أيام في تسريع الرمز ، وثلاثة أيام فقط في إكمال الوظيفة الجديدة. نعم ، استغرق الأمر ثلاثة أيام فقط لإضافة أنواع جديدة من الرسوم البيانية ، وهنا أصبح OOP مفيدًا للغاية ، حيث زادت قاعدة الرموز قليلاً. لم يكن لدي ما يكفي من الوقت لإكمال مهمة المكافأة (+5 مخططات إضافية). أعتقد أن تلك الأيام الخمسة التي قضيتها في التخلص من عواقب ثقتي بالنفس ، يمكن أن أقضيها في حل مشكلة المكافأة.
ومع ذلك ، فقد أسفرت عملي عن النتيجة: المركز الرابع وجائزة "العزاء" البالغة ألف دولار:
بالمناسبة ، استمرت المنافسة ، ولكن بدون لي.
أنا سعيد بالمشاركة: بالإضافة إلى كونها ممتعة ومثيرة للاهتمام ، حصلت على خبرة مهنية جيدة ودرس حياة.
بالإضافة إلى ذلك ، استخدمت هذه المكتبة في تطوير برنامج timetracker الخاص بشركتنا ، والذي أخطط أيضًا للحديث عنه في المستقبل القريب.
للمناقشة ، أقترح السؤال التالي: لماذا تحتاج Telegram إلى كل هذا؟ أعتقد أنه مقابل المال الكافي ، ستتلقى Telegram أفضل مكتبة في العالم لعرض الرسوم البيانية: أفضل نتيجة من مئات المحاولات للقيام بعمل أفضل من غيرها. يسمح لك المبدأ التنافسي بالحصول على مستوى عالٍ من الجودة لا يستطيع أحد فعله دون مقابل.
وعدد قليل من الروابط: