สร้างแถบเลื่อนเต็มหน้าแบบกำหนดเองด้วย CSS และ JavaScript

เผยแพร่แล้ว: 2022-03-11

ฉันทำงานกับเลย์เอาต์เต็มหน้าจอแบบกำหนดเองเป็นประจำทุกวัน โดยปกติ เลย์เอาต์เหล่านี้บ่งบอกถึงการโต้ตอบและแอนิเมชั่นจำนวนมาก ไม่ว่าจะเป็นไทม์ไลน์ที่ซับซ้อนที่กระตุ้นเวลาของการเปลี่ยนหรือชุดเหตุการณ์ที่ผู้ใช้เลื่อนตาม ในกรณีส่วนใหญ่ UI ต้องการมากกว่าแค่การใช้โซลูชันปลั๊กอินที่พร้อมใช้งานทันทีพร้อมการปรับแต่งและการเปลี่ยนแปลงเล็กน้อย . ในทางกลับกัน ฉันเห็นนักพัฒนา JavaScript จำนวนมากมักจะเข้าถึงปลั๊กอิน JS ที่พวกเขาชื่นชอบเพื่อให้งานของพวกเขาง่ายขึ้น แม้ว่างานอาจไม่ต้องการเสียงระฆังและนกหวีดทั้งหมดที่ปลั๊กอินมีให้

ข้อจำกัดความรับผิดชอบ: การใช้ปลั๊กอินตัวใดตัวหนึ่งที่มีอยู่นั้นมีประโยชน์แน่นอน คุณจะได้รับตัวเลือกมากมายที่คุณสามารถใช้เพื่อปรับแต่งความต้องการของคุณโดยไม่ต้องเขียนโค้ดมาก นอกจากนี้ ผู้เขียนปลั๊กอินส่วนใหญ่ปรับโค้ดให้เหมาะสม ทำให้ใช้งานได้ข้ามเบราว์เซอร์และข้ามแพลตฟอร์ม และอื่นๆ แต่ถึงกระนั้น คุณจะได้รับไลบรารีขนาดเต็มรวมอยู่ในโปรเจ็กต์ของคุณสำหรับสิ่งที่แตกต่างกันเพียงหนึ่งหรือสองอย่างที่มีให้ ฉันไม่ได้บอกว่าการใช้ปลั๊กอินของบุคคลที่สามใด ๆ นั้นเป็นสิ่งที่ไม่ดีโดยธรรมชาติ ฉันทำมันทุกวันในโครงการของฉัน เพียงว่าโดยทั่วไปแล้วควรชั่งน้ำหนักข้อดีข้อเสียของแต่ละวิธีตามที่เป็นอยู่ แนวปฏิบัติที่ดีในการเข้ารหัส ในการทำสิ่งของคุณเองด้วยวิธีนี้ ต้องใช้ความรู้และประสบการณ์ในการเขียนโค้ดอีกเล็กน้อยจึงจะรู้ว่าคุณต้องการอะไร แต่สุดท้ายแล้ว คุณควรได้โค้ดชิ้นหนึ่งที่ทำสิ่งเดียวและอีกสิ่งเดียวเท่านั้น คุณต้องการให้

บทความนี้มีวัตถุประสงค์เพื่อแสดงแนวทาง CSS/JS ที่แท้จริงในการพัฒนาเลย์เอาต์ตัวเลื่อนที่ทริกเกอร์การเลื่อนแบบเต็มหน้าจอพร้อมแอนิเมชั่นเนื้อหาที่กำหนดเอง ในแนวทางที่ลดขนาดลงนี้ ฉันจะพูดถึงโครงสร้าง HTML พื้นฐานที่คุณคาดว่าจะได้รับจากส่วนหลังของ CMS เทคนิคการจัดวาง CSS (SCSS) สมัยใหม่ และการเข้ารหัส JavaScript วานิลลาเพื่อการโต้ตอบเต็มรูปแบบ ด้วยแนวคิดที่ไร้เหตุผล แนวคิดนี้สามารถขยายไปยังปลั๊กอินขนาดใหญ่ได้อย่างง่ายดาย และ/หรือใช้ในแอปพลิเคชันต่างๆ โดยไม่ต้องพึ่งพาแกนหลัก

การออกแบบที่เราจะสร้างคืองานแสดงผลงานสถาปนิกที่เรียบง่ายพร้อมรูปภาพและชื่อที่โดดเด่นของแต่ละโครงการ แถบเลื่อนที่สมบูรณ์พร้อมภาพเคลื่อนไหวจะมีลักษณะดังนี้:

แถบเลื่อนตัวอย่างผลงานสถาปนิก

