เมื่อทำวิศวกรรมย้อนกลับบางอย่างเกี่ยวกับAirpods Pro หน้าเราสังเกตเห็นว่าการเคลื่อนไหวไม่ได้ใช้แต่video
canvas
การดำเนินการดังต่อไปนี้:
- โหลดล่วงหน้าประมาณ 1,500 ภาพผ่าน HTTP2 ซึ่งเป็นเฟรมภาพเคลื่อนไหว
- สร้างอาร์เรย์ของรูปภาพในรูปแบบของ
HTMLImageElement
- ตอบสนองต่อทุก
scroll
เหตุการณ์ DOM และขอเฟรมภาพเคลื่อนไหวที่สอดคล้องกับภาพที่ใกล้ที่สุดด้วยrequestAnimationFrame
- ในเฟรมภาพเคลื่อนไหวขอให้โทรกลับแสดงภาพโดยใช้
ctx.drawImage
( ctx
เป็น2d
บริบทของcanvas
องค์ประกอบ)
requestAnimationFrame
ฟังก์ชั่นจะช่วยให้คุณบรรลุผลเรียบเป็นกรอบจะได้รับการรอการตัดบัญชีและตรงกับ "เฟรมต่อวินาที" อัตราของหน้าจอเป้าหมาย
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการแสดงเฟรมในเหตุการณ์เลื่อนอย่างถูกต้องคุณสามารถอ่านได้ที่: https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event
ที่ถูกกล่าวว่าเกี่ยวกับปัญหาหลักของคุณฉันมีวิธีการทำงานที่ประกอบด้วย:
- การสร้างตัวยึดที่มีความสูงและความกว้างเท่ากันกับ
video
องค์ประกอบ โดยมีวัตถุประสงค์คือเพื่อหลีกเลี่ยงวิดีโอทับซ้อนส่วนที่เหลือของ HTML เมื่อตั้งค่าabsolute
ตำแหน่ง
- เข้าสู่การ
scroll
โทรกลับเหตุการณ์เมื่อตัวยึดตำแหน่งมาถึงด้านบนของวิวพอร์ตกำหนดตำแหน่งของวิดีโอเป็นabsolute
และtop
ค่าที่เหมาะสม
แนวคิดก็คือวิดีโอจะไม่ลื่นไหลและจะเกิดขึ้นเหนือตัวยึดตำแหน่งในเวลาที่เหมาะสมเมื่อเลื่อนไปที่ด้านล่าง
นี่คือ JavaScript:
//Get video element
let video = $("#video-effect-wrapper video").get(0);
video.pause();
let topOffset;
$(window).resize(onResize);
function computeVideoSizeAndPosition() {
const { width, height } = video.getBoundingClientRect();
const videoPlaceholder = $("#video-placeholder");
videoPlaceholder.css("width", width);
videoPlaceholder.css("height", height);
topOffset = videoPlaceholder.position().top;
}
function updateVideoPosition() {
if ($(window).scrollTop() >= topOffset) {
$(video).css("position", "absolute");
$(video).css("left", "0px");
$(video).css("top", topOffset);
} else {
$(video).css("position", "fixed");
$(video).css("left", "0px");
$(video).css("top", "0px");
}
}
function onResize() {
computeVideoSizeAndPosition();
updateVideoPosition();
}
onResize();
//Initialize video effect wrapper
$(document).ready(function () {
//If .first text-element is set, place it in bottom of
//text-display
if ($("#video-effect-wrapper .text.first").length) {
//Get text-display position properties
let textDisplay = $("#video-effect-wrapper #text-display");
let textDisplayPosition = textDisplay.offset().top;
let textDisplayHeight = textDisplay.height();
let textDisplayBottom = textDisplayPosition + textDisplayHeight;
//Get .text.first positions
let firstText = $("#video-effect-wrapper .text.first");
let firstTextHeight = firstText.height();
let startPositionOfFirstText = textDisplayBottom - firstTextHeight + 50;
//Set start position of .text.first
firstText.css("margin-top", startPositionOfFirstText);
}
});
//Code to launch video-effect when user scrolls
$(document).scroll(function () {
//Calculate amount of pixels there is scrolled in the video-effect-wrapper
let n = $(window).scrollTop() - $("#video-effect-wrapper").offset().top + 408;
n = n < 0 ? 0 : n;
//If .text.first is set, we need to calculate one less text-box
let x = $("#video-effect-wrapper .text.first").length == 0 ? 0 : 1;
//Calculate how many percent of the video-effect-wrapper is currenlty scrolled
let percentage = n / ($(".text").eq(1).outerHeight(true) * ($("#video-effect-wrapper .text").length - x)) * 100;
//console.log(percentage);
//console.log(percentage);
//Get duration of video
let duration = video.duration;
//Calculate to which second in video we need to go
let skipTo = duration / 100 * percentage;
//console.log(skipTo);
//Skip to specified second
video.currentTime = skipTo;
//Only allow text-elements to be visible inside text-display
let textDisplay = $("#video-effect-wrapper #text-display");
let textDisplayHeight = textDisplay.height();
let textDisplayTop = textDisplay.offset().top;
let textDisplayBottom = textDisplayTop + textDisplayHeight;
$("#video-effect-wrapper .text").each(function (i) {
let text = $(this);
if (text.offset().top < textDisplayBottom && text.offset().top > textDisplayTop) {
let textProgressPoint = textDisplayTop + (textDisplayHeight / 2);
let textScrollProgressInPx = Math.abs(text.offset().top - textProgressPoint - textDisplayHeight / 2);
textScrollProgressInPx = textScrollProgressInPx <= 0 ? 0 : textScrollProgressInPx;
let textScrollProgressInPerc = textScrollProgressInPx / (textDisplayHeight / 2) * 100;
//console.log(textScrollProgressInPerc);
if (text.hasClass("first"))
textScrollProgressInPerc = 100;
text.css("opacity", textScrollProgressInPerc / 100);
} else {
text.css("transition", "0.5s ease");
text.css("opacity", "0");
}
});
updateVideoPosition();
});
นี่คือ HTML:
<div id="video-effect-wrapper">
<video muted autoplay>
<source src="https://ndvibes.com/test/video/video.mp4" type="video/mp4" id="video">
</video>
<div id="text-display"/>
<div class="text first">
Scroll down to test this little demo
</div>
<div class="text">
Still a lot to improve
</div>
<div class="text">
So please help me
</div>
<div class="text">
Thanks! :D
</div>
</div>
<div id="video-placeholder">
</div>
<div id="other-parts-of-website">
<p>
Normal scroll behaviour wanted.
</p>
<p>
Normal scroll behaviour wanted.
</p>
<p>
Normal scroll behaviour wanted.
</p>
<p>
Normal scroll behaviour wanted.
</p>
<p>
Normal scroll behaviour wanted.
</p>
<p>
Normal scroll behaviour wanted.
</p>
</div>
คุณสามารถลองที่นี่: https://jsfiddle.net/crkj1m0v/3/