แถบเลื่อนที่กำหนดเองนั้น พบได้ยากมาก ซึ่งส่วนใหญ่แล้วเป็นเพราะ แถบเลื่อนก็เป็นหนึ่งในบิตเล็กๆ ที่เหลือบนเว็บ จัดรูปแบบไม่ได้ (ฉันกำลังมองคุณอยู่ เครื่องมือเลือกวันที่) คุณสามารถใช้ JavaScript เพื่อสร้าง JavaScript ของตัวเองได้ แต่ก็มีราคาแพงและต่ำ และอาจทำให้ดูช้า ในบทความนี้ เราจะใช้ประโยชน์จาก เมทริกซ์ CSS ที่ไม่ปกติเพื่อสร้างแถบเลื่อนแบบกำหนดเอง JavaScript ขณะเลื่อนดู มีเพียงโค้ดการตั้งค่าบางส่วนเท่านั้น
TL;DR
ถ้าไม่สนใจอะไรเล็กๆ ก็พอ คุณเพียงแค่ต้องการดู การสาธิตแมว Nyan และรับไลบรารีไหม คุณสามารถดูโค้ดการสาธิตได้ใน ที่เก็บของ GitHub
LAM;WRA (ยาวและอยู่ในเชิงคณิตศาสตร์ จะอ่านต่อไป)
เมื่อนานมาแล้ว เราได้สร้างเครื่องมือเลื่อนพารัลแลกซ์ (คุณได้อ่าน บทความนั้น ซึ่งเป็นสิ่งที่ดีจริงๆ ที่คุ้มค่ากับการเสียเวลาของคุณ) ดันองค์ประกอบกลับโดยใช้ CSS 3D การเปลี่ยนรูปแบบ ทำให้องค์ประกอบเคลื่อนที่ช้ากว่าความเร็วในการเลื่อนจริง
สรุป
มาเริ่มกันด้วยการสรุปวิธีการทำงานของแถบเลื่อนพารัลแลกซ์
ตามที่แสดงในภาพเคลื่อนไหว เราสร้างเอฟเฟกต์พารัลแลกซ์จากการดันองค์ประกอบ "ย้อนกลับ" ในพื้นที่ 3 มิติตามแกน Z การเลื่อนในเอกสาร การแปลตามแกน Y ดังนั้นถ้าเราเลื่อนลง อย่างเช่น 100px องค์ประกอบจะแปลเป็นด้านบน 100 พิกเซล ซึ่งจะมีผลกับองค์ประกอบทั้งหมด แม้แต่กับหน้าที่ "ข้างหลัง" แต่เพราะอยู่ห่างจาก กล้องที่มีการเคลื่อนไหวบนหน้าจอที่สังเกตได้จะมีขนาดเล็กกว่า 100 พิกเซล เอฟเฟ็กต์พารัลแลกซ์ที่ต้องการ
แน่นอนว่าการย้ายองค์ประกอบ กลับไปในอวกาศจะทำให้มีขนาดเล็กลงด้วย ซึ่งเราแก้ไขโดยการปรับขนาด องค์ประกอบให้สำรองข้อมูล เราหาทางคณิตศาสตร์ได้ถูกต้องแล้ว ตอนที่เราสร้าง ตัวเลื่อนพารัลแลกซ์ จะไม่ขอเล่ารายละเอียดทั้งหมดซ้ำ
ขั้นตอนที่ 0: คุณอยากทำอะไรบ้าง
แถบเลื่อน และเป็นสิ่งที่เรากำลังจะสร้าง แต่คุณเคยคิด เกี่ยวกับสิ่งที่พวกเขาทำ ว่าแต่คิดว่าไม่ตอบเลย แถบเลื่อนเป็นตัวบ่งชี้ ปริมาณเนื้อหาที่ดูได้ในปัจจุบันและความคืบหน้า ในฐานะที่คุณผู้อ่านสร้างไว้ หากเลื่อนลง แถบเลื่อนก็จะปรับ บ่งบอกว่าคุณกำลังคืบหน้าไปในตอนท้าย หากเนื้อหาทั้งหมดเหมาะสม ในวิวพอร์ต โดยปกติแล้วแถบเลื่อนจะซ่อนไว้ หากเนื้อหามีความสูงเป็น 2 เท่าของวิวพอร์ต แถบเลื่อนจะเต็ม 1⁄2 ของความสูงของวิวพอร์ต คอนเทนต์มีความสูงเป็น 3 เท่าของความสูง วิวพอร์ตจะปรับขนาดแถบเลื่อนไปที่ 1⁄3 ของวิวพอร์ต เป็นต้น คุณจะเห็นรูปแบบ คุณยังคลิกแล้วลากแถบเลื่อนเพื่อเลื่อนไปยังส่วนต่างๆ แทนการเลื่อนได้ด้วย ไซต์ได้รวดเร็วยิ่งขึ้น ซึ่งเป็นพฤติกรรมที่น่าประหลาดใจสำหรับผู้ดูแลที่ไม่ชัดเจน แบบนี้ เข้าร่วมการต่อสู้ทีละรายการ
ขั้นตอนที่ 1: ใส่อุปกรณ์กลับด้าน
เอาล่ะ เราทำให้องค์ประกอบเคลื่อนไหวได้ช้ากว่าความเร็วในการเลื่อนด้วย CSS 3D เปลี่ยนรูปแบบตามที่ระบุไว้ในบทความแบบเลื่อนพารัลแลกซ์ เราสลับกันได้ เส้นทาง ปรากฏว่าเราสามารถและนั่นเป็นวิธีที่เราสร้าง แถบเลื่อนที่กำหนดเองได้สมบูรณ์แบบ เพื่อให้เข้าใจวิธีการทำงาน เราจำเป็นต้องครอบคลุม ข้อมูลพื้นฐานเกี่ยวกับ CSS 3 มิติก่อน
ถ้าต้องการดูการคาดการณ์มุมมองใดๆ ในเชิงคณิตศาสตร์ คุณ น่าจะได้ใช้ พิกัดเดียวกัน ผมจะไม่ลงรายละเอียดว่ากลยุทธ์เหล่านี้คืออะไร และทำไมจึงได้ผล แต่คุณอาจลองนึกถึง อย่างเช่นพิกัด 3 มิติกับพิกัดเพิ่มเติมอันที่ 4 ที่เรียกว่า w ช่วงเวลานี้ พิกัดควรเป็น 1 ยกเว้นในกรณีที่คุณต้องการให้มุมมองบิดเบี้ยว พ ไม่ต้องกังวลเกี่ยวกับรายละเอียดของ w เนื่องจากเราจะไม่ใช้ ค่าอื่นๆ นอกเหนือจาก 1 จากนี้ไปจุดทั้งหมดจึงมาจากเวกเตอร์ 4 มิติ [x, y, z, w=1] และทำให้เมทริกซ์ต้องการ เป็น 4x4 ด้วย
มีครั้งหนึ่งที่คุณเห็นว่า CSS ใช้พิกัดเดียวกันภายใต้
ฮู้ดคือเมื่อคุณกำหนดเมทริกซ์ 4x4 ของคุณเองในคุณสมบัติการแปลงโดยใช้
matrix3d()
matrix3d
รับ 16 อาร์กิวเมนต์ (เนื่องจากเมทริกซ์
4x4) โดยระบุหนึ่งคอลัมน์หลังจากนั้น เราจึงสามารถใช้ฟังก์ชันนี้เพื่อ
ระบุการหมุน การแปล และอื่นๆ ด้วยตนเอง แต่เครื่องมือนี้ช่วยให้เรา
พิกัด w นั้นยุ่งเหยิง!
ก่อนที่จะใช้ประโยชน์จาก matrix3d()
ได้ เราต้องมีบริบท 3 มิติเพราะไม่มี
จะไม่มีการบิดเบี้ยวของมุมมอง 3 มิติ และไม่จำเป็นต้องใช้
พิกัดที่เป็นเอกภาพ ในการสร้างบริบท 3 มิติ เราต้องมีคอนเทนเนอร์ที่มี
perspective
และองค์ประกอบบางอย่างภายในที่เราสามารถเปลี่ยนรูปแบบใน
สร้าง Space 3 มิติแล้ว สำหรับ
ตัวอย่าง
![โค้ด CSS ชิ้นหนึ่งที่บิดเบี้ยว div โดยใช้
มุมมอง](https://developer.chrome.com/static/docs/css-ui/custom-scrollbar/image/a-piece-css-code-distor-1ed892aadfd7c.png?authuser=0&hl=th)
เครื่องมือ CSS จะประมวลผลองค์ประกอบภายในคอนเทนเนอร์มุมมอง ดังนี้
- เปลี่ยนแต่ละมุม (จุดยอดมุม) ขององค์ประกอบให้เป็นพิกัดเดียวกัน
[x,y,z,w]
ซึ่งสัมพันธ์กับคอนเทนเนอร์มุมมอง - ใช้การแปลงทั้งหมดขององค์ประกอบเป็นเมทริกซ์จากขวาไปซ้าย
- หากองค์ประกอบมุมมองสามารถเลื่อนได้ ให้ใช้เมทริกซ์การเลื่อน
- ใช้เมทริกซ์มุมมอง
เมทริกซ์การเลื่อนคือการแปลตามแกน y หากเราเลื่อนลงตาม 400 พิกเซล องค์ประกอบทั้งหมดจะต้องย้ายขึ้น 400 พิกเซล เมทริกซ์มุมมองคือ เมทริกซ์ที่ "ดึง" ชี้ไปใกล้จุดหายไปมากขึ้นในแบบ 3 มิติ ให้อยู่กับคุณ วิธีนี้ได้รับผลทั้ง 2 ข้อจากการทำให้สิ่งต่างๆ ดูเล็กลงเมื่อ อยู่ไกลออกไปและยังทำให้ "เคลื่อนไหวช้าลง" ขณะแปล ดังนั้น หากมีการถอยหลังกลับไป 400px จะทำให้องค์ประกอบนั้น เพื่อเลื่อนหน้าจอแค่ 300 พิกเซล
ถ้าต้องการทราบรายละเอียดทั้งหมด คุณควรอ่านข้อมูล ข้อกำหนดเกี่ยวกับ CSS เปลี่ยนรูปแบบการแสดงผลได้ แต่เพื่อประโยชน์ของบทความนี้ ผมได้ปรับให้ ด้านบน
กล่องของเราอยู่ในคอนเทนเนอร์มุมมองที่มีค่า p สำหรับ perspective
และสมมติว่าคอนเทนเนอร์เป็นแบบเลื่อนได้และเลื่อนลงได้
n พิกเซล
เมทริกซ์แรกคือเมทริกซ์มุมมอง เมทริกซ์ที่สองคือการเลื่อน เมตริกซ์ กล่าวโดยสรุปคือ หน้าที่ของเมทริกซ์การเลื่อนคือการทำให้องค์ประกอบย้ายขึ้นเมื่อเรา เลื่อนลง ดังนั้นเครื่องหมายลบ
แต่สำหรับแถบเลื่อนของเรา เราต้องการสิ่งที่ตรงกันข้ามคือเราต้องการให้องค์ประกอบ
ย้ายลง เมื่อเราเลื่อนลง เคล็ดลับที่เราใช้ได้มีดังนี้
กลับพิกัด w ของมุมกล่อง ถ้าพิกัด w คือ
-1 การแปลทั้งหมดจะมีผลในทิศทางตรงกันข้าม เราจะทำอย่างไร
ใช่ไหม เครื่องมือ CSS จะดูแลการแปลงมุมของกล่องให้เป็น
พิกัดที่เป็นเอกภาพ และกำหนด w เป็น 1 ได้เวลาให้ matrix3d()
เปล่งประกายแล้ว
.box {
transform:
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
);
}
เมทริกซ์นี้จะไม่ดำเนินการใดๆ นอกจากลบ w ดังนั้นเมื่อเครื่องมือ CSS
เปลี่ยนแต่ละมุมให้เป็นเวกเตอร์ของรูปแบบ [x,y,z,1]
เมทริกซ์จะ
แปลงเป็น [x,y,z,-1]
ฉันระบุขั้นตอนระดับกลางเพื่อแสดงผลกระทบของการแปลงองค์ประกอบ เมตริกซ์ หากคุณไม่ถนัดเรื่องคณิตศาสตร์เมทริกซ์ ก็ไม่เป็นไร ยูเรกา คือ ในบรรทัดสุดท้าย เราเพิ่มค่าออฟเซ็ตการเลื่อน n ลงใน y ของเรา พิกัดแทนการลบ ระบบจะแปลองค์ประกอบลง หากเราเลื่อนลง
แต่ถ้าเราแค่ใส่เมทริกซ์นี้ลงใน ตัวอย่าง ระบบจะไม่แสดงองค์ประกอบนั้น เนื่องจากข้อกำหนดของ CSS กำหนดให้ จุดยอดที่มี w < 0 จะบล็อกองค์ประกอบไม่ให้แสดง และเนื่องจาก Z ขณะนี้พิกัดอยู่ที่ 0 และ p คือ 1 ส่วน w จะเท่ากับ -1
โชคดีที่เราเลือกค่า z ได้ เพื่อให้แน่ใจว่าเราได้ w=1 เราต้องการ เพื่อตั้ง z = -2
.box {
transform:
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
)
translateZ(-2px);
}
นี่ไง กลับมาแล้ว!
ขั้นตอนที่ 2: เคลื่อนไหวร่างกาย
ตอนนี้กล่องของเราอยู่ในนั้นแล้ว และมีลักษณะแบบเดียวกับที่จะไม่มี เปลี่ยนรูปแบบได้ ตอนนี้ คอนเทนเนอร์มุมมองไม่สามารถเลื่อนได้ เราจึงไม่สามารถ เห็นได้ แต่เรารู้ว่าองค์ประกอบจะไปในทิศทางอื่นเมื่อ เลื่อนแล้ว มาทำให้คอนเทนเนอร์เลื่อนได้กัน ตกลงไหม เราสามารถเพิ่ม องค์ประกอบ Spacer ที่กินพื้นที่:
<div class="container">
<div class="box"></div>
<span class="spacer"></span>
</div>
<style>
/* … all the styles from the previous example … */
.container {
overflow: scroll;
}
.spacer {
display: block;
height: 500px;
}
</style>
และตอนนี้ เลื่อนกล่อง กล่องสีแดงเลื่อนลง
ขั้นตอนที่ 3: กำหนดขนาด
เรามีองค์ประกอบที่เลื่อนลงเมื่อหน้าเว็บเลื่อนลง นั่นเป็นเรื่องยาก ออกไปหน่อยได้ไหม คราวนี้เราต้องจัดรูปแบบให้ดูเหมือนแถบเลื่อน ทำให้มีการโต้ตอบมากขึ้น
แถบเลื่อนมักจะประกอบด้วย "นิ้วโป้ง" และ "แทร็ก" ส่วนแทร็กนั้นไม่ใช่ มองเห็นได้เสมอ ความสูงของนิ้วโป้งสูงจะเป็นสัดส่วนโดยตรงกับ เนื้อหานั้นมองเห็นได้
<script>
const scroller = document.querySelector('.container');
const thumb = document.querySelector('.box');
const scrollerHeight = scroller.getBoundingClientRect().height;
thumb.style.height = /* ??? */;
</script>
scrollerHeight
คือความสูงขององค์ประกอบที่เลื่อนได้
scroller.scrollHeight
คือความสูงรวมของเนื้อหาที่เลื่อนได้
scrollerHeight/scroller.scrollHeight
คือเศษส่วนของเนื้อหาที่
มองเห็นได้ อัตราส่วนของพื้นที่แนวตั้งที่ครอบนิ้วหัวแม่มือควรเท่ากับ
อัตราส่วนของเนื้อหาที่มองเห็นได้:
<script>
// …
thumb.style.height =
scrollerHeight * scrollerHeight / scroller.scrollHeight + 'px';
// Accommodate for native scrollbars
thumb.style.right =
(scroller.clientWidth - scroller.getBoundingClientRect().width) + 'px';
</script>
ขนาดนิ้วโป้งคือ ดูดีแล้ว แต่เคลื่อนไหวเร็วเกินไป นี่คือที่ที่เราสามารถดึงเทคนิคของเราจาก แถบเลื่อนพารัลแลกซ์ หากเราเลื่อนองค์ประกอบถอยหลังเข้ามาอีก องค์ประกอบจะเคลื่อนที่ช้าลง ขณะเลื่อน เราแก้ไขขนาดได้โดยการปรับขนาดขึ้น แต่เราควรจะผลักดัน กลับเข้าไปใหม่ มาลองคิดเลขเก่งๆ กันเลย! นี่เป็นครั้งสุดท้ายที่ฉัน สัญญา
ข้อมูลสำคัญที่ควรทราบคือ เราต้องการให้ปลายนิ้วโป้ง
อยู่ในแนวเดียวกับขอบด้านล่างขององค์ประกอบที่เลื่อนได้เมื่อเลื่อนจนสุด
ลง กล่าวคือ หากเราเลื่อนดู
scroller.scrollHeight - scroller.height
พิกเซล เราต้องการให้นิ้วโป้งของเรา
แปลโดย scroller.height - thumb.height
สำหรับทุกพิกเซลของแถบเลื่อน
ต้องการให้นิ้วโป้งของเราขยับเป็นเสี้ยวของพิกเซล
และนั่นคือปัจจัยที่ใช้ในการปรับขนาด ตอนนี้เราต้องแปลงค่าตัวคูณมาตราส่วนเป็น
ตามแนวแกน z ซึ่งเราทำแล้วในการเลื่อนพารัลแลกซ์
ตาม
ส่วนที่เกี่ยวข้องในข้อกำหนด
ค่าตัวคูณมาตราส่วนเท่ากับ p/(p − z) เราสามารถแก้สมการนี้จาก z เป็น
ดูว่าเราต้องแปลค่านิ้วโป้งเท่าไรไปตามแกน z แต่เก็บไว้
แต่เนื่องจาก ความ ทำงานร่วมกัน ของเรา เราจึงจำเป็นต้องแปล
-2px
เพิ่มเติมไปพร้อมกับ z และโปรดทราบว่ามีการใช้การเปลี่ยนรูปแบบขององค์ประกอบ
ขวาไปซ้าย หมายความว่าการแปลทั้งหมดก่อนเมทริกซ์พิเศษจะไม่
การกลับด้านการแปลกลับ อย่างไรก็ตาม การแปลทั้งหมดหลังจากเมทริกซ์พิเศษของเราจะ! มาเริ่มกันเลย
แปลตอนนี้เลย!
<script>
// ... code from above...
const factor =
(scrollerHeight - thumbHeight)/(scroller.scrollHeight - scrollerHeight);
thumb.style.transform = `
matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, -1
)
scale(${1/factor})
translateZ(${1 - 1/factor}px)
translateZ(-2px)
`;
</script>
เรามี แถบเลื่อน! และเป็นเพียงองค์ประกอบ DOM ที่เราสามารถจัดรูปแบบได้ตามใจชอบ สิ่งหนึ่งที่ ในแง่ของความสามารถเข้าถึงได้ง่าย คือการทำให้นิ้วโป้งตอบสนองต่อ คลิกแล้วลาก เนื่องจากผู้ใช้จำนวนมากคุ้นเคยกับแถบเลื่อนในลักษณะนั้น เพื่อไม่ให้บล็อกโพสต์นี้ยาวขึ้น ฉันจะไม่อธิบาย รายละเอียดของส่วนนั้น ดูที่ รหัสห้องสมุด เพื่อดูรายละเอียดการทำงาน
แล้ว iOS ล่ะ
เอ่อ เพื่อนเก่า iOS Safari เช่นเดียวกับการเลื่อนพารัลแลกซ์ เราจะเจอกับ
ที่นี่ เนื่องจากเรากำลังเลื่อนไปยังองค์ประกอบหนึ่ง เราจึงต้องระบุ
-webkit-overflow-scrolling: touch
แต่ทำให้เกิดการแยก 3 มิติ
เอฟเฟกต์การเลื่อนหยุดทำงาน เราแก้ปัญหานี้ได้ในแถบเลื่อนพารัลแลกซ์
โดยการตรวจหา iOS Safari และใช้ position: sticky
เป็นวิธีแก้ปัญหาชั่วคราว และ
เราจะทำสิ่งเดียวกันนี้ ดูที่
บทความพารัลแลกซ์
เพื่อรีเฟรชความทรงจำ
แล้วแถบเลื่อนของเบราว์เซอร์จะเป็นอย่างไร
ในบางระบบ เราจะต้องจัดการกับแถบเลื่อนแบบเนทีฟอย่างถาวร
ในอดีต แถบเลื่อนจะซ่อนไม่ได้ (ยกเว้น
Pseudo-selector ที่ไม่ได้มาตรฐาน)
เราจึงต้องรีบจัดการกับการแฮ็ก (ไม่มีคณิตศาสตร์) เพื่อซ่อนไว้ เรารวม
เอลิเมนต์ที่เลื่อนในคอนเทนเนอร์ที่มี overflow-x: hidden
และทำให้ฟิลด์
องค์ประกอบที่เลื่อนได้กว้างกว่าคอนเทนเนอร์ แถบเลื่อนเริ่มต้นของเบราว์เซอร์คือ
ออกจากมุมมองแล้ว
Fin
นำทุกอย่างมารวมกัน ตอนนี้เราก็สามารถสร้างกรอบที่สมบูรณ์แบบ แถบเลื่อน – เช่นเดียวกับใน การสาธิตแมว Nyan
หากคุณไม่เห็นแมว Nyan แสดงว่าคุณ ข้อบกพร่องที่เราพบและยื่นไว้ ขณะสร้างการสาธิตนี้ (คลิกนิ้วหัวแม่มือเพื่อให้แมว Nyan ปรากฏขึ้น) Chrome ช่วยหลีกเลี่ยงงานที่ไม่จำเป็นได้อย่างดีเยี่ยม เช่น การวาดภาพหรือภาพเคลื่อนไหวของสิ่งต่างๆ ที่ไม่ได้อยู่ในหน้าจอ ข่าวร้ายคือ เรื่องตลกร้ายๆ ของเมทริกซ์ทำให้ Chrome คิดว่า GIF แมว Nyan นั้นอยู่นอกหน้าจอ เราหวังว่าปัญหานี้จะได้รับการแก้ไขในเร็วๆ นี้
เท่านี้ก็เรียบร้อย เป็นงานที่หนักมาก ขอขอบคุณอย่างยิ่งที่อ่านเนื้อหาทั้งหมด ประมาณนั้น นี่คือบางส่วน ต้องใช้ความพยายามอย่างเต็มที่ จึงจะได้ผล และแทบจะไม่คุ้มค่ากับความพยายาม ยกเว้นในกรณีที่แถบเลื่อนที่กำหนดเองเป็นส่วนสำคัญของประสบการณ์การใช้งาน แต่ ก็ดีที่รู้ว่าเป็นไปได้ ไม่จริงเหรอ ความจริงที่ว่า การดำเนินการเพื่อ แถบเลื่อนที่กำหนดเองแสดงให้เห็นว่า CSS มีงานต้องทำบ้าง แต่ไม่ต้องกังวลไป ในอนาคต ของ Houdini AnimationWorklet กำลังจะ เอฟเฟกต์แบบเลื่อนได้ที่ให้ความรู้สึกสมบูรณ์แบบในเฟรมแบบนี้ง่ายขึ้นมาก