คุณสามารถตรวจสอบการสาธิตได้ที่นี่ และสามารถเข้าถึง Github repo ของฉันสำหรับรายละเอียดเพิ่มเติม

ภาพรวม HTML

นี่คือ HTML พื้นฐานที่เราจะใช้งาน:

 <div> <div class="mask"> <!-- Textual logo will go here --> </div> <div> <div class="slides"> <!-- Featured image slides will go here --> </div> <div class="slides mask"> <!-- Slide titles will go here --> </div> </div> <div> <!-- Static info on the right --> </div> <nav> <!-- Current slide indicator --> </nav> </div>

div ที่มีรหัสของ hero-slider คือเจ้าของหลักของเรา ภายในเค้าโครงแบ่งออกเป็นส่วนต่างๆ:

  • โลโก้ (ส่วนคงที่)
  • สไลด์โชว์ที่เราจะทำกันเป็นส่วนใหญ่
  • ข้อมูล (ส่วนคงที่)
  • Slider nav ซึ่งจะระบุสไลด์ที่ใช้งานอยู่ในปัจจุบันรวมถึงจำนวนสไลด์ทั้งหมด

มาเน้นที่ส่วนสไลด์โชว์กันเพราะนั่นคือจุดสนใจของเราในบทความนี้ ที่นี่เรามีสองส่วน คือ main และ aux หลักคือ div ที่มีรูปภาพเด่นในขณะที่ aux เก็บชื่อรูปภาพ โครงสร้างของแต่ละสไลด์ภายในตัวยึดทั้งสองนี้ค่อนข้างเรียบง่าย เรามีสไลด์รูปภาพด้านในตัวยึดหลัก:

 <div class="slide" data-index="0"> <div class="abs-mask"> <div class="slide-image"> </div> </div> </div>

แอตทริบิวต์ข้อมูลดัชนีคือสิ่งที่เราจะใช้เพื่อติดตามว่าเราอยู่ที่ใดในสไลด์โชว์ div abs-mask ที่เราจะใช้เพื่อสร้างเอฟเฟกต์การเปลี่ยนภาพที่น่าสนใจ และ div รูปภาพสไลด์มีรูปภาพเด่นเฉพาะ รูปภาพจะแสดงในบรรทัดราวกับว่ามาจาก CMS โดยตรงและกำหนดโดยผู้ใช้ปลายทาง

ในทำนองเดียวกัน ชื่อเรื่องจะเลื่อนอยู่ในที่ยึด aux:

 <h2 class="slide-title slide" data-index="0"><a href="#">#64 Paradigm</a></h2>

ชื่อสไลด์แต่ละชื่อเป็นแท็ก H2 ที่มีแอตทริบิวต์ข้อมูลที่เกี่ยวข้องและลิงก์เพื่อนำไปสู่หน้าเดียวของโครงการนั้น

HTML ที่เหลือของเราค่อนข้างตรงไปตรงมาเช่นกัน เรามีโลโก้ที่ด้านบน ข้อมูลคงที่ซึ่งบอกผู้ใช้ว่าพวกเขาอยู่ในหน้าใด คำอธิบายบางส่วน และตัวเลื่อนปัจจุบัน/ตัวบ่งชี้ทั้งหมด

ภาพรวม CSS

รหัส CSS ต้นทางเขียนด้วย SCSS ซึ่งเป็นตัวประมวลผลล่วงหน้าของ CSS ซึ่งรวบรวมเป็น CSS ปกติซึ่งเบราว์เซอร์สามารถตีความได้ SCSS ช่วยให้คุณได้รับประโยชน์จากการใช้ตัวแปร การเลือกแบบซ้อน มิกซ์อิน และสิ่งที่น่าสนใจอื่นๆ แต่ต้องคอมไพล์เป็น CSS เพื่อให้เบราว์เซอร์อ่านโค้ดตามที่ควรจะเป็น สำหรับจุดประสงค์ของบทช่วยสอนนี้ ฉันได้ใช้ Scout-App เพื่อจัดการกับการคอมไพล์ เนื่องจากฉันต้องการให้มีเครื่องมือน้อยที่สุด

ฉันใช้ flexbox เพื่อจัดการเลย์เอาต์พื้นฐานแบบเคียงข้างกัน แนวคิดคือการมีสไลด์โชว์อยู่ด้านหนึ่งและส่วนข้อมูลอีกด้านหนึ่ง

 #hero-slider { position: relative; height: 100vh; display: flex; background: $dark-color; } #slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #info { position: relative; flex: 1 1 $side-width; padding: $offset; background-color: #fff; }

