landing page 101 تطوير صفحة هبوط (Landing Page)


نذير صغير

في المقال الأول تكلمنا حول أساسيات صفحات الهبوط، وفي المقال الثاني صممنا صفحة هبوط في Photoshop باستخدام ما تعلمانه، الآن سنكمل ما بدأنا به ببناء صفحة الهبوط فعليا، سنمر على كل المراحل إنطلاقا من توزيع الملفات إلى صفحة جاهزة، هيا لننطلق.

يمكنك تحميل الشِفرة المصدرية لهذا الدرس لمعاينة النتيجة النهائية. وهذا تذكير بواجهة الصفحة:

landing-page.thumb.png.f649d7622729e75b2

الأساسيات

في البداية سنقوم بتوضيح ما سنقوم باستخدامه ولماذا سنستخدمه.

  • أولا، لدينا الصور التي حصلنا عليها من ملف PSD الذي قمنا بتصميمه في درس التصميم، فلنستخدمها.
  • لن نستخدم خط أيقونات لسبب وجيه وهو أنها ستكون مضيعة وقت وبدون فائدة فعلية فالصفحة مجرد صفحة واحدة لذا لا يوجد الكثير لنقلق بشأنه (بشأن عدد طلبات HTTP كمثال).
  • سنستخدم هيكلة HTML5 Boilerplate وهو هيكل عمل مصغر لبناء صفحات HTML حيث يحتوي على تقسيم مجلدات ممتاز، ويوزدك ببعض الملفات المهمة مثل .htaccess و ملف index كامل يحتوي على كل شيء تحتاجه.
  • سنستعمل بعض إضافات إطار عمل Bootstrap، وهم:
    •  Affix من أجل تثبيت قائمة التصفح عند النزول.
    • إضافة Scrollspy من أجل تغيير لون الأيقونات كلما ممرنا على مرحلة.
  • سنستخدم إضافة Owl carousel 2.0 من أجل جزء الشهادات، إذا كنت تملك إضافة أخرى فأنت حر باستخدامها.
  • سنستعمل موقع TinyPNG من أجل ضغط الصور التي حصلنا عليها من ملف PSD.
  • سنستخدم موقع Everything Font من أجل تحويل خط Cocon إلى نسخة الويب (woff) حتى نستخدمها.

تقسييم الملفات

سيكون تقسيمنا للملفات على الشكل التالي:

project/
├── css/
│   ├── main.css
│   ├── normalize.css
│   └── owl.carousel.css
├── fonts/
│   └── CoconNextArabicLight.woff
├── js/
│   ├── main.js
│   └── vendors/
│       ├── bootstrap.min.js
│       ├── jquery-1.11.2.min.js
│       └── owl.carousel.min.js
├── img/
├── index.html
└── favicon.ico

عند تحميل إطار عمل H5BP فإنه يزودنا بالتقسييم بالتالي بشكل افتراضي، ما قمنا به نحن هو إضافة بعض الملفات في بعض الأماكن فحسب، مجلد img يحتوي على الصور التي حصلنا عليها من المرحلة السابقة عبر استخراجها من فوتوشوب، كما قمنا بإضافة bootstrap.js الذي يحتوي على كلٍ من affix وscrollspy. 
وقمنا بحذف بعض الملفات مثل modernizer وhtml5shiv لأننا لن ندعم المتصفحات القديمة.

البداية

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

نحن نملك خط Cocon بصيغة سطح المكتب (ttf). لا ضرر في هذه الصيغة إطلاقا، هي تعمل بشكل ممتاز في المتصفحات، ولكننا نستطيع تقليل الحجم إلى النصف عبر تحويل الخط إلى صيغة woff المخصصة للويب وذلك عبر موقع التحويل (ستجد الخط محولا في الشفرة المصدرية لهذا الدرس). 

نحتاج إلى ضغط الصور، هناك فائدة عظيمة نكسبها هنا، موقع TinyPNG سيقوم بتقليل حجم الصور إلى أكثر 70% مع الحفاظ على الجودة، وهذا حجم ممتاز لذلك سنقوم بذلك أيضا.

