कोडलैब: स्टोरीज़ से जुड़ा कॉम्पोनेंट बनाना

यह कोडलैब आपको Instagram Stories जैसा अनुभव देने का तरीका सिखाता है वेब पर. आगे चलकर हम कॉम्पोनेंट बनाते हैं. एचटीएमएल से शुरू करते हुए सीएसएस से शुरू करते हैं. फिर JavaScript.

मेरी ब्लॉग पोस्ट स्टोरीज़ कॉम्पोनेंट बनाना देखें इस कॉम्पोनेंट को बनाने के दौरान किए गए सुधारों के बारे में जानने के लिए.

सेटअप

  1. प्रोजेक्ट में बदलाव करने के लिए, बदलाव करने के लिए रीमिक्स करें पर क्लिक करें.
  2. app/index.html खोलें.

एचटीएमएल

मैं हमेशा सिमेंटिक एचटीएमएल इस्तेमाल करना चाहता/चाहती हूं. हर दोस्त की बहुत सी कहानियां हो सकती हैं. इसलिए, मुझे लगा कि हर दोस्त के लिए <section> एलिमेंट और हर स्टोरी के लिए <article> एलिमेंट. चलिए, फिर से शुरू करते हैं. सबसे पहले, हमें अपने स्टोरी कॉम्पोनेंट.

अपने <body> में <div> एलिमेंट जोड़ें:

<div class="stories">

</div>

दोस्तों को दिखाने के लिए कुछ <section> एलिमेंट जोड़ें:

<div class="stories">
  <section class="user"></section>
  <section class="user"></section>
  <section class="user"></section>
  <section class="user"></section>
</div>

खबरों को दिखाने के लिए कुछ <article> एलिमेंट जोड़ें:

<div class="stories">
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/480/840);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/480/841);"></article>
  </section>
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/481/840);"></article>
  </section>
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/481/841);"></article>
  </section>
  <section class="user">
    <article class="story" style="--bg: url(https://picsum.photos/482/840);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/482/843);"></article>
    <article class="story" style="--bg: url(https://picsum.photos/482/844);"></article>
  </section>
</div>
  • हम कहानियों के प्रोटोटाइप बनाने के लिए, इमेज सेवा (picsum.com) का इस्तेमाल कर रहे हैं.
  • हर <article> पर style एट्रिब्यूट, लोड होने वाले प्लेसहोल्डर का हिस्सा है तकनीक के बारे में ज़्यादा जानकारी मिलेगी, जिसके बारे में आप अगले सेक्शन में ज़्यादा जानेंगे.

सीएसएस

हमारा कॉन्टेंट स्टाइल के लिए तैयार है. आओ इन हड्डियों को कुछ ऐसा बनाएं जो लोग के साथ इंटरैक्ट करना है. आज हम मोबाइल डिवाइसों के लिए काम करेंगे.

.stories

हमें अपने <div class="stories"> कंटेनर के लिए, हॉरिज़ॉन्टल स्क्रोलिंग कंटेनर चाहिए. ऐसा करने के लिए, हम ये काम कर सकते हैं:

  • कंटेनर को ग्रिड बनाना
  • पंक्ति ट्रैक को भरने के लिए हर चाइल्ड को सेट करना
  • हर चाइल्ड डिवाइस की चौड़ाई, मोबाइल डिवाइस के व्यूपोर्ट की चौड़ाई के बराबर होती है

ग्रिड, पिछले 100vw-चौड़ा कॉलम की दाईं ओर नए कॉलम जोड़ना जारी रखेगा पहला, जब तक कि इसमें आपके मार्कअप में सभी एचटीएमएल एलिमेंट शामिल न कर दिए जाएं.

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

app/css/index.css के सबसे नीचे, यह सीएसएस जोड़ें:

.stories {
  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;
}

अब हमारे पास ऐसा कॉन्टेंट है जो व्यूपोर्ट से बाहर भी उपलब्ध है, इसलिए आपको यह बताना होगा कि कंटेनर में उसे कैसे मैनेज करना है. अपने .stories नियमों-सेट में कोड की हाइलाइट की गई पंक्तियां जोड़ें:

.stories {
  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}

हम हॉरिज़ॉन्टल स्क्रोलिंग चाहते हैं, इसलिए हम overflow-x को auto. जब उपयोगकर्ता स्क्रोल करता है, तब हम चाहते हैं कि कॉम्पोनेंट हमें अगली स्टोरी पर धीरे से चले, इसलिए, हम scroll-snap-type: x mandatory का इस्तेमाल करेंगे. इस बारे में और पढ़ें सीएसएस स्क्रोल करने पर स्नैप पॉइंट में सीएसएस और ओवरस्क्रोल-व्यवहार मेरी ब्लॉग पोस्ट के सेक्शन.