มาดูการวางตำแหน่งกัน และอีกครั้ง ให้เน้นที่ส่วนสไลด์โชว์:

 #slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #slides-main { @extend %abs; &:after { content: ''; @extend %abs; background-color: rgba(0, 0, 0, .25); z-index: 100; } .slide-image { @extend %abs; background-position: center; background-size: cover; z-index: -1; } } #slides-aux { position: relative; top: 1.25rem; width: 100%; .slide-title { position: absolute; z-index: 300; font-size: 4vw; font-weight: 700; line-height: 1.3; @include outlined(#fff); } }

ฉันได้ตั้งค่าตัวเลื่อนหลักให้อยู่ในตำแหน่งที่แน่นอน และให้ภาพพื้นหลังขยายพื้นที่ทั้งหมดโดยใช้คุณสมบัติ background-size: cover เพื่อให้แตกต่างจากชื่อสไลด์มากขึ้น ฉันได้ตั้งค่าองค์ประกอบหลอกแบบสัมบูรณ์ซึ่งทำหน้าที่เป็นภาพซ้อนทับ ตัวเลื่อน aux ที่มีชื่อสไลด์จะอยู่ที่ด้านล่างของหน้าจอและด้านบนของรูปภาพ

เนื่องจากจะมองเห็นได้ครั้งละหนึ่งสไลด์เท่านั้น ฉันจึงตั้งค่าให้แต่ละชื่อเป็นแบบสัมบูรณ์เช่นกัน และให้คำนวณขนาดผู้ถือผ่าน JS เพื่อให้แน่ใจว่าไม่มีการตัดทอน แต่ให้เพิ่มเติมเกี่ยวกับเรื่องนี้ในหัวข้อต่อไปของเรา คุณสามารถดูการใช้คุณลักษณะ SCSS ที่เรียกว่าการขยายได้ที่นี่:

 %abs { position: absolute; top: 0; left: 0; height: 100%; width: 100%; }

เนื่องจากฉันใช้การจัดตำแหน่งสัมบูรณ์เป็นจำนวนมาก ฉันจึงดึง CSS นี้ไปยังส่วนขยายเพื่อให้ใช้งานได้ง่ายในตัวเลือกต่างๆ นอกจากนี้ ฉันยังได้สร้างมิกซ์อินที่เรียกว่า "โครงร่าง" เพื่อจัดเตรียมแนวทางแบบแห้งเมื่อกำหนดสไตล์ของชื่อและชื่อตัวเลื่อนหลัก

 @mixin outlined($color: $dark-color, $size: 1px) { color: transparent; -webkit-text-stroke: $size $color; }