الآن سنعد بعض الأمور الابتدائية في ملف CSS:

@import url(owl.carousel.css);

@font-face {
    font-family: Cocon;
    src: url(../fonts/CoconNextArabicLight.woff) format('woff');
}

*, *:before, *:after {
    box-sizing: border-box;
}

body {
    font-family: cocon, sans-serif;
    background: #fff;
    position: relative;
}
  • سنقوم باستدعاء ملف owl.carousel من أجل تأثيرات الحركة في السكربت، سنقوم بإدراج خط Cocon للاستخدام، سنستعمل box-sizing من أجل احتواء العناصر بشكل أفضل. 
  • سنضيف بعض الأمور الافتراضية لوسم body
  • مكتبة scrollspy تحتاج إلى أن يكون وسم body بخاصية position:relative من أجل أن تعمل.

هيكلة الصفحة

في سابق الأمر، كنُت قد خططت لبناء الصفحة عبر إطار عمل Bootstrap المعروف، وسبق وقلت هذا في المقال السابق، ولكني تراجعت عن الأمر لقلة الفوائد مقارنة بالسيئات، ووجدتها فرصة حسنة لاستخدام Flex module والتكلم عنه أكثر.

أما بخصوص Bootstrap فلدي آرائي حوله، من بينها أنّه إطار مناسب تماما لإطلاق المشاريع بسرعة من أجل التجريب (لاحظ الاسم Bootstrap) كما أنّه إطار مناسب لبناء المشاريع الداخلية حيث لن يتم إطلاق المشروع لجمهور فعلي، إلا إذا كنت تنوي تخصيصه حتى النخاع فتغير مظهره، فذلك شأنك.

الصفحة مكونة من 6 أجزاء أساسية: 

  1. رأس الصفحة.
  2. روابط التصفح عبر الصفحة.
  3. مميزات التطبيق.
  4. الاقتباسات (أو الشهادات).
  5. الأرقام (أو التحليلات).
  6. التحميل والمشاركة.

والهيكة الأساسية ستكون كالتالي:

<header class="l-header">
  <div class="l-app-preview">
  </div>
  <div class="l-page-fold">
  </div>
</header><!-- .l-header -->

<nav class="l-sticky-nav">
  <div class="sticky-nav">
  </div><!-- sticky-nav -->
</nav><!-- l-sticky-nav -->

<article id="features-section">
  <section class="l-app-feature">
  </section>
</article><!-- #features-section -->

<article id="testimonials-section">
</article><!-- #testimonials-section -->

<article id="analysicts-section">
</article><!-- #analysicts-section -->

<article id="download-section">
</article><!-- #download-section -->

قمنا بتقسييم كل جزء من الصفحة إلى وسم article مستقل (ما عدى روابط التصفح ورأس الصفحة حيث يملكان وسوما افتراضية خاصة بهم من قبل).

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

روابط التصفح تملك جزئين، حاوي رئيسي وحاوي الروابط، وهناك سبب وجيه لهذا التكرار، بحيث عندما يتم تفعيل تثبيت الروابط على أعلى الشاشة، نرى أن يبقى هناك مكان فارغ (في المكان السابق الذي كانت الروابط موجودة فيه) حيث لا يحدث اختلال في الصفحة، الجزء المسؤول عن هذا سيكون l-sticky-nav، أيضا لاحظوا أنّ كل الروابط التي تحتوي على حرف L قبل اسمها (اختصارا لـ layout) ستكون مسؤولة عن الهيكلة بشكل خاص.

رأس الصفحة

<header class="l-header">
  <div class="l-app-preview">
    <img src="img/home-screen.png" alt="">
  </div>
  <div class="l-page-fold">
    <h1 class="display-title">اكتشف أشخاصا بنفس اهتماماتك</h1>
    <p class="display-content">تعرف على أشخاص يشاركونك الهوايات والاهتمامات شارك، اقرء، وتواصل مع الجميع عبر تطبيق اهتمامات</p>
      <a href="" class="display-badge">
        <img src="img/badge.png" alt="حمل من متجر التطبيقات">
      </a>
  </div>