स्क्रोल स्नैप करने के लिए पैरंट कंटेनर और बच्चे, दोनों को सहमत होना ज़रूरी है. चलिए, अब इसे समझते हैं. app/css/index.css के निचले हिस्से में यह कोड जोड़ें:

.user {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

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

इससे आप अपने दोस्तों के बीच स्क्रोल कर पाएंगे, लेकिन हमें अब भी समस्या आ रही है जिस समस्या को हल करना हो.

.user

आइए, .user सेक्शन में एक ऐसा लेआउट बनाएं जिसमें उन चाइल्ड स्टोरी को शामिल किया गया हो एलिमेंट जोड़े जा सकते हैं. हम इसे हल करने के लिए, स्टैकिंग की एक आसान ट्रिक इस्तेमाल करेंगे. दरअसल, हम 1x1 ग्रिड बना रहे हैं, जहां लाइन और कॉलम की ग्रिड एक जैसी होती है [story] का उपनाम और हर स्टोरी ग्रिड आइटम, उस स्पेस को ऐक्सेस करने की कोशिश करेगा, जिससे एक स्टैक बन जाता है.

हाइलाइट किए गए कोड को अपने .user नियमसेट में जोड़ें:

.user {
  scroll-snap-align: start;
  scroll-snap-stop: always;
  display: grid;
  grid: [story] 1fr / [story] 1fr;
}

app/css/index.css के निचले हिस्से में ये नियम-सेट जोड़ें:

.story {
  grid-area: story;
}

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

.story

अब हमें बस स्टोरी के आइटम का स्टाइल दिखाना है.

पहले हमने बताया था कि हर <article> एलिमेंट पर style एट्रिब्यूट, प्लेसहोल्डर लोड करने की तकनीक:

<article class="story" style="--bg: url(https://picsum.photos/480/840);"></article>

हम सीएसएस की background-image प्रॉपर्टी का इस्तेमाल करेंगे. इससे हमें यह तय करने में मदद मिलती है कि बैकग्राउंड की एक से ज़्यादा इमेज होनी चाहिए. हम उन्हें एक क्रम में रख सकते हैं ताकि हमारा उपयोगकर्ता चित्र शीर्ष पर है और लोड होने पर अपने आप दिखाई देगा. यहां की यात्रा पर हूं इसे चालू करते हैं, तो हम अपने इमेज यूआरएल को कस्टम प्रॉपर्टी (--bg) में रखेंगे और उसका इस्तेमाल करेंगे हमारे सीएसएस में, लोडिंग प्लेसहोल्डर के साथ लेयर करें.

सबसे पहले, ग्रेडिएंट को बैकग्राउंड इमेज से बदलने के लिए .story नियमसेट को अपडेट करते हैं लोड होने के बाद. हाइलाइट किए गए कोड को अपने .story नियमसेट में जोड़ें:

.story {
  grid-area: story;

  background-size: cover;
  background-image:
    var(--bg),
    linear-gradient(to top, lch(98 0 0), lch(90 0 0));
}

background-size को cover पर सेट करने से, यह पक्का होता है कि व्यूपोर्ट क्योंकि हमारी इमेज इसे भर देगी. बैकग्राउंड के लिए दो इमेज तय की जा रही हैं इससे हमें लोडिंग टूंबस्टोन नाम की एक अच्छी सीएसएस वेब ट्रिक पाने में मदद मिलती है:

  • बैकग्राउंड इमेज 1 (var(--bg)) वह यूआरएल है जिसे हमने एचटीएमएल में इनलाइन तरीके से पास किया है
  • बैकग्राउंड की इमेज 2 (linear-gradient(to top, lch(98 0 0), lch(90 0 0)) एक ग्रेडिएंट है यूआरएल लोड होने के दौरान दिखाने के लिए

इमेज डाउनलोड होने के बाद, सीएसएस ग्रेडिएंट को अपने-आप इमेज से बदल देगा.

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

.story {
  grid-area: story;

  background-size: cover;
  background-image:
    var(--bg),
    linear-gradient(to top, lch(98 0 0), lch(90 0 0));

  user-select: none;
  touch-action: manipulation;
}
  • user-select: none, उपयोगकर्ताओं को गलती से टेक्स्ट चुनने से रोकता है
  • touch-action: manipulation ब्राउज़र को यह निर्देश देता है कि ये इंटरैक्शन को टच इवेंट के रूप में माना जाना चाहिए, जिससे ब्राउज़र तय करें कि आप किसी URL पर क्लिक कर रहे हैं या नहीं

अब कहानी के बीच ट्रांज़िशन को ऐनिमेट करने के लिए, छोटी सीएसएस जोड़ें. जोड़ें आपके .story नियमसेट पर हाइलाइट किया गया कोड:

.story {
  grid-area: story;

  background-size: cover;
  background-image:
    var(--bg),
    linear-gradient(to top, lch(98 0 0), lch(90 0 0));

  user-select: none;
  touch-action: manipulation;

  transition: opacity .3s cubic-bezier(0.4, 0.0, 1, 1);

  &.seen {
    opacity: 0;
    pointer-events: none;
  }
}

.seen क्लास को उस कहानी में जोड़ दिया जाएगा जिसके लिए एग्ज़िट करना ज़रूरी है. मुझे कस्टम ईज़िंग फ़ंक्शन (cubic-bezier(0.4, 0.0, 1,1)) मिला Material Design के ईज़िंग से लिया गया है गाइड (नीचे की ओर स्क्रोल करके एक्सर्लीरेटेड ईज़िंग सेक्शन पर जाएं).

अगर आपने अच्छे से देखा होगा, तो शायद आपने pointer-events: none पर ध्यान दिया होगा लॉन्च करने की तैयारी कर रहे हैं. मेरे लिए, सिर्फ़ यही एक विकल्प है समस्या हल नहीं हुई है. हमें इसकी ज़रूरत है, क्योंकि .seen.story एलिमेंट में सबसे ऊपर होगी और उस पर टैप मिलेंगे, भले ही यह न दिख रहा हो. इसके लिए, pointer-events से none, हम कांच की कहानी को एक खिड़की में बदल देते हैं और कोई ज़्यादा उपयोगकर्ता इंटरैक्शन कर सकते हैं. ट्रेड ऑफ़ करना भी बहुत बुरा नहीं है, यहां मैनेज करना ज़्यादा मुश्किल भी नहीं है हमारी सीएसएस में तुरंत दिखने लगेगा. हम z-index को एक साथ नहीं ला रहे हैं. मुझे यह अच्छा लग रहा है अब भी.

JavaScript

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

सेटअप

सबसे पहले, चलिए ज़्यादा से ज़्यादा जानकारी का हिसाब लगाते हैं और उसे सेव करते हैं. app/js/index.js में यह कोड जोड़ें:

const stories = document.querySelector('.stories')
const median = stories.offsetLeft + (stories.clientWidth / 2)

JavaScript की हमारी पहली पंक्ति हमारे प्राथमिक HTML का रेफ़रंस लेकर उसे स्टोर करती है एलिमेंट रूट को टारगेट कर सकता है. अगली लाइन यह पता लगाती है कि हमारे एलिमेंट का बीच कहां है, इसलिए हम तय कर सकता है कि टैप को आगे बढ़ाना है या पीछे.

स्थिति

इसके बाद हम एक छोटा सा ऑब्जेक्ट बनाते हैं, जिसमें कुछ राज्य हमारे लॉजिक के हिसाब से काम का होता है. इसमें मामला, तो हमारी केवल वर्तमान कहानी में दिलचस्पी है. हम अपने एचटीएमएल मार्कअप में, पहले दोस्त और उसकी सबसे हाल की कहानी को ढूंढकर, उसे ऐक्सेस करें. हाइलाइट किया गया कोड जोड़ें आपके app/js/index.js में:

const stories = document.querySelector('.stories')
const median = stories.offsetLeft + (stories.clientWidth / 2)

const state = {
  current_story: stories.firstElementChild.lastElementChild
}

लिसनर

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

चूहा

आइए, अपने स्टोरीज़ कंटेनर पर 'click' इवेंट के बारे में सुनकर शुरुआत करते हैं. हाइलाइट किए गए कोड को app/js/index.js में जोड़ें:

const stories = document.querySelector('.stories')
const median = stories.offsetLeft + (stories.clientWidth / 2)

const state = {
  current_story: stories.firstElementChild.lastElementChild
}

stories.addEventListener('click', e => {
  if (e.target.nodeName !== 'ARTICLE')
    return

  navigateStories(
    e.clientX > median
      ? 'next'
      : 'prev')
})

अगर कोई क्लिक होता है और वह <article> एलिमेंट पर नहीं होता है, तो हम कोई कार्रवाई नहीं करते हैं. अगर यह कोई लेख है, तो हम माउस या उंगली की हॉरिज़ॉन्टल पोज़िशन clientX. हमने अभी तक navigateStories लागू नहीं किया है, लेकिन तर्क यह है कि यह बताता है कि हमें किस दिशा में जाना है. अगर वह उपयोगकर्ता पोज़िशन हम जानते हैं कि हमें next तक नेविगेट करना होगा, नहीं तो यह prev (पिछला).

कीबोर्ड

अब, कीबोर्ड को दबाने के बारे में जानते हैं. डाउन ऐरो को दबाकर रखने पर, हम next के लिए. अगर यह अप ऐरो है, तो हम prev पर जाते हैं.

हाइलाइट किए गए कोड को app/js/index.js में जोड़ें:

const stories = document.querySelector('.stories')
const median = stories.offsetLeft + (stories.clientWidth / 2)

const state = {
  current_story: stories.firstElementChild.lastElementChild
}

stories.addEventListener('click', e => {
  if (e.target.nodeName !== 'ARTICLE')
    return

  navigateStories(
    e.clientX > median
      ? 'next'
      : 'prev')
})

document.addEventListener('keydown', ({key}) => {
  if (key !== 'ArrowDown' || key !== 'ArrowUp')
    navigateStories(
      key === 'ArrowDown'
        ? 'next'
        : 'prev')
})

स्टोरीज़ नेविगेशन

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

सबसे पहले, हम कुछ ऐसे सिलेक्टर छिपा देते हैं जिनसे हमें यह तय करने में मदद मिलती है कि दोस्त बनाने या कोई कहानी दिखाने/छिपाने के लिए. चूंकि HTML वह स्थान है जहां हम काम करते हैं, इसलिए हम दोस्तों (उपयोगकर्ताओं) या कहानियों (कहानी) की मौजूदगी के बारे में क्वेरी करना.

ये वैरिएबल हमें इस तरह के सवालों के जवाब देने में मदद करेंगे: "अगर स्टोरी x के आधार पर, "आगे बढ़ें" शामिल है, तो का मतलब है, इसी दोस्त की किसी दूसरी कहानी पर जाना या किसी दूसरे दोस्त की ओर ले जाना?" पेड़ की मदद से मैंने यह किया हमने माता-पिता और बच्चों तक पहुंचने के लिए हमारी बनाई संरचना तैयार की है.

app/js/index.js के निचले हिस्से में यह कोड जोड़ें:

const navigateStories = direction => {
  const story = state.current_story
  const lastItemInUserStory = story.parentNode.firstElementChild
  const firstItemInUserStory = story.parentNode.lastElementChild
  const hasNextUserStory = story.parentElement.nextElementSibling
  const hasPrevUserStory = story.parentElement.previousElementSibling
}

आम तौर पर, आम भाषा के हिसाब से बनाया गया हमारा बिज़नेस लॉजिक इस तरह का लक्ष्य होता है:

  • तय करें कि टैप को कैसे हैंडल करना है
    • अगर कोई अगली/पिछली कहानी है: वह कहानी दिखाएँ:
    • अगर यह दोस्त की आखिरी/पहली कहानी है, तो नया दोस्त दिखाएं
    • अगर उस दिशा में कोई कहानी नहीं है: कुछ न करें
  • नई कहानी को state में रखें

हाइलाइट किए गए कोड को अपने navigateStories फ़ंक्शन में जोड़ें:

const navigateStories = direction => {
  const story = state.current_story
  const lastItemInUserStory = story.parentNode.firstElementChild
  const firstItemInUserStory = story.parentNode.lastElementChild
  const hasNextUserStory = story.parentElement.nextElementSibling
  const hasPrevUserStory = story.parentElement.previousElementSibling

  if (direction === 'next') {
    if (lastItemInUserStory === story && !hasNextUserStory)
      return
    else if (lastItemInUserStory === story && hasNextUserStory) {
      state.current_story = story.parentElement.nextElementSibling.lastElementChild
      story.parentElement.nextElementSibling.scrollIntoView({
        behavior: 'smooth'
      })
    }
    else {
      story.classList.add('seen')
      state.current_story = story.previousElementSibling
    }
  }
  else if(direction === 'prev') {
    if (firstItemInUserStory === story && !hasPrevUserStory)
      return
    else if (firstItemInUserStory === story && hasPrevUserStory) {
      state.current_story = story.parentElement.previousElementSibling.firstElementChild
      story.parentElement.previousElementSibling.scrollIntoView({
        behavior: 'smooth'
      })
    }
    else {
      story.nextElementSibling.classList.remove('seen')
      state.current_story = story.nextElementSibling
    }
  }
}

इसे आज़माएं

  • साइट की झलक देखने के लिए, ऐप्लिकेशन देखें दबाएं. इसके बाद, फ़ुलस्क्रीन फ़ुलस्क्रीन.

नतीजा

यह कॉम्पोनेंट से जुड़ी मेरी ज़रूरतों को पूरा करने वाला है. कॉन्टेंट बनाने के लिए बेझिझक उसे डेटा के साथ ड्राइव करें और सामान्य रूप से उसे अपना बनाएं!