كيف صممت تطبيق بومودورو كلوك والدروس التي تعلمتها على طول الطريق

شرعت في رحلة freeCodeCamp الخاصة بي في ديسمبر 2017 ، وأنا أخجل مشروعين من إكمال شهادة تطوير الواجهة الأمامية. يوثق هذا المنشور عمليتي لإكمال مشروع ساعة بومودورو.

ما هي ساعة بومودورو؟

تقنية بومودورو هي إطار عمل لإدارة الوقت بسيط بقدر ما هو فعال - تستخدم مؤقتًا لتقسيم عملك إلى كتل زمنية (عادةً 25 دقيقة) ، مفصولة بفاصل 5 دقائق. بعد كل 4 بومودوروس ، يمكنك أن تأخذ استراحة أطول.

كان علي أن أكمل قصص المستخدمين التالية:

  • يمكنني أن أبدأ بومودورو لمدة 25 دقيقة ، وسوف ينطلق المؤقت بمجرد انقضاء 25 دقيقة.
  • يمكنني إعادة ضبط الساعة لبومودورو القادم.
  • يمكنني تخصيص طول كل بومودورو.

تخطيط تصميم

مبدأ التصميم الخاص بي هو الحفاظ على واجهة المستخدم نظيفة وبسيطة. أحببت فكرة استخدام الطماطم كمؤقت. يوجد عرض عمل / استراحة وعد تنازلي للمؤقت وزر تشغيل / إيقاف مؤقت.

أسفل المؤقت ، كان لدي إعدادات لتعديل مدة العمل وفاصل الوقت وزر إعادة الضبط.

واجهت مشاكل التخطيط

واجهت مشكلات كبيرة في وضع صورة الطماطم في الخلفية تحت العناصر الأخرى. كم أتمنى أن يكون هناك خيار تخطيط يمكنني تحديده! ؟

كان أحد الاقتراحات التي وجدتها هو حفظ صورة الطماطم على لون الخلفية المفضل لدي كصورة جديدة ، ثم استخدام تلك الصورة في الخلفية. كان الجانب السلبي لذلك هو أنه بدأ يبدو متزعزعًا بمجرد أن اختبرت استجابة التخطيط.

في النهاية، تمكنت من الحصول على ذلك الحق مع مزيج من absolute positioning، تعديل topو leftالنسب المئوية، و transform.

#status { position: absolute; top: 45%; left:50%; transform: translate(-50%, -50%);}
.timerDisplay { position: absolute; top: 60%; left: 50%; transform: translate(-50%, -50%);}
#start-btn { position: absolute; bottom: 8%; left: 48%; transform: translate(-50%, -50%);}

كانت الإعدادات السفلية واضحة إلى حد ما. لقد استخدمت CSS Grid لفصل المكونات إلى ثلاثة أعمدة ، بحيث يكون العمود الأوسط نصف عرض الأعمدة الخارجية.

.settings { margin: auto; width: 80%; display: grid; grid-template-columns: 2fr 1fr 2fr; align-items: center;}

مرة أخرى ، اعتدت transformعلى تغيير زر إعادة الضبط لمحاذاة أفضل.

هيكلة الكود الخاص بي - ثم تفكيكه

أجد أنه من المفيد التوصل إلى هيكل الكود الخاص بي إذا قمت بتفصيل المتطلبات:

  • سيتم تبديل المؤقت بين البدء والإيقاف المؤقت عندما أنقر على زر "ابدأ".
  • بمجرد أن يصل المؤقت إلى الصفر ، سينطلق الإنذار.
  • تتبع جلسة العمل دائمًا جلسة استراحة.
  • يمكن تعديل فترات العمل والاستراحة.
  • زر "إعادة الضبط" (كما خمنت) يعيد ضبط المؤقت.

كنت قد أكملت سابقًا ساعة العد التنازلي في دورة Wes Bos JavaScript30 ، لذلك علمت أنه يمكنني استخدام هذه setIntervalالطريقة. قررت أيضًا أن أتحدى نفسي من خلال التمسك بـ Vanilla JavaScript ، وتجنب الاعتماد على jQuery.

وهكذا بدأت في كتابة كود JavaScript الخاص بي. بينما تمكنت من إنشاء ساعة بومودورو وظيفية ، لن أخوض في الإصدار الأول من الكود الخاص بي هنا. هذا لأنني أجريت تغييرات كبيرة عليه بعد تلقي تعليقات بناءة من شخص غريب مذهل على Reddit. ؟

نعم ، تحدث أشياء جميلة على Reddit!

كانت النقاط الرئيسية للتعليقات هي:

  • setInterval(timer, 1000)يستغرق تشغيل 1000 مللي ثانية على الأقل ، ولكنه قد يستغرق وقتًا أطول. لذلك يجب عليك التحقق من مقدار الوقت المنقضي بالفعل ، أو قد تكون ساعتك غير دقيقة.
  • قم بتجميع جميع تحديثات HTML في قسم واحد ، حيث يسهل ذلك تحديث التعليمات البرمجية وتصحيحها.
  • من الجيد عمومًا إنشاء الكود دون التفكير في التمثيل على الإطلاق.
  • تأكد من منطق المؤقت وتخلص من التعليمات البرمجية غير الضرورية.
  • تأكد من أن أسماء المتغيرات وصفية. اترك تعليقات عند الضرورة.

يمكنك عرض أول التزام لي على GitHub.

إعادة هيكلة الكود الخاص بي