</header><!-- .l-header -->

كان ذلك هو رأس الصفحة، بكل بساطة وكما أشدنا سابقا، الآن حان وقت إضافة بعض CSS له.

.l-header {
    display: flex;
    justify-content: center;
    align-items: center;

    width: 100%;
    min-height: 100vh;/* used to make sure the header will cover the screen */
    position: relative;
    padding: 48px 0; /* سيعطي هذا بعض المساحة للشاشات الصغيرة حيث المحتوى أكبر من ارتفاع الشاشة */

    background: url(../img/background.png) center center no-repeat;
    background-attachment: fixed;
    background-size: cover;
}

الشفرة المصدرية مقسمة لثلاث أجزاء، كل جزء له مهمته المعينة.

الجزء الأول مسؤول عن هيكلة رأس الصفحة، نحن نطلب منه أن يستخدم flex كنظام للهيكلة عبر display:flex وأن يقوم بوضع جميع العناصر في منتصف الصفحة أفقيا عبر justify-content:center والواقع أنها تقبل العديد من الخيارات الأخرى، منها flex-start التي تضع جميع العناصر في بداية الصفحة (اليمين في حالة العربية، أو اليسار في حالة اللاتينية، لكن الأمر لا علاقة له باتجاه الصفحة في واقع الأمر، بل باتجاه ترتيب العناصر عبر خاصية flex-direction التي تقبل 4 خيارات وهي ترتيب العناصر بشكل أفقي من بداية الصفحة لنهايتها عبر row ومن نهاية الصفحة إلى بدايتها عبر row-reverse أو من أعلى الصفحة إلى أسفلها عبر column أو العكس عبر column-reverse وسنتكلم عن هذا لاحقا) أو خيار flex-end الذي يقوم بالعكس، والخياران اللذان ستستخدمها كثيرا وهما space-around حيث يتم توزيع المساحة بين العناصر بالتساوي (وبالتالي في المنتصف ولكن مع مساحة بينهم وقبلهم وبعدهم) و space-between والتي تقوم بتوزيع المساحة بين العناصر بدون إضافة مساحة قبل العناصر وبعدهم (أيّ يتم تركيز كامل المساحة في المنتصف). الخيار الثالث يقول أننا نريد أن نضع العناصر في المنتصف عموديا وذلك عبر align-items:center والواقع أنّ الخاصية تملك أيضا خيارات كثيرة لا متسع لنا لشرحها كاملة الآن لأنها تحتاح لشرح بالصور في الغالب، ومنها flex-start التي تضع العناصر أعلى الصفحة (أو بالأحرى حسب الطريقة التي تم تحديد اتجاه المحور العمودي فيها عبر flex-direction كما بيّنا سابقا).

الجزء الثاني، هو مسؤول عن جعل حجم رأس الصفحة بكامل أبعاد الشاشة، نستطيع جعله بكامل عرض الشاشة عبر width:100% أو بالمثل عبر width:100vw لا فرق بينهم لأنّ حاوي رأس الصفحة نفسه بكامل عرض الصفحة، تبقى المشكلة في الارتفاع، تحديد 100% لن ينفع وتحديد رقم معين لن ينفع إطلاقا مع كل الشاشات، الحل الوحيد هو استخدام وحدة القياس الجديدة vh والتي تقيم أبعاد الشاشة وليس العناصر، بحيث 100vh تعني كامل ارتفاع الشاشة.

في الجزء الثالث سنقوم بإضافة خلفية، وضعها في المنتصف، نعطيها خاصية cover حتى تتمدد (مع المحافظة على الأبعاد ratio) على حسب الشاشة.

.l-header:before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 100%;
    background-color: rgba(0,0,0,0.64);
}

هذا الجزء مسؤول عن إضافة تأثير السواد فوق الصورة، البعض يقوم بإضافة عنصر (div) آخر داخل رأس الصفحة من أجل تحقيق هذا، ولكن هذا الحل أكثر نظافة، لاشيء فعلي لنشرحه هنا عدى أنّ عدم إدراج خاصية content (حتى ولو كانت فارغة) سيجعل الشفرة المصدرية السابقة غير فعالة، فأشباه الأصناف (pseudo-classes) لا تعمل بدونه.

