Saat melakukan reverse engineering pada halaman Airpods Pro , kami melihat bahwa animasi tidak menggunakan a video
, tetapi a canvas
. Implementasinya adalah sebagai berikut:
- Preload sekitar 1500 gambar melalui HTTP2, sebenarnya bingkai animasi
- Buat array gambar dalam bentuk
HTMLImageElement
- Bereaksi terhadap setiap
scroll
acara DOM dan minta bingkai animasi yang sesuai dengan gambar terdekat, denganrequestAnimationFrame
- Dalam bingkai animasi meminta panggilan balik, tampilkan gambar dengan menggunakan
ctx.drawImage
( ctx
menjadi 2d
konteks canvas
elemen)
The requestAnimationFrame
Fungsi akan membantu Anda mencapai efek halus sebagai frame akan ditangguhkan dan disinkronkan dengan "frame per detik" tingkat layar sasaran.
Untuk informasi lebih lanjut tentang cara menampilkan bingkai pada acara gulir dengan benar, Anda dapat membaca ini: https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event
Yang sedang berkata, mengenai masalah utama Anda, saya memiliki solusi kerja yang terdiri dari:
- Membuat placeholder, dengan tinggi dan lebar yang sama dari
video
elemen. Tujuannya adalah untuk menghindari video tumpang tindih dengan sisa HTML ketika diatur ke absolute
posisi
- Ke
scroll
callback acara, ketika placeholder mencapai puncak viewport, mengatur posisi video untuk absolute
, dan hak top
nilai
Idenya adalah bahwa video selalu tetap keluar dari aliran, dan terjadi di atas penampung pada saat yang tepat ketika menggulir ke bawah.
Ini JavaScriptnya:
//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();
});
Ini HTML-nya:
<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>
Anda dapat mencoba di sini: https://jsfiddle.net/crkj1m0v/3/
Jika Anda ingin video untuk kembali kunci di tempat seperti yang Anda gulir kembali, Anda harus menandai tempat di mana Anda beralih dari
fixed
kerelative
.sumber