ในบทช่วยสอนนี้ เราจะสร้างหน้าที่สแน็ปจากส่วนเต็มหน้าจอหนึ่งไปยังอีกส่วนหนึ่งในขณะที่ผู้ใช้เลื่อน เอฟเฟกต์นี้ได้รับความนิยมจากไลบรารี fullpage.js
แต่ตอนนี้ มีตัวเลือกที่ดีกว่าเนื่องจากการเปลี่ยนแปลงใบอนุญาตสำหรับหน้าเต็ม (ตอนนี้คุณต้องจ่ายเงิน) โมดูล CSS scroll-snap ใหม่ และการรองรับเบราว์เซอร์ที่สมบูรณ์ของ API ผู้สังเกตการณ์ทางแยก
ดังนั้น หากคุณกำลังมองหาทางเลือกอื่นของ fullpage.js คุณอาจไม่ต้องการทั้งไลบรารีด้วยซ้ำ คุณสามารถเขียนโค้ดได้ด้วยตัวเอง!
อันที่จริงแล้วเอฟเฟกต์การสแนปคือ 100% CSS JS ใช้เพื่อทำให้องค์ประกอบจางลงเมื่อหน้าจอ "สแนป" เข้าสู่มุมมอง
เราจะใช้ประโยชน์จาก "Dinosaur Park scroll snap demo" โดย Michelle Barker ที่มีอยู่ใน CodePen เพื่อเป็นแรงบันดาลใจ:
ด้วยการใช้ CSS ร่วมกับ Intersection Observer เราจะสร้างแต่ละส่วนแบบเต็มหน้าจอของหน้า snap-scroll และทำให้เนื้อหาจางลงเมื่อเข้าสู่วิวพอร์ต
ยิ่งไปกว่านั้น โค้ด JS เพียงประมาณ 30 บรรทัดเท่านั้น! แอนิเมชั่นทำด้วย CSS; สิ่งที่เราต้องทำคือเข้าใจเมื่อส่วนและเนื้อหาเข้าสู่วิวพอร์ตของเราด้วย JS
1) สร้างเพจของคุณ
ขั้นแรก จะต้องสร้างเพจหรือกำหนดค่าหน้าที่มีอยู่เพื่อให้เข้ากันได้กับเอฟเฟกต์ง่ายๆ นี้ มีสองสิ่งที่ควรทราบที่นี่:
เรากำลังใช้ <main>
องค์ประกอบซึ่งมีส่วนของเรา องค์ประกอบหลักนี้คือ 100vh เช่นเดียวกับแต่ละองค์ประกอบ <section>
ภายใน
HTML:
<main> <section class="section section-1"> <div class="section__content" data-content >เนื้อหาภายใน</div> </section> <section class="section section-2"> <div class= "section__content" data-content >เนื้อหาภายใน</div> </section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">สำรวจและค้นพบ</h3 > </header> <div class="section__content" data-content >เนื้อหาภายใน</div> </section> </main> ซีเอสเอส:
/* พฤติกรรมการเลื่อน */ @media (ความสูงขั้นต่ำ: 30em) { หลัก { scroll-snap-type: y บังคับ; ความสูง: 100vh; ล้น-y: เลื่อน; } } .section { สี: ขาว; ตำแหน่ง: ญาติ; scroll-snap-align: ศูนย์; จอแสดงผล: ดิ้น; ทิศทางดิ้น: คอลัมน์; ความสูงขั้นต่ำ: 100vh; } @media (ความสูงขั้นต่ำ: 30em) { .section (ความสูง: 100vh; } } 2) กำหนดค่า Snapping ด้วย CSS
ที่นี่เราจะเห็นว่ามีหลายส่วน แต่ละส่วนมีความสูง 100vh
<main> องค์ประกอบยังเป็น 100vh โดยมี overflow-y:scroll
สำหรับผู้ใช้ ลักษณะการเลื่อนจะเหมือนกับหน้าเว็บเริ่มต้น
หากคุณเพิ่ม CSS ที่กล่าวถึงข้างต้น เอฟเฟกต์การสแนปจะเล่นอยู่แล้ว มาดูกันว่ามันทำงานอย่างไร
ขั้นแรก แบบสอบถาม " @media (min-height: 30em)
" จะปิดใช้เอฟเฟกต์การสแนปสำหรับขนาดหน้าจอมือถือที่สั้นในแง่ของความสูง ใต้ส่วนหลัก มีหนึ่งคุณสมบัติที่เปิดใช้งานการสแนป:
main { .... scroll-snap-type: y บังคับ; .... } นี่เป็นคุณสมบัติเล็ก ๆ ที่ยอดเยี่ยม "กำหนดวิธีการบังคับใช้จุดสแน็ปอย่างเคร่งครัดบนคอนเทนเนอร์เลื่อนในกรณีที่มี" (MDN)
และภายใต้ส่วนของเรา เรามี:
.section { .... scroll-snap-align: center; .... } คุณสมบัตินี้ "คุณสมบัติระบุตำแหน่งสแน็ปของกล่องเป็นการจัดตำแหน่งของพื้นที่สแน็ป (เป็นตัวกำหนดการจัดตำแหน่ง) ภายในสแนปพอร์ตของคอนเทนเนอร์สแน็ป (เป็นคอนเทนเนอร์การจัดตำแหน่ง)" (MDN)
เรียนรู้เพิ่มเติมเกี่ยวกับโมดูล CSS scroll-snap ที่นี่:
และมันก็ง่ายอย่างนั้น กำหนดลูกภายในคอนเทนเนอร์ <main> (ทุกส่วน) จะเลื่อนสแนปไปที่กึ่งกลางของคอนเทนเนอร์ <main> เนื่องจากทั้ง Main และ Section เป็น 100vh การเลื่อนจะเลื่อนจากส่วนหนึ่งไปอีกส่วน
3) เฟดเนื้อหาในการใช้ Intersection Observer
ถึงตอนนี้ เรามีเอฟเฟกต์การเลื่อนแบบเต็มหน้า และหากคุณไม่ต้องการทำอย่างอื่น คุณสามารถข้ามส่วนนี้และเพลิดเพลินกับเว็บไซต์ของคุณได้ อย่างไรก็ตาม เมื่อแต่ละส่วนของหน้าเต็มเข้ามาดู ฉันต้องการลดเนื้อหาภายใน - เอฟเฟกต์ที่ไม่ซ้ำใครและเจ๋ง (เช่นเดียวกับ fullpage.js ที่เป็นไปได้ด้วย)
ในการทำเช่นนี้ เราจะใช้ผู้สังเกตการณ์ทางแยกเพื่อทำความเข้าใจตำแหน่งของส่วนต่างๆ ภายในวิวพอร์ต <main> ของเรา
ส่วน const = [...document.querySelectorAll("section")]; ให้ตัวเลือก = { rootMargin: "0px", เกณฑ์: 0.75, }; const callback = (รายการผู้สังเกตการณ์) => { items.forEach ((รายการ) => { const { target } = รายการ; if (entry.intersectionRatio >= 0.75) { target.classList.add ("is-visible") ; } อื่น { target.classList.remove("is-visible"); } }); }; const ผู้สังเกตการณ์ = IntersectionObserver ใหม่ (โทรกลับ, ตัวเลือก); section.forEach((ส่วน, ดัชนี) => { const sectionChildren = [...section.querySelector("[data-content]")).children]; sectionChildren.forEach((el, index) => { el.style .setProperty("--delay", `${index * 250}ms`); }); observer.observe(section); }); .setProperty("--delay", `${index * 250}ms`); }); ข้อสังเกต. มีสองสิ่งที่ต้องทำที่นี่ ขั้นแรก จะระบุว่าส่วนใดอยู่ในวิวพอร์ต และเมื่อเข้าไปแล้ว จะเพิ่มคลาส CSS .is-visible
เมื่อมันออกจากวิวพอร์ต มันจะลบคลาสนั้นออก
นอกจากนี้ เมื่อส่วนนี้เข้าสู่วิวพอร์ต ตามลำดับ (ทีละรายการ) จะตั้งค่าคุณสมบัติของการหน่วงเวลาเป็น 250ms ในแต่ละองค์ประกอบย่อย สิ่งนี้ทำให้เกิดการจางหายที่เซ
สิ่งนี้ทำกับเด็ก ๆ ภายในองค์ประกอบที่มีแอตทริบิวต์ของเนื้อหาข้อมูลเท่านั้น ดังนั้น คุณต้องเพิ่มสิ่งนั้นลงใน wrappers รอบ ๆ เนื้อหาหน้าของคุณ หากคุณต้องการให้เอฟเฟกต์เฟด
ในการทำงานนี้ เราต้องตั้งค่าแอนิเมชั่นเฟดอินสำหรับองค์ประกอบที่มีคลาส .is-visible เพิ่มเข้ามา
@media (ความสูงขั้นต่ำ: 30em) { .section__content > * { ความทึบ: 0; แปลง: translate3d(0, 4rem, 0); การเปลี่ยนแปลง: ความทึบ 800ms var(--delay), เปลี่ยน 800ms cubic-bezier(0.13, 0.07, 0.26, 0.99) var(--delay); } } .is-visible .section__content > * { ความทึบ: 1; แปลง: translate3d(0, 1rem, 0); } .is-visible .section__img { ความทึบ: 0.75; } หากเราย้อนกลับไปที่ HTML จากตัวอย่างแรก คุณจะเห็นว่ามี div ล้อมรอบเนื้อหาที่มีคลาสของ .section__content และแอตทริบิวต์ data-content
<main> <section class="section section-1"> <div class="section__content" data-content >เนื้อหาภายใน</div> </section> <section class="section section-2"> <div class= "section__content" data-content >เนื้อหาภายใน</div> </section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">สำรวจและค้นพบ</h3 > </header> <div class="section__content" data-content >เนื้อหาภายใน</div> </section> </main> สิ่งนี้เชื่อมโยง JS และ CSS ของเราเข้าด้วยกัน ความทึบเป็น 0 สำหรับเนื้อหาจนกว่าคลาส .is-visible
จะถูกเพิ่มโดยผู้สังเกตการณ์ทางแยกเมื่อเข้าสู่วิวพอร์ต เมื่อมันออกไป มันจะเปลี่ยนกลับเป็นความทึบเป็น 0 ดังนั้นไม่ว่าทิศทางการเลื่อนของคุณจะเป็นอย่างไร ฟีดจะมีผลเสมอ
สุดท้าย การ delay
ที่ใช้อย่างต่อเนื่องกับองค์ประกอบย่อยแต่ละรายการจะทำให้การจางหายไป
หมายเหตุส่วนที่สาม ส่วนหัวของส่วนไม่อยู่ในส่วนข้อมูล-เนื้อหา ดังนั้นจะไม่จางหายไป (จะมีอยู่แล้ว)
The Code ทั้งหมด
นี่คือตัวอย่าง:
นี่คือรหัสทั้งหมดที่เราใช้ในการสาธิตของเรา:
HTML
<main> <section class="section section-1"> <div class="section__content" data-content> <img class="section__img section__img--left" src="https://s3-us-west-2 .amazonaws.com/s.cdpn.io/85648/trex.svg" alt="T-rex" /> <h2>เนื้อหาภายใน</h2> <p>บลา บลา บลา</p> </div> < /section> <section class="section section-2"> <div class="section__content" data-content> <h2>เนื้อหาภายใน</h2> <p>blah blah blah</p> </div> </ section> <section class="section section-3"> <header class="section__header"> <h3 class="section__title">สำรวจและค้นพบ</h3> </header> <div class="section__content" data-content > <h2>เนื้อหาภายใน</h2> <p>blah blah blah</p> </div> </section> </main> CSS
@media (ความสูงขั้นต่ำ: 30em) { หลัก { ประเภทเลื่อนสแน็ป: y บังคับ; ความสูง: 100vh; ล้น-y: เลื่อน; } } .section { สี: ขาว; ตำแหน่ง: ญาติ; scroll-snap-align: ศูนย์; จอแสดงผล: ดิ้น; ทิศทางดิ้น: คอลัมน์; ความสูงขั้นต่ำ: 100vh; } @media (ความสูงขั้นต่ำ: 30em) { .section (ความสูง: 100vh; } } @media (ความสูงต่ำสุด: 30em) { .section__content > * { ความทึบ: 0; แปลง: translate3d(0, 4rem, 0); การเปลี่ยนแปลง: ความทึบ 800ms var(--delay), เปลี่ยน 800ms cubic-bezier(0.13, 0.07, 0.26, 0.99) var(--delay); } } .is-visible .section__content > * { ความทึบ: 1; แปลง: translate3d(0, 1rem, 0); } .is-visible .section__img { ความทึบ: 0.75; } JS
ส่วน const = [...document.querySelectorAll("section")]; ให้ตัวเลือก = { rootMargin: "0px", เกณฑ์: 0.75, }; const callback = (รายการ, ผู้สังเกตการณ์) => { entries.forEach ((entry) => { const { target } = รายการ; if (entry.intersectionRatio >= 0.75) { target.classList.add ("is-visible") ; } อื่น ๆ { target.classList.remove("is-visible"); } }); }; const ผู้สังเกตการณ์ = IntersectionObserver ใหม่ (โทรกลับ, ตัวเลือก); section.forEach((ส่วน, ดัชนี) => { const sectionChildren = [...section.querySelector("[data-content]")).children]; sectionChildren.forEach((el, index) => { el.style .setProperty("--delay", `${index * 250}ms`); }); observer.observe(section); }); .setProperty("--delay", `${index * 250}ms`); }); ข้อสังเกต. บทสรุป
นี่เป็นทางเลือก fullpage.js ที่สมบูรณ์แบบซึ่งไม่ได้หยุดเพียงแค่การสแนประหว่างส่วนต่างๆ แบบเต็มหน้าจอ นอกจากนี้เรายังใช้ API ผู้สังเกตการณ์ทางแยกเพื่อทำความเข้าใจเมื่อส่วนใดส่วนหนึ่งเข้าสู่หน้า จากนั้นจึงใช้ภาพเคลื่อนไหวที่เซแบบไดนามิก คุณสามารถใช้ตัวอย่างพื้นฐานนี้เพื่อสร้างและสร้างเอฟเฟกต์สุดเจ๋งด้วย vanilla JS และ CSS ที่น้อยที่สุด