بعد الحصول على كل هذه التعليقات القيمة ، قمت بإعادة هيكلة الكود الخاص بي عدة مرات حتى أصبحت راضيًا عنه.

أولاً ، لقد حددت جميع المتغيرات. نظرًا لأنني لم أكن أستخدم jQuery ، فقد تأكدت من التقاط جميع العناصر باستخدام document.querySelector.

let countdown = 0; // variable to set/clear intervalslet seconds = 1500; // seconds left on the clocklet workTime = 25;let breakTime = 5;let isBreak = true;let isPaused = true;
const status = document.querySelector("#status");const timerDisplay = document.querySelector(".timerDisplay");const startBtn = document.querySelector("#start-btn");const resetBtn = document.querySelector("#reset");const workMin = document.querySelector("#work-min");const breakMin = document.querySelector("#break-min");

بعد ذلك ، قمت بإنشاء عنصر الصوت.

const alarm = document.createElement('audio'); alarm.setAttribute("src", "//www.soundjay.com/misc/sounds/bell-ringing-05.mp3");

عند النقر فوق الزر "ابدأ" ، يتم مسح الفاصل الزمني. يتم تعيين فاصل زمني جديد في حالة isPausedالتغيير من صواب إلى خطأ .

زر "إعادة الضبط" يمسح الفاصل الزمني ويعيد ضبط المتغيرات.

startBtn.addEventListener('click', () => { clearInterval(countdown); isPaused = !isPaused; if (!isPaused) { countdown = setInterval(timer, 1000); }})
resetBtn.addEventListener('click', () => { clearInterval(countdown); seconds = workTime * 60; countdown = 0; isPaused = true; isBreak = true;})

و ظيفة الموقت هو المكان الذي يحدث السحر العد التنازلي. تخصم ثانية واحدة من الثواني . إذا كانت الثواني < ؛ 0 ، يتم تشغيل التنبيه ، وتحدد الوظيفة ما إذا كان العد التنازلي التالي يجب أن يكون جلسة عمل أو جلسة استراحة.

function timer() { seconds --; if (seconds < 0) { clearInterval(countdown); alarm.currentTime = 0; alarm.play(); seconds = (isBreak ? breakTime : workTime) * 60; isBreak = !isBreak; }}

حان الوقت الآن للعمل على أزرار +/- للعمل وفواصل المدد. في البداية ، قمت بإنشاء onclickوظيفة لكل زر. بينما كانت وظيفية ، كان هناك بالتأكيد مجال للتحسين.

document.querySelector("#work-plus").onclick = function() { workDuration  5 ? workDuration -= increment : workDuration; }document.querySelector("#break-plus").onclick = function() { breakDuration  5 ? breakDuration -= increment : breakDuration; }

اقترح نفس النوع من Redditor أن أستخدم مصفوفة ترابطية ، وهي في الأساس مجموعة من أزواج القيم الرئيسية.

let incrementFunctions = {"#work-plus": function () { workTime = Math.min(workTime + increment, 60)}, "#work-minus": function () { workTime = Math.max(workTime - increment, 5)}, "#break-plus": function () { breakTime = Math.min(breakTime + increment, 60)}, "#break-minus": function () { breakTime = Math.max(breakTime - increment, 5)}};
for (var key in incrementFunctions) { if (incrementFunctions.hasOwnProperty(key)) { document.querySelector(key).onclick = incrementFunctions[key]; }}

حان الوقت لتحديث HTML!

لقد قمت بإنشاء وظائف لتحديث عرض العد التنازلي وعرض الزر ، وقمت بدمج هذه الوظائف في وظيفة شاملة تعمل أيضًا على تحديث حالة العمل / الاستراحة والمدد.

أخيرًا ، اعتدت document.onclickتشغيل وظيفة updateHTML في كل مرة ينقر فيها المستخدم على الصفحة. اعتدت أيضًا window.setIntervalعلى تشغيل الوظيفة 10 مرات في الثانية لإجراء قياس جيد.

function countdownDisplay() { let minutes = Math.floor(seconds / 60); let remainderSeconds = seconds % 60; timerDisplay.textContent = `${minutes}:${remainderSeconds < 10 ? '0' : ''}${remainderSeconds}`;}
function buttonDisplay() { if (isPaused && countdown === 0) { startBtn.textContent = "START"; } else if (isPaused && countdown !== 0) { startBtn.textContent = "Continue"; } else { startBtn.textContent = "Pause"; }}
function updateHTML() { countdownDisplay(); buttonDisplay(); isBreak ? status.textContent = "Keep Working" : status.textContent = "Take a Break!"; workMin.textContent = workTime; breakMin.textContent = breakTime;}
window.setInterval(updateHTML, 100);
document.onclick = updateHTML;

وهذا هو ختام مشروعي!

يمكنك عرض مشروعي النهائي هنا.

افكار اخيرة

أهم ما استخلصته من هذا المشروع هو أنني يجب أن أهدف إلى البساطة من حيث تصميم الكود ، لأنه شرط أساسي للاعتمادية. ستجعل الكود الخاص بي سهل الفهم ، وسهل التصحيح ، والتحديث بسهولة.

أتذكر أيضًا فوائد البرمجة المقترنة ومراجعات الكود ، خاصة عندما يكون المرء جديدًا في الترميز.

لا يزال هناك الكثير لنتعلمه. لكن في الوقت الحالي ، اسمحوا لي أن أكافئ نفسي بطبق من باستا البومودورو.