.l-app-preview,
.l-page-fold {
    width: 50%;
    z-index: 10;
    position: relative; /* نحتاج إلى هذا الجزء لأن خاصية z-index لا تعمل مع static */
}

هنا نقوم بإعطاء كل جزء منهم 50% من عرض الحاوي الخاص بهم، حتى يتم توزيع المحتوى بشكل عادل، خاصية z-index حتى نتأكد من أنّ المحتوى فوق تأثير السواد على الخلفية (لن يشكل هذا مشكلة في الأغلب لأننا استخدمنا شبه صنع before الذي سيضعه قبل العناصر أصلا). 
خاصية position:relative تم وضعها من أجل z-index لأنّه لا يعمل بدون تحديدها (في الواقع هو يعمل مع الجميع عدى static أيّ القيمة الافتراضية).

مع إضافة بعض التأثيرات على العنوان، والقيام بتوسيط الصفحة، نخرج بهذه الشِفرة:

.l-header {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    min-height: 100vh;
    position: relative;
    padding: 48px 0;
    background: url(../img/background.png) center center no-repeat;
    background-attachment: fixed;
    background-size: cover;
}

.l-header:before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 100%;
    background-color: rgba(0,0,0,0.64);
}

.l-app-preview,
.l-page-fold {
    width: 50%;
    z-index: 10;
    position: relative;
}

.l-app-preview img {
    display: block;
    margin: 0 auto;
}

.l-page-fold {
    padding-left: 24px;
}

.display-title {
    color: #fff;
    font-size: 55px;
    line-height: 1.2;
    margin-bottom: 36px;

}

.display-content {
    color: #fff;
    font-size: 28px;
    line-height: 1.7;
    margin-bottom: 128px;
}

تاليا سنعمل على روابط التصفح

روابط التصفح

الهيكلة كالتالي:

<nav class="l-sticky-nav">
    <div class="sticky-nav">
        <div class="nav-element">
            <a href="#features-section" class="nav-element--link active">
                <img src="img/equalizer.png" alt="" class="nav-element--image">
            </a>
        </div>
        <div class="nav-element">
            <a href="#testimonials-section" class="nav-element--link">
                <img src="img/id.png" alt="" class="nav-element--image">
            </a>
        </div>
        <div class="nav-element">
            <a href="#analysicts-section" class="nav-element--link">
                <img src="img/globe.png" alt="" class="nav-element--image">
            </a>
        </div>
        <div class="nav-element">
            <a href="#download-section" class="nav-element--link">
                <img src="img/download.png" alt="" class="nav-element--image">
            </a>
        </div>
    </div><!-- sticky-nav -->
</nav><!-- l-sticky-nav -->

لاشيء لشرحه، هيا لنطلع على شِفرة CSS:

.l-sticky-nav {
    margin-bottom: 128px; /* هذا الجزء منفصل عن روابط التحكم، بالأحرى عندما تلتصق الروابط في أعلى الشاشة سيبقى هذا في مكانه */
}

.l-sticky-nav.fixed {
    padding: 50px; /* عندما يتم تثبيت التثبيت على روابط التصفح، سيبقى هذا في مكانه حتى لا تختل المساحة */
}

سنعطي الحاوي الأول بعض المساحة في الأسفل، أيضا عندما يتم تثبيت الروابط سيصبح l-sticky-nav فارغا وبالتالي يكون ارتفاعه 0، مما يلغي فائدته، لذا في تلك الحالة سنجعل ارتفاعه 100px.

الآن بالنسبة للحاوي الثاني:

.sticky-nav {
    display: flex;
    justify-content: space-around;
    flex-wrap: wrap;
    padding: 24px;
    background-color: #fff;
    box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.1);
}

خاصية flex-wrap تحدد هل سيقوم الحاوي بوضع كامل العناصر في سطر واحد (عبر إعطائه no-wrap أيضا الخاصية الافتراضية) أو بوضع العناصر في سطر جديد إن لم يكن هناك متسع من المكان (عبر wrap)، عدى هذا كل شيء واضح، نقوم بتوسيط العناصر فحسب.