สำหรับส่วนที่คงที่ของเลย์เอาต์นี้ มันไม่ได้ซับซ้อนอะไรเกี่ยวกับมัน แต่ที่นี่คุณสามารถเห็นวิธีการที่น่าสนใจเมื่อวางตำแหน่งข้อความที่ต้องอยู่บนแกน Y แทนที่จะเป็นโฟลว์ปกติ:

 .slider-title-wrapper { position: absolute; top: $offset; left: calc(100% - #{$offset}); transform-origin: 0% 0%; transform: rotate(90deg); @include outlined; }

ฉันต้องการดึงความสนใจของคุณไปที่คุณสมบัติ transform-origin เนื่องจากฉันพบว่ามันใช้งานไม่ได้มากสำหรับเลย์เอาต์ประเภทนี้ วิธีจัดตำแหน่งองค์ประกอบนี้คือจุดยึดอยู่ที่มุมซ้ายบนขององค์ประกอบ ตั้งค่าจุดหมุนและให้ข้อความไหลจากจุดนั้นลงด้านล่างอย่างต่อเนื่องโดยไม่มีปัญหากับขนาดหน้าจอต่างๆ

มาดูส่วน CSS ที่น่าสนใจกันดีกว่า - แอนิเมชั่นการโหลดเริ่มต้น:

โหลดภาพเคลื่อนไหวสำหรับตัวเลื่อน

โดยปกติ พฤติกรรมแอนิเมชั่นที่ซิงค์ประเภทนี้ทำได้โดยใช้ไลบรารี - ตัวอย่างเช่น GSAP เป็นหนึ่งในสิ่งที่ดีที่สุด ให้ความสามารถในการเรนเดอร์ที่ยอดเยี่ยม ใช้งานง่าย และมีฟังก์ชันไทม์ไลน์ที่ช่วยให้นักพัฒนาสามารถเชื่อมโยงองค์ประกอบทางโปรแกรม การเปลี่ยนแปลงเข้าสู่กันและกัน

อย่างไรก็ตาม เนื่องจากนี่เป็นเพียงตัวอย่าง CSS/JS ล้วนๆ ฉันจึงตัดสินใจใช้แบบพื้นฐานที่นี่ ดังนั้นแต่ละองค์ประกอบจะถูกตั้งค่าเป็นตำแหน่งเริ่มต้นตามค่าเริ่มต้น ไม่ว่าจะซ่อนไว้โดยการแปลงหรือความทึบ และแสดงเมื่อโหลดตัวเลื่อนซึ่งถูกทริกเกอร์โดย JS ของเรา คุณสมบัติการเปลี่ยนภาพทั้งหมดได้รับการปรับแต่งด้วยตนเองเพื่อให้แน่ใจว่ามีการไหลที่เป็นธรรมชาติและน่าสนใจ โดยการเปลี่ยนแปลงแต่ละครั้งจะดำเนินต่อไปสู่การเปลี่ยนแปลงอื่นโดยให้ประสบการณ์ภาพที่น่าพึงพอใจ

 #logo:after { transform: scaleY(0); transform-origin: 50% 0; transition: transform .35s $easing; } .logo-text { display: block; transform: translate3d(120%, 0, 0); opacity: 0; transition: transform .8s .2s, opacity .5s .2s; } .current, .sep:before { opacity: 0; transition: opacity .4s 1.3s; } #info { transform: translate3d(100%, 0, 0); transition: transform 1s $easing .6s; } .line { transform-origin: 0% 0; transform: scaleX(0); transition: transform .7s $easing 1s; } .slider-title { overflow: hidden; >span { display: block; transform: translate3d(0, -100%, 0); transition: transform .5s 1.5s; } }

หากมีสิ่งหนึ่งที่ผมอยากให้คุณเห็นตรงนี้ ก็คือการใช้คุณสมบัติการท transform ส์ฟอร์ม เมื่อย้ายองค์ประกอบ HTML ไม่ว่าจะเป็นทรานสิชั่นหรือแอนิเมชั่น ขอแนะนำให้ใช้คุณสมบัติการ transform ฉันเห็นคนจำนวนมากที่มักจะใช้ขอบหรือช่องว่างภายใน หรือแม้แต่ออฟเซ็ต บน ซ้าย ฯลฯ ซึ่งไม่ได้ผลลัพธ์ที่เพียงพอเมื่อพูดถึงการเรนเดอร์

เพื่อให้เข้าใจในเชิงลึกยิ่งขึ้นเกี่ยวกับวิธีใช้ CSS เมื่อเพิ่มพฤติกรรมเชิงโต้ตอบ ฉันไม่สามารถแนะนำบทความต่อไปนี้ได้เพียงพอ

เป็นหน้าที่ของ Paul Lewis วิศวกร Chrome และครอบคลุมทุกอย่างที่เราควรรู้เกี่ยวกับการเรนเดอร์พิกเซลในเว็บ ไม่ว่าจะเป็น CSS หรือ JS

ภาพรวม JavaScript และลอจิกตัวเลื่อน

ไฟล์ JavaScript แบ่งออกเป็นสองฟังก์ชันที่แตกต่างกัน

ฟังก์ชัน heroSlider ซึ่งดูแลฟังก์ชันทั้งหมดที่เราต้องการที่นี่ และฟังก์ชัน utils ที่ฉันได้เพิ่มฟังก์ชันยูทิลิตี้ที่นำกลับมาใช้ใหม่ได้หลายฟังก์ชัน ฉันได้แสดงความคิดเห็นแต่ละฟังก์ชันยูทิลิตี้เหล่านี้เพื่อให้บริบทหากคุณต้องการนำกลับมาใช้ใหม่ในโครงการของคุณ

ฟังก์ชั่นหลักถูกเข้ารหัสในลักษณะที่มีสองสาขา: init และ resize สาขาเหล่านี้สามารถใช้ได้ผ่านการกลับมาของฟังก์ชันหลัก และเรียกใช้เมื่อจำเป็น init คือการเริ่มต้นของฟังก์ชันหลักและทริกเกอร์ในเหตุการณ์การโหลดหน้าต่าง ในทำนองเดียวกัน สาขาการปรับขนาดจะถูกทริกเกอร์เมื่อปรับขนาดหน้าต่าง วัตถุประสงค์เพียงอย่างเดียวของฟังก์ชันการปรับขนาดคือการคำนวณขนาดตัวเลื่อนของชื่อเรื่องใหม่เมื่อปรับขนาดหน้าต่าง เนื่องจากขนาดแบบอักษรของชื่อเรื่องอาจแตกต่างกันไป

ในฟังก์ชัน heroSlider ฉันได้จัดเตรียมวัตถุตัวเลื่อนซึ่งมีข้อมูลและตัวเลือกทั้งหมดที่เราต้องการ:

 const slider = { hero: document.querySelector('#hero-slider'), main: document.querySelector('#slides-main'), aux: document.querySelector('#slides-aux'), current: document.querySelector('#slider-nav .current'), handle: null, idle: true, activeIndex: -1, interval: 3500 };

แนวทางนี้สามารถปรับเปลี่ยนได้อย่างง่ายดายหากคุณใช้ React เช่นการจัดเก็บข้อมูลในสถานะหรือใช้ hooks ที่เพิ่มใหม่ เพื่อให้ตรงประเด็น มาดูกันว่าคู่คีย์-ค่าแต่ละคู่หมายถึงอะไร:

  • คุณสมบัติสี่ประการแรกคือการอ้างอิง HTML ไปยังองค์ประกอบ DOM ที่เราจะจัดการ
  • คุณสมบัติ handle จะถูกใช้เพื่อเริ่มและหยุดการทำงานอัตโนมัติ
  • คุณสมบัติ idle เป็นแฟล็กที่จะป้องกันไม่ให้ผู้ใช้บังคับเลื่อนในขณะที่สไลด์อยู่ระหว่างการเปลี่ยน
  • activeIndex จะช่วยให้เราติดตามสไลด์ที่ใช้งานอยู่ในปัจจุบัน
  • interval หมายถึงช่วงเวลาเล่นอัตโนมัติของตัวเลื่อน

เมื่อเริ่มต้นตัวเลื่อน เราเรียกใช้สองฟังก์ชัน:

 setHeight(slider.aux, slider.aux.querySelectorAll('.slide-title')); loadingAnimation();

ฟังก์ชัน setHeight เอื้อมมือไปที่ฟังก์ชันยูทิลิตี้เพื่อกำหนดความสูงของตัวเลื่อน aux ของเราตามขนาดชื่อสูงสุด วิธีนี้ช่วยให้มั่นใจได้ว่ามีการปรับขนาดที่เพียงพอและจะไม่มีการตัดชื่อสไลด์แม้ว่าเนื้อหาจะเหลือสองบรรทัดก็ตาม

ฟังก์ชัน LoadAnimation เพิ่มคลาส CSS ให้กับองค์ประกอบที่มีการเปลี่ยน CSS ของอินโทร:

 const loadingAnimation = function () { slider.hero.classList.add('ready'); slider.current.addEventListener('transitionend', start, { once: true }); }

เนื่องจากตัวบ่งชี้ตัวเลื่อนของเราเป็นองค์ประกอบสุดท้ายในไทม์ไลน์การเปลี่ยนแปลง CSS เราจึงรอให้การเปลี่ยนแปลงสิ้นสุดและเรียกใช้ฟังก์ชันเริ่มต้น โดยการระบุพารามิเตอร์เพิ่มเติมเป็นอ็อบเจ็กต์ เรารับรองว่าสิ่งนี้จะถูกทริกเกอร์เพียงครั้งเดียว

มาดูฟังก์ชันเริ่มต้นกัน:

 const start = function () { autoplay(true); wheelControl(); window.innerWidth <= 1024 && touchControl(); slider.aux.addEventListener('transitionend', loaded, { once: true }); }

ดังนั้นเมื่อเค้าโครงเสร็จสิ้น การเปลี่ยนแปลงเริ่มต้นจะถูกทริกเกอร์โดยฟังก์ชัน loadingAnimation และฟังก์ชันเริ่มต้นจะเข้ามาแทนที่ จากนั้นจะเรียกใช้ฟังก์ชันการเล่นอัตโนมัติ เปิดใช้งานการควบคุมวงล้อ กำหนดว่าเราอยู่บนอุปกรณ์สัมผัสหรือเดสก์ท็อป และรอให้การเลื่อนชื่อเรื่องเลื่อนในขั้นแรกเพื่อเพิ่มคลาส CSS ที่เหมาะสม

เล่นอัตโนมัติ

หนึ่งในฟีเจอร์หลักในเลย์เอาต์นี้คือฟีเจอร์เล่นอัตโนมัติ มาดูฟังก์ชันที่เกี่ยวข้องกัน:

 const autoplay = function (initial) { slider.autoplay = true; slider.items = slider.hero.querySelectorAll('[data-index]'); slider.total = slider.items.length / 2; const loop = () => changeSlide('next'); initial && requestAnimationFrame(loop); slider.handle = utils().requestInterval(loop, slider.interval); }

ขั้นแรก เราตั้งค่าสถานะการเล่นอัตโนมัติเป็นจริง โดยระบุว่าตัวเลื่อนอยู่ในโหมดเล่นอัตโนมัติ แฟล็กนี้มีประโยชน์ในการพิจารณาว่าจะทริกเกอร์การเล่นอัตโนมัติอีกครั้งหลังจากที่ผู้ใช้โต้ตอบกับตัวเลื่อนหรือไม่ จากนั้นเราจะอ้างอิงรายการตัวเลื่อนทั้งหมด (สไลด์) เนื่องจากเราจะเปลี่ยนคลาสที่ใช้งานอยู่และคำนวณการวนซ้ำทั้งหมดที่ตัวเลื่อนจะมีโดยการเพิ่มรายการทั้งหมดและหารด้วยสองเนื่องจากเรามีเค้าโครงตัวเลื่อนที่ซิงค์กันสองแบบ (หลักและ aux) แต่มี "ตัวเลื่อน" ตัวเดียวเท่านั้นซึ่งเปลี่ยนทั้งคู่พร้อมกัน

ส่วนที่น่าสนใจที่สุดของโค้ดที่นี่คือฟังก์ชันลูป มันเรียกใช้ slideChange โดยให้ทิศทางของสไลด์ซึ่งเราจะดูในนาทีนี้ อย่างไรก็ตาม ฟังก์ชันวนซ้ำถูกเรียกสองสามครั้ง มาดูกันว่าทำไม

หากอาร์กิวเมนต์เริ่มต้นถูกประเมินว่าเป็นจริง เราจะเรียกใช้ฟังก์ชันลูปเป็นการเรียกกลับ requestAnimationFrame สิ่งนี้จะเกิดขึ้นเมื่อมีการโหลดตัวเลื่อนครั้งแรกซึ่งทำให้เกิดการเปลี่ยนแปลงสไลด์ในทันที การใช้ requestAnimationFrame เราดำเนินการเรียกกลับที่ให้ไว้ก่อนที่จะทาสีใหม่เฟรมถัดไป

ไดอะแกรมของขั้นตอนที่ใช้สร้างตัวเลื่อน

อย่างไรก็ตาม เนื่องจากเราต้องการเล่นสไลด์ต่อไปในโหมดเล่นอัตโนมัติ เราจะใช้ฟังก์ชันเดียวกันนี้ซ้ำๆ ซึ่งมักจะทำได้ด้วย setInterval แต่ในกรณีนี้ เราจะใช้หนึ่งในฟังก์ชันยูทิลิตี้– requestInterval แม้ว่า setInterval จะทำงานได้ดี แต่ requestInterval เป็นแนวคิดขั้นสูงซึ่งอาศัย requestAnimationFrame และให้แนวทางที่มีประสิทธิภาพมากกว่า ช่วยให้มั่นใจได้ว่าฟังก์ชันจะถูกทริกเกอร์อีกครั้งก็ต่อเมื่อแท็บเบราว์เซอร์ทำงานอยู่เท่านั้น

ข้อมูลเพิ่มเติมเกี่ยวกับแนวคิดนี้ในบทความที่ยอดเยี่ยมนี้สามารถพบได้ในเทคนิค CSS โปรดทราบว่าเรากำหนดค่าที่ส่งคืนจากฟังก์ชันนี้ให้กับคุณสมบัติ slider.handle ของเรา ID เฉพาะที่ฟังก์ชันส่งคืนนี้มีให้สำหรับเรา และเราจะใช้เพื่อยกเลิกการเล่นอัตโนมัติในภายหลังโดยใช้ cancelAnimationFrame

เปลี่ยนสไลด์

ฟังก์ชัน slideChange เป็นฟังก์ชันหลักในแนวคิดทั้งหมด มันเปลี่ยนสไลด์ไม่ว่าจะโดยการเล่นอัตโนมัติหรือโดยทริกเกอร์ผู้ใช้ รู้ทิศทางของตัวเลื่อน ให้การวนซ้ำ ดังนั้นเมื่อคุณมาถึงสไลด์สุดท้าย คุณจะสามารถไปยังสไลด์แรกได้ นี่คือวิธีที่ฉันเข้ารหัส:

 const changeSlide = function (direction) { slider.idle = false; slider.hero.classList.remove('prev', 'next'); if (direction == 'next') { slider.activeIndex = (slider.activeIndex + 1) % slider.total; slider.hero.classList.add('next'); } else { slider.activeIndex = (slider.activeIndex - 1 + slider.total) % slider.total; slider.hero.classList.add('prev'); } //reset classes utils().removeClasses(slider.items, ['prev', 'active']); //set prev const prevItems = [...slider.items] .filter(item => { let prevIndex; if (slider.hero.classList.contains('prev')) { prevIndex = slider.activeIndex == slider.total - 1 ? 0 : slider.activeIndex + 1; } else { prevIndex = slider.activeIndex == 0 ? slider.total - 1 : slider.activeIndex - 1; } return item.dataset.index == prevIndex; }); //set active const activeItems = [...slider.items] .filter(item => { return item.dataset.index == slider.activeIndex; }); utils().addClasses(prevItems, ['prev']); utils().addClasses(activeItems, ['active']); setCurrent(); const activeImageItem = slider.main.querySelector('.active'); activeImageItem.addEventListener('transitionend', waitForIdle, { once: true }); }

แนวคิดคือการกำหนดสไลด์ที่ใช้งานอยู่ตามดัชนีข้อมูลที่เราได้รับจาก HTML มาพูดถึงแต่ละขั้นตอนกัน:

  1. ตั้งค่าสถานะไม่ได้ใช้งานของตัวเลื่อนเป็นเท็จ แสดงว่ากำลังดำเนินการเปลี่ยนสไลด์ และปิดใช้งานท่าทางล้อและการสัมผัส
  2. คลาส CSS ทิศทางของตัวเลื่อนก่อนหน้าถูกรีเซ็ตและเราตรวจสอบคลาสใหม่ พารามิเตอร์ทิศทางมีให้โดยค่าเริ่มต้นเป็น 'ถัดไป' หากเรามาจากฟังก์ชันเล่นอัตโนมัติหรือโดยผู้ใช้ที่เรียกใช้ฟังก์ชัน wheelControl หรือ touchControl
  3. ตามทิศทาง เราจะคำนวณดัชนีสไลด์ที่ใช้งานอยู่และจัดเตรียมคลาส CSS ของทิศทางปัจจุบันให้กับตัวเลื่อน คลาส CSS นี้ใช้เพื่อกำหนดเอฟเฟกต์การเปลี่ยนภาพที่จะใช้ (เช่น ขวาไปซ้าย หรือซ้ายไปขวา)
  4. สไลด์ได้รับการรีเซ็ตคลาส CSS "สถานะ" (ก่อนหน้า ใช้งานอยู่) โดยใช้ฟังก์ชันยูทิลิตี้อื่นซึ่งลบคลาส CSS แต่สามารถเรียกใช้บน NodeList ได้ แทนที่จะเป็นองค์ประกอบ DOM เดียว หลังจากนั้น เฉพาะสไลด์ก่อนหน้าและปัจจุบันเท่านั้นที่จะได้รับการเพิ่มคลาส CSS เหล่านั้น ซึ่งช่วยให้ CSS สามารถกำหนดเป้าหมายเฉพาะสไลด์เหล่านั้นและจัดเตรียมการเปลี่ยนภาพที่เพียงพอ
  5. setCurrent คือการโทรกลับที่อัปเดตตัวบ่งชี้ตัวเลื่อนตาม activeIndex
  6. สุดท้าย เรารอให้การเปลี่ยนแปลงของสไลด์รูปภาพที่ใช้งานอยู่สิ้นสุดลงเพื่อเรียกการเรียกกลับ waitForIdle ซึ่งจะเริ่มเล่นอัตโนมัติใหม่หากผู้ใช้ถูกขัดจังหวะก่อนหน้านี้

การควบคุมของผู้ใช้

ตามขนาดหน้าจอ ฉันได้เพิ่มการควบคุมของผู้ใช้สองประเภท ได้แก่ วงล้อและการสัมผัส การควบคุมล้อ:

 const wheelControl = function () { slider.hero.addEventListener('wheel', e => { if (slider.idle) { const direction = e.deltaY > 0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } }); }

ที่นี่ เราฟังวงล้อแม้และหากตัวเลื่อนอยู่ในโหมดไม่ได้ใช้งาน (ขณะนี้ไม่ได้ทำการเปลี่ยนแปลงสไลด์) เราจะกำหนดทิศทางของล้อ เรียกใช้ stopAutoplay เพื่อหยุดฟังก์ชั่นการเล่นอัตโนมัติหากกำลังดำเนินการอยู่ และเปลี่ยนสไลด์ตามทิศทาง ฟังก์ชัน stopAutoplay ไม่ได้เป็นเพียงแค่ฟังก์ชันง่ายๆ ที่ตั้งค่าสถานะการเล่นอัตโนมัติของเราเป็นค่าเท็จ และยกเลิกช่วงเวลาของเราโดยเรียกใช้ฟังก์ชันยูทิลิตี้ cancelRequestInterval ส่งผ่านหมายเลขอ้างอิงที่เหมาะสม:

 const stopAutoplay = function () { slider.autoplay = false; utils().clearRequestInterval(slider.handle); }

คล้ายกับ wheelControl เรามี touchControl ที่ดูแลท่าทางสัมผัส:

 const touchControl = function () { const touchStart = function (e) { slider.ts = parseInt(e.changedTouches[0].clientX); window.scrollTop = 0; } const touchMove = function (e) { slider.tm = parseInt(e.changedTouches[0].clientX); const delta = slider.tm - slider.ts; window.scrollTop = 0; if (slider.idle) { const direction = delta < 0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } } slider.hero.addEventListener('touchstart', touchStart); slider.hero.addEventListener('touchmove', touchMove); }

เรารับฟังสองเหตุการณ์: touchstart และ touchmove จากนั้นเราคำนวณส่วนต่าง หากคืนค่าเป็นลบ เราจะเปลี่ยนเป็นสไลด์ถัดไปเมื่อผู้ใช้เลื่อนจากขวาไปซ้าย ในทางกลับกัน หากค่าเป็นบวก หมายความว่าผู้ใช้เลื่อนจากซ้ายไปขวา เราจะทริกเกอร์ slideChange โดยส่งทิศทางเป็น "ก่อนหน้า" ในทั้งสองกรณี ฟังก์ชันเล่นอัตโนมัติจะหยุดลง

นี่เป็นการใช้ท่าทางสัมผัสของผู้ใช้ที่ค่อนข้างง่าย ในการสร้างสิ่งนี้ เราสามารถเพิ่มปุ่มก่อนหน้า/ถัดไปเพื่อทริกเกอร์ slideChange เมื่อคลิก หรือเพิ่มรายการสัญลักษณ์แสดงหัวข้อย่อยเพื่อไปยังสไลด์โดยตรงตามดัชนี

บทสรุปและความคิดสุดท้ายเกี่ยวกับ CSS

ไปแล้ว วิธี CSS/JS แท้ ๆ ในการเขียนโค้ดเลย์เอาต์ตัวเลื่อนที่ไม่ได้มาตรฐานพร้อมเอฟเฟกต์การเปลี่ยนภาพที่ทันสมัย

ฉันหวังว่าคุณจะพบว่าแนวทางนี้มีประโยชน์ในฐานะวิธีคิด และสามารถใช้สิ่งที่คล้ายกันในโปรเจ็กต์ส่วนหน้าของคุณเมื่อเขียนโค้ดสำหรับโปรเจ็กต์ที่ไม่จำเป็นต้องออกแบบตามอัตภาพ

สำหรับผู้ที่สนใจเอฟเฟกต์การเปลี่ยนภาพ ฉันจะพูดถึงสิ่งนี้ในสองสามบรรทัดถัดไป

หากเราทบทวนโครงสร้าง HTML ของสไลด์ที่ฉันให้ไว้ในส่วนแนะนำ เราจะเห็นว่าแต่ละสไลด์ของรูปภาพมี div ล้อมรอบด้วยคลาส CSS ของ abs-mask สิ่งที่ div นี้ทำคือการซ่อนส่วนหนึ่งของภาพที่มองเห็นได้ด้วยจำนวนหนึ่งโดยใช้ overflow:hidden และออฟเซ็ตไปในทิศทางที่แตกต่างจากภาพ ตัวอย่างเช่น ถ้าเราดูวิธีเข้ารหัสสไลด์ก่อนหน้า:

 &.prev { z-index: 5; transform: translate3d(-100%, 0, 0); transition: 1s $easing; .abs-mask { transform: translateX(80%); transition: 1s $easing; } }

สไลด์ก่อนหน้านี้มีค่าออฟเซ็ต -100% ในแกน X โดยย้ายไปทางซ้ายของสไลด์ปัจจุบัน อย่างไรก็ตาม div abs-mask ด้านในถูกแปลไปทางขวา 80% ทำให้วิวพอร์ตแคบลง เมื่อรวมกับการมีดัชนี z ที่ใหญ่ขึ้นสำหรับสไลด์ที่ทำงานอยู่จะทำให้เกิดเอฟเฟกต์หน้าปก—ภาพที่ใช้งานจะครอบคลุมภาพก่อนหน้าในขณะเดียวกันก็ขยายพื้นที่ที่มองเห็นได้ด้วยการย้ายมาสก์ซึ่งให้มุมมองแบบเต็ม