Сколько элементов видно в окне просмотра
На странице есть div
(коричневый прямоугольник). Страница выше, чем область просмотра (оранжевый прямоугольник), поэтому ее можно прокручивать, что означает, что div
может отображаться только частично или не отображаться вообще.
![element visibility]()
Какой самый простой алгоритм, чтобы сказать, сколько% div
видно в окне просмотра?
(Чтобы упростить задачу, div
всегда вписывается в область просмотра горизонтально, поэтому при расчетах необходимо учитывать только ось Y).
Ответы
Ответ 1
См. еще один пример в скрипте:
https://jsfiddle.net/1hfxom6h/3/
/*jslint browser: true*/
/*global jQuery, window, document*/
(function ($) {
'use strict';
var results = {};
function display() {
var resultString = '';
$.each(results, function (key) {
resultString += '(' + key + ': ' + Math.round(results[key]) + '%)';
});
$('p').text(resultString);
}
function calculateVisibilityForDiv(div$) {
var windowHeight = $(window).height(),
docScroll = $(document).scrollTop(),
divPosition = div$.offset().top,
divHeight = div$.height(),
hiddenBefore = docScroll - divPosition,
hiddenAfter = (divPosition + divHeight) - (docScroll + windowHeight);
if ((docScroll > divPosition + divHeight) || (divPosition > docScroll + windowHeight)) {
return 0;
} else {
var result = 100;
if (hiddenBefore > 0) {
result -= (hiddenBefore * 100) / divHeight;
}
if (hiddenAfter > 0) {
result -= (hiddenAfter * 100) / divHeight;
}
return result;
}
}
function calculateAndDisplayForAllDivs() {
$('div').each(function () {
var div$ = $(this);
results[div$.attr('id')] = calculateVisibilityForDiv(div$);
});
display();
}
$(document).scroll(function () {
calculateAndDisplayForAllDivs();
});
$(document).ready(function () {
calculateAndDisplayForAllDivs();
});
}(jQuery));
div {
height:200px;
width:300px;
border-width:1px;
border-style:solid;
}
p {
position: fixed;
left:320px;
top:4px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div1">div1</div>
<div id="div2">div2</div>
<div id="div3">div3</div>
<div id="div4">div4</div>
<p id="result"></p>
Ответ 2
Поиграв немного, я думаю, что я нашел, пожалуй, самый простой способ сделать это: я в основном определяю, насколько элемент распространяется на область просмотра (неважно, в каком направлении), и на основании этого это может быть легко вычислил, сколько из них видно.
// When the page is completely loaded.
$(document).ready(function() {
// Returns in percentages how much can be seen vertically
// of an element in the current viewport.
$.fn.pvisible = function() {
var eTop = this.offset().top;
var eBottom = eTop + this.height();
var wTop = $(window).scrollTop();
var wBottom = wTop + $(window).height();
var totalH = Math.max(eBottom, wBottom) - Math.min(eTop, wTop);
var wComp = totalH - $(window).height();
var eIn = this.height() - wComp;
return (eIn <= 0 ? 0 : eIn / this.height() * 100);
}
// If the page is scrolled.
$(window).scroll(function() {
// Setting the opacity of the divs.
$("div").each(function() {
$(this).css("opacity", Math.round($(this).pvisible()) / 100);
});
});
});
html,
body {
width: 100%;
height: 100%;
}
body {
background-color: rgba(255, 191, 127, 1);
}
div {
width: 60%;
height: 30%;
margin: 5% auto;
background-color: rgba(175, 153, 131, 1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
Ответ 3
Вот фрагмент, иллюстрирующий, как вы можете это рассчитать.
Я поместил значения% в поля для удобочитаемости, и он даже вроде "следует" в окне просмотра ^^:
версия скрипта
function listVisibleBoxes() {
var results = [];
$("section").each(function () {
var screenTop = document.documentElement.scrollTop;
var screenBottom = document.documentElement.scrollTop + $(window).height();
var boxTop = $(this).offset().top;
var boxHeight = $(this).height();
var boxBottom = boxTop + boxHeight;
if(boxTop > screenTop) {
if(boxBottom < screenBottom) {
//full box
results.push(this.id + "-100%");
$(this).html("100%").css({ "line-height": "50vh" });
} else if(boxTop < screenBottom) {
//partial (bottom)
var percent = Math.round((screenBottom - boxTop) / boxHeight * 100) + "%";
var lineHeight = Math.round((screenBottom - boxTop) / boxHeight * 50) + "vh";
results.push(this.id + "-" + percent);
$(this).html(percent).css({ "line-height": lineHeight });
}
} else if(boxBottom > screenTop) {
//partial (top)
var percent = Math.round((boxBottom - screenTop) / boxHeight * 100) + "%";
var lineHeight = 100 - Math.round((boxBottom - screenTop) / boxHeight * 50) + "vh";
results.push(this.id + "-" + percent);
$(this).html(percent).css({ "line-height": lineHeight });
}
});
$("#data").html(results.join(" | "));
}
$(function () {
listVisibleBoxes();
$(window).on("scroll", function() {
listVisibleBoxes();
});
});
body {
background-color: rgba(255, 191, 127, 1);
font-family: Arial, sans-serif;
}
section {
background-color: rgba(175, 153, 131, 1);
height: 50vh;
font-size: 5vh;
line-height: 50vh;
margin: 10vh auto;
overflow: hidden;
text-align: center;
width: 50vw;
}
#data {
background-color: rgba(255, 255, 255, .5);
left: 0;
padding: .5em;
position: fixed;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="one"></section>
<section id="two"></section>
<section id="three"></section>
<section id="four"></section>
<section id="five"></section>
<section id="six"></section>
<div id="data">data here</div>
Ответ 4
Я искал Google, и этот поток был первым в списке. Причина, из-за которой он снова превзошел, - это то, что другие, подобные мне в поиске того, как сделать эту проверку, имеют быструю информацию о более современном решении: IntersectionObserver.
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
Ответ 5
Chrome теперь поддерживает Intersection Observer API:
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
Пример (TypeScript):
export const elementVisibleInPercent = (element: HTMLElement) => {
return new Promise((resolve, reject) => {
const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
entries.forEach((entry: IntersectionObserverEntry) => {
resolve(Math.floor(entry.intersectionRatio * 100));
clearTimeout(timeout);
observer.disconnect();
});
});
observer.observe(element);
// Probably not needed, but in case something goes wrong.
const timeout = setTimeout(() => {
reject();
}, 500);
});
};
Пример (JavaScript):
export const elementVisibleInPercent = (element) => {
return new Promise((resolve, reject) => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
resolve(Math.floor(entry.intersectionRatio * 100));
clearTimeout(timeout);
observer.disconnect();
});
});
observer.observe(element);
// Probably not needed, but in case something goes wrong.
const timeout = setTimeout(() => {
reject();
}, 500);
});
};