.sticky-nav.affix {
    top: 0;
    right: 0;
    width: 100%;
    z-index: 10;
    position: fixed;
}

الآن عند تفعيل التثبيت سيتم (عبر Javascript) إضافة class إلى الحاوي الثاني، إنطلاقا منه سنقوم بتحديد ما نريده، وهو تثبيت العناصر في أعلى الشاشة، لكن في العلم أنّه عند إضافة خاصية fixed فالعنصر يخرج من نسق الصفحة، وشيء آخر وهو أنّ خاصية display:block لا تفيد بشيء حينها، لذا العنصر لن يأخذ كامل المساحة الأفقية التي يجدها (width:100%) بل يأخذ ما يحتاجه فحسب، لذا علينا تحديد الأمر بأنفسنا.

.nav-element--image {
    filter: grayscale(100%);
    -webkit-filter: grayscale(100%);
    transition: all 200ms ease-out;
}

.nav-element--link.active .nav-element--image,
.nav-element--link:hover .nav-element--image {
    filter: grayscale(0);
    -webkit-filter: grayscale(0);
}

نقوم بتحويل جميع الأيقونات إلى الرمادي عدى الأيقونات المفعلة (.active) أو التي يكون المؤشر فوقها.

وهكذا نكون انتهينا من جزء آخر، الدور على جزء مميزات التطبيق.

مميزات التطبيق

الهيكلة كالتالي:

<article id="features-section">
    <section class="l-app-feature">
        <div class="app-feature-preview">
            <img src="img/feature-1.png" alt="" class="app-feature-image">
        </div>
        <div class="app-feature-content">
            <h2 class="app-feature-title">أضف، تابع، وتواصل مع من تشاركه الاهتمام</h2>
            <p class="app-feature-text">أضف إلى قائمة أصدقائك من تجدهم يشاركونك نفس الاهتمام، صنف أصدقائك على حسب اهتماماتك، وتواصل مباشرة معهم</p>
        </div>
    </section>
    <section class="l-app-feature">
        <div class="app-feature-preview">
            <img src="img/feature-2.png" alt="" class="app-feature-image">
        </div>
        <div class="app-feature-content">
            <h2 class="app-feature-title">شارك ما تجده مميزا، وعبر عن رأيك</h2>
            <p class="app-feature-text">أكتب المقالات لتشاركها مع دائرة أصدقائك، وأقرء مقالات الجديدة أو اكتشف اهتمامات جديدة</p>
        </div>
    </section>
    <section class="l-app-feature">
        <div class="app-feature-preview">
            <img src="img/feature-3.png" alt="" class="app-feature-image">
        </div>
        <div class="app-feature-content">
            <h2 class="app-feature-title">ابني حسابك الشخصي، وأحصل على متابعين</h2>
            <p class="app-feature-text">ابني حسابك الشخصي الخاص، شارك صورك وسيرتك، ودع الناس يقومون بمتابعتك</p>
        </div>
    </section>
</article><!-- #features-section -->

تم تقسييم كل جزء إلى section خاص به يحتوي بدوره على قسمين، واحد للصورة وواحد للعنوان والشرح، الأمر شبيه تماما برأس الصفحة، في الواقع سيعمل بنفس مفهوم رأس الصفحة.

.l-app-feature {
    display: flex;
    justify-content: center;
    align-items: center;

    padding: 128px 0;
}
    .l-app-feature:nth-child(even) {
        flex-direction: row-reverse;
        background: #13191C;
    }
        .l-app-feature:nth-child(even) .app-feature-title,
        .l-app-feature:nth-child(even) .app-feature-text {
            color: white;
        }

الشيء الوحيد الجديد هنا هو (nth-child(even نحن هنا نقوم بتحديد كل العناصر التي تكون رتبتها رقما زوجيا (الثاني والرابع والسادس، يمكن أيضا القيام بالأمر عبر تمرير 2n+2 بدل evenمن أجل نفس النتيجة، في الواقع تستطيع تمرير أي معادلة من الشكلة an+b من أجل استهداف العناصر التي تريدها) قمنا بتمرير flex-direction:row-reverse من أجل عكس اتجاه المحور الأفقي حتى تبدأ العنصر من الاتجاه المعاكس، هذا سيجعل الصورة على اليسار والمحتوى على اليمين. وهذه كامل الشِفرة

.l-app-feature {
    display: flex;
    justify-content: center;
    align-items: center;

    padding: 128px 0;
}
    .l-app-feature:nth-child(even) {
        flex-direction: row-reverse;
        background: #13191C;
    }
        .l-app-feature:nth-child(even) .app-feature-title,
        .l-app-feature:nth-child(even) .app-feature-text {
            color: white;
        }
.app-feature-preview,
.app-feature-content {
    width: 50%;
    max-width: 600px;
}

.app-feature-preview img {
    display: block;
    margin: 0 auto;
}

.app-feature-title {
    font-size: 36px;
    color: #f85940;
    margin-bottom: 36px;
}

.app-feature-text {
    font-size: 28px;
    color: rgba(30,30,30,.9);
    line-height: 1.7;
}

جزء الشهادات

في هذا الجزء قمنا باستخدام carousel من أجل عرض الشهادات والهيكلة كانت كالتالي:

<article id="testimonials-section">
    <h1 class="section-title">فلنسمع رأي البقية</h1>
    <section class="testimonials-carousel">
        <div class="testimonial-item">
            <div class="testimonial-text">أتمنى لو كان هذا التطبيق موجودا قبل سنة</div>
            <div class="testimonial-avatar"><img src="img/face-1-128.png" alt="" class="testimonial-image"></div>
        </div>
    </section>
</article><!-- #testimonials-section -->

أضفنا عنوانا، إلى جانب وضع الشهادات في حاوي خاص بهم من أجل تحديده عبر Javascript، ثم وضعنا لكل شهادة حاوي خاص بها testimonial-text يحتوي على محتوى الشهادة بالإضافة إلى صورة.

.testimonial-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 24px 0;
    margin-bottom: 48px;
}

هيكلة كل عنصر يتكون بسيطة، استخدما flex-direction:column لترتيب العناصر عموديا، كما استخدما align-items:center لتوسيط العناصر أفقيا (لاحظ أننا استخدمناها سابقا لتوسيط العناصر عموديا، ولكن لأن محور الترتيب اختلف الآن فهي ترتب العناصر أفقيا إلى الوسط)

.testimonial-text:before,
.testimonial-text:after {
    font-size: 48px;
    display: inline-block;
    padding: 0 16px;
}
.testimonial-text:before {
    content: "”";
}
.testimonial-text:after {
    content: "“";
}

قمنا هنا بإضافة علامتي تنصيص قبل وبعد الشهادة، قمنا بتكبير حجمهما ووضع بعض الفراغ بينهما وبين الشهادة.

OwlCarousel (السكربت المستخدم من أجل عرض الشهادات) يعطينا أزرار التصفح بين الشهادات (على شكل نقاط) ولكنها بدون أيّ تأثير لذا سنقوم بذلك يدويا

.owl-dots {
    text-align: center;
}

.owl-dot {
    height: 16px;
    width: 16px;
    border: 2px solid #E4523B;
    border-radius: 50%;
    display: inline-block;
    margin: 0 4px;
}

.owl-dot.active {
    background: #E4523B;
}

تحليلات الأرقام

يحين وقت أرقام التحليلات، من أجل إطفاء التغيير وإضافة التركيز قمنا بإضافة خلفية لهم، هيكلة الجزء كانت كالتالي:

<article id="analysicts-section">
    <h1 class="section-title section-title__white">بعض الأرقام</h1>
    <div class="l-analysicts">
        <div class="analysicts-item">
            <div class="item-image">
                <img src="img/articles.png" alt="">
            </div>
            <h1 class="item-number">320</h1>
            <h2 class="item-title">مقالة نشرت</h2>
        </div>
        <div class="analysicts-item">
            <div class="item-image">
                <img src="img/groups.png" alt="">
            </div>
            <h1 class="item-number">57</h1>
            <h2 class="item-title">مجموعة أنشئت</h2>
        </div>
        <div class="analysicts-item">
            <div class="item-image">
                <img src="img/users.png" alt="">
            </div>
            <h1 class="item-number">+8000</h1>
            <h2 class="item-title">عضو مُسجل</h2>
        </div>
        <div class="analysicts-item">
            <div class="item-image">
                <img src="img/upload.png" alt="">
            </div>
            <h1 class="item-number">450</h1>
            <h2 class="item-title">ملف رفع</h2>
        </div>
    </div>
</article><!-- #analysicts-section -->

منطق الهيكلة شبيهة بهيكلة مميزات التطبيق والشِفرة والتقسييم كانا بسيطان إلى أقصى حد:

#analysicts-section {
    position: relative;
    padding: 48px 0;
    margin: 48px 0;

    background: url(../img/numbers-background.png) center center no-repeat;
    background-attachment: fixed;
    background-size: cover;
}

.l-analysicts {
    display: flex;
    justify-content: space-around;
    flex-wrap: wrap;
}

.analysicts-item {
    text-align: center;
    color: white;
}

جزء التحميل والمشاركة

هذا الجزء بسيط إلى أقصى حد، عنوان بسيط مع زر للتحميل و 4 أزرار للدلالة على شبكات التطبيق الاجتماعية

<article id="download-section">
    <h1 class="section-title">إذا، هل قررت الانضمام؟</h1>
    <div class="cta-button"><img src="img/badge.png" alt=""></div>
    <div class="l-social-links">
        <a href="" class="social-link">
            <img src="img/facebook.png" alt="شارك في Facebook">
        </a>
        <a href="" class="social-link">
            <img src="img/twitter.png" alt="شارك في Twitter">
        </a>
        <a href="" class="social-link">
            <img src="img/pinterest.png" alt="شارك في Pinterest">
        </a>
        <a href="" class="social-link">
            <img src="img/google.png" alt="شارك في Google+">
        </a>
    </div>
</article>

وبنفس البساطة أيضا

.cta-button {
  margin: 36px 0;
  text-align: center;
  display: block;
}

.l-social-links {
  display: flex;
  justify-content: space-around;
  padding: 48px 0 64px;
}

Javascript

حان الوقت لإضافة Javascript إلى الصفحة، سنبدأ بإضافة Affix لروابط التصفح من أجل تثبيتهم.

تنويه: في عالم مثالي كنا لنستخدم position:sticky لتقوم بالمهمة لنا، للأسف المتصفح الوحيد الذي يدعمها هو فيرفوكس، متصفح chrome كان يدعمها من قبل ولكنهم أزالوا الدعم في الوقت الحالي، لذا affix يعتبر أشبه بـِ polyfill.

لدينا الشِفرة التالية التي سيتقوم بكل شيء لنا:

$('.sticky-nav').affix({
  offset: {
    top: $('.l-header').outerHeight()
  }
});
$('.sticky-nav').on('affix.bs.affix', function () {
    $('.l-sticky-nav').addClass('fixed');
});
$('.sticky-nav').on('affixed-top.bs.affix', function () {
    $('.l-sticky-nav').removeClass('fixed');
});

نقوم بتفعيل السكربت عبر إضافته إلى sticky-nav ثم نقوم بتحديد offset والذي يعني متى يتم تفعيل السكربت، نحن نريده أن يتفعل حالما يتم المرور عليه، نريد حساب كامل المسافة التي فوقه، وتلك المسافة ستكون حجم رأس الصفحة، لذا نضيفها عبر ()outerHeight التي تحسب الارتفاع الخارجي (من ضمنه padding).

ثانيا نقوم بالاستماع إلى بعض الأحداث (events) والسكربت يوفر لنا 4 أحداث، سنستخدم اثنين فحسب، أول حدث ينطلق عندما يتم تفعيل السكربت مباشرة، وفي تلك اللحظة نضيف fixed إلى الحاوي من أجل أن يستغل المكان، تاليا نستمع للحدث الثاني والذي ينطلق حالما يتم إزالة السكربت (عند الرجوع للحالة العادية) في تلك اللحظة سنزيل fixed عن الحاوي ليعود إلى طبيعته.

الآن سنتجه إلى scrollspy وهي تملك شروطا أكثر ازعاجا، مثل أنّها تعمل مع عناصر bootstrap فحسب لذا سنقوم ببعض التعديل في HTML و CSS أولا.

<nav class="l-sticky-nav">
    <ul class="nav sticky-nav">
        <li class="nav-element">
            <a href="#features-section" class="nav-element--link active">
                <img src="img/equalizer.png" alt="" class="nav-element--image">
            </a>
        </li>
        <li class="nav-element">
            <a href="#testimonials-section" class="nav-element--link">
                <img src="img/id.png" alt="" class="nav-element--image">
            </a>
        </li>
        <li class="nav-element">
            <a href="#analysicts-section" class="nav-element--link">
                <img src="img/globe.png" alt="" class="nav-element--image">
            </a>
        </li>
        <li class="nav-element">
            <a href="#download-section" class="nav-element--link">
                <img src="img/download.png" alt="" class="nav-element--image">
            </a>
        </li>
    </ul><!-- sticky-nav -->
</nav><!-- l-sticky-nav -->

ما قمنا به هنا هو إضافة nav إلى الحاوي الثاني sticky-nav وقمنا بتغييره من div إلى ul ثم قمنا بتغيير العناصر nav-element من div إلى li.

نقوم الآن بتعديل بعض CSS:

.nav-element.active .nav-element--image,
.nav-element:hover .nav-element--image {
    filter: grayscale(0);
    -webkit-filter: grayscale(0);
}

كل ما فعلناه هو أننا غيرنا nav-element–link إلى nav-element فحسب. الآن نستطيع استخدام الإضافة بشكل عادي تماما.

$('body').scrollspy({ target: '.l-sticky-nav' });

سوف نقوم بإضافتها إلى body هذا لأن عناصرنا (أقسام الصفحة) تقع مباشرة داخل وسم body وحددنا الهدف l-sticky-nav حيث يكون هو من يتم تعديل القيم فيه.

تاليا سنقوم بإضافة carousel إلى الشهادات:

if($('.testimonials-carousel').children().length > 1) {
    $('.testimonials-carousel').owlCarousel({
        loop: true,
        nav: false,
        dots: true,
        rtl: true,
        items: 1
    }) 
}  

ما نقوم به هو أننا نتفقد إن كان هنالك أكثر من شهادة، لأن OwlCarousel لا يعمل إذا كان هناك عنصر واحد في الصفحة (مشكلة على الأرجح سيتم إزالتها في النسخة الرسمية) 
بعد ذلك نقوم بتفعيله، وإعطائه بعض الخيارات، منها أننا لا نريد روابط للتقليب إلى اليمين واليسار، وأننا نريد نقاط التصفح فحسب، وأننا نريده أن يعمل من اليمين إلى اليسار (هذا السبب الوحيد الذي يدفعنا إلى استخدام النسخة 2.0 على النسخة الرسمية 1.3 وهو بسبب دعم اليمين إلى اليسار) وأننا نريد عنصرا واحدا كل مرة.

هذا هو كامل شِفرة Javascript، لقد كانت بسيطة حقا:

$(document).ready(function(){
    $('.sticky-nav').affix({
      offset: {
        top: $('.l-header').outerHeight()
      }
    });
    $('.sticky-nav').on('affix.bs.affix', function () {
        $('.l-sticky-nav').addClass('fixed');
    });
    $('.sticky-nav').on('affixed-top.bs.affix', function () {
        $('.l-sticky-nav').removeClass('fixed');
    });

    $('body').scrollspy({ target: '.l-sticky-nav' });

    if($('.testimonials-carousel').children().length > 1) {
        $('.testimonials-carousel').owlCarousel({
            loop: true,
            nav: false,
            dots: true,
            rtl: true,
            items: 1
        }) 
    }    
})

الخاتمة

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



2 اشخاص أعجبوا بهذا


تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن