Beim Reverse Engineering auf der Airpods Pro-Seite stellen wir fest, dass in der Animation nicht a video
, sondern a verwendet wird canvas
. Die Implementierung ist wie folgt:
- Laden Sie ungefähr 1500 Bilder über HTTP2 vor, eigentlich die Frames der Animation
- Erstellen Sie ein Array von Bildern in Form von
HTMLImageElement
- Reagieren Sie auf jedes
scroll
DOM-Ereignis und fordern Sie einen Animationsrahmen an, der dem nächstgelegenen Bild entsprichtrequestAnimationFrame
- Zeigen Sie im Anforderungsrückruf des Animationsrahmens das Bild an, indem Sie
ctx.drawImage
( ctx
als 2d
Kontext des canvas
Elements) verwenden.
Die requestAnimationFrame
Funktion soll Ihnen dabei helfen, einen gleichmäßigeren Effekt zu erzielen, da die Bilder verschoben und mit der Rate "Bilder pro Sekunde" des Zielbildschirms synchronisiert werden.
Weitere Informationen zum ordnungsgemäßen Anzeigen eines Frames in einem Bildlaufereignis finden Sie unter: https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event
Abgesehen von Ihrem Hauptproblem habe ich jedoch eine funktionierende Lösung, die besteht aus:
- Erstellen eines Platzhalters mit derselben Höhe und Breite wie das
video
Element. Ziel ist es, zu vermeiden, dass das Video den Rest des HTML-Codes überlappt, wenn es auf absolute
Position gesetzt wird
scroll
Wenn der Platzhalter im Ereignis-Rückruf den oberen Rand des Ansichtsfensters erreicht, setzen Sie die Position des Videos auf absolute
und den richtigen top
Wert
Die Idee ist, dass das Video immer außerhalb des Flusses bleibt und im richtigen Moment über dem Platzhalter stattfindet, wenn Sie nach unten scrollen.
Hier ist das 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();
});
Hier ist der 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>
Sie können es hier versuchen: https://jsfiddle.net/crkj1m0v/3/
Wenn Sie möchten, dass das Video beim Scrollen wieder einrastet, müssen Sie die Stelle markieren, an der Sie von
fixed
zu wechselnrelative
.quelle