Recalculate scrolling div position when used in a clipping path - javascript

I am using clipping paths to change my logo colour base on the background colour.
In addition to this the logo scrolls from top to bottom based on the users vertical position on the page. Top of page = logo at top, bottom of page = logo at bottom etc.
Unfortunately when I added the clipping paths the logos lost their scroll position and after the first one, do not work at all.
Is there a way around this? Also, the logo position was a little off to start with so if there is any way of addressing this at the same time.
You can see the original question here:
div position based on scroll position
I have tried this, but I can't seem to get it to work.
Scroll position lost when hiding div
I am using Advanced Custom Fields and each sections PHP file has this in the header as part of the clipping path using either the white or dark version of the logo accordingly. Its parent is positioned relatively and its child absolutely.
div class="logo-scroll">
<div class="scroll-text">
<img width="53px" height="260px" src="/wp-content/uploads/2019/07/sheree-walker-web-design-edinburgh-vertical-01.svg"/>
</div>
</div>
The Javascript
const docHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
const logo = document.querySelector('.scroll-text');
const logoHeight = logo.offsetHeight;
// to get the pseudoelement's '#page::before' top we use getComputedStyle method
const barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let viewportHeight, barHeight, maxScrollDist, currentScrollPos, scrollFraction;
logo.style.top = barTopMargin + 'px';
window.addEventListener('load', update);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
setSizes();
function update() {
currentScrollPos = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
scrollFraction = currentScrollPos / (docHeight - viewportHeight);
logo.style.top = barTopMargin + (scrollFraction * maxScrollDist) + 'px';
}
function setSizes() {
viewportHeight = window.innerHeight;
// to get the pseudoelement's '#page::before' height we use getComputedStyle method
barHeight = parseInt(getComputedStyle(document.querySelector('#page'), '::before').height);
maxScrollDist = barHeight - logoHeight;
update();
}
The CSS
.logo-scroll .scroll-text img {
padding: 0 6px 0 17px;
}
#page::before {
content: "";
position: fixed;
top: 30px;
bottom: 30px;
left: 30px;
right: 30px;
border: 2px solid white;
pointer-events: none;
-webkit-transition: all 2s; /* Safari prior 6.1 */
transition: all 2s;
}
.logo-scroll {
position: fixed;
left: 30px;
top: 30px;
bottom: 30px;
border-right: 2px solid white;
width: 75px;
z-index: 10;
}
.scroll-text {
position: fixed;
}

let logos, logoHeight, barTopMargin;
let viewportHeight;
window.addEventListener('load', init);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
function init(lockUpdate) {
logos = document.querySelectorAll('.scroll-text');
setSizes(lockUpdate);
}
function update() {
// ensure initialization and prevent recursive call
if (!logos) init(true);
//*************************************************
/**************************************************
THIS LINE MUST BE HERE.
**************************************************/
let maxScrollDist = document.documentElement.scrollHeight - viewportHeight;
//*************************************************
let currentScrollPos = document.documentElement.scrollTop;
let newTop;
let middle = currentScrollPos + viewportHeight/2;
let middleY = maxScrollDist/2;
if (middle >= (maxScrollDist+viewportHeight)/2) {
let p = (middleY - Math.floor(middle - (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = viewportHeight/2 - logoHeight/2;
newTop += (100-p)*(viewportHeight/2)/100;
newTop -= (100-p)*(barTopMargin +logoHeight/2)/100;
newTop = Math.max(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
} else {
let p = (middleY - Math.floor(-middle + (maxScrollDist+viewportHeight)/2))*100/middleY;
newTop = barTopMargin*(100-p)/100+(viewportHeight/2 - (logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, viewportHeight/2 - logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
function setSizes(lockUpdate) {
logoHeight = logos[0].offsetHeight;
barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
viewportHeight = window.innerHeight;
if (lockUpdate === true) return;
update();
}
updated and tested.
to check it put this code in your console:
document.removeEventListener('scroll', update);
document.onscroll = function() {
let _logoHeight = logos[0].offsetHeight;
let _barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let _viewportHeight = window.innerHeight;
let _maxScrollDist = document.documentElement.scrollHeight - _viewportHeight;
let currentScrollPos = document.documentElement.scrollTop;
let percent100 = currentScrollPos + _viewportHeight;
let scrolledPercent = currentScrollPos * 100/_maxScrollDist;
let newTop = ((_viewportHeight - _logoHeight/2)*scrolledPercent/100);
let middle = currentScrollPos + _viewportHeight/2;
let middleY = _maxScrollDist/2; // 100
if (middle >= (_maxScrollDist+_viewportHeight)/2) {
let y1 = middleY - Math.floor(middle - (_maxScrollDist+_viewportHeight)/2);
let p = y1*100/middleY;
newTop = _viewportHeight/2 - _logoHeight/2;
newTop += (100-p)*(_viewportHeight/2)/100;
newTop -= (100-p)*(30 +_logoHeight/2)/100;
newTop = Math.max(newTop, _viewportHeight/2 - _logoHeight/2); /*fix*/
} else {
let y2 = middleY - Math.floor(-middle + (_maxScrollDist+_viewportHeight)/2);
let p = y2*100/middleY;
newTop = 30*(100-p)/100+(_viewportHeight/2 - (_logoHeight/2)*p/100 )*p/100;
newTop = Math.min(newTop, _viewportHeight/2 - _logoHeight/2); /*fix*/
}
logos.forEach(function(el) {
el.style.top = newTop + "px";
});
}
CSS fix:
custom.css :: line 767
#media (max-width: 1000px)...
.scroll-text {
padding-left: 13px;
/*width: 27px;*/
}
.scroll-text img {
/* remove it. but if necessary move it to .scroll-text rule above
width: 27px; */
}
custom.css :: line 839
#media (max-width: 599px)...
.logo-scroll {
/*display: none; why! remove it*/
}
custom.css :: line 268
.scroll-text {
position: fixed;
/* height: 280px; remove it*/
padding-left: 20px;
}
see this capture
finally, have a nice day and goodby.

You are selecting only the first 'logo-text'. Instead of:
const logo = document.querySelector('.scroll-text');
You should use querySelectorAll:
const logos = document.querySelectorAll('.scroll-text');
Then, in your scroll handler, you should move them all.
So, you would then substitute each instance of usage of logo with a loop through the all the logo elements:
logos.forEach(logo => logo.style.top = ...);
Please be aware that you are doing quite expensive stuff in a scroll handler, which is not great for rendering performance. You might also want to use requestAnimationFrame to improve the rendering performance. Check out the reference page on MDN. Actually, I quickly whipped a version using requestAnimationFrame but there was no sensible performance improvement. This is probably due to the fact that apparently rAF fires roughly at the same rate than the scroll event. Anyway, I removed it to avoid confusion. If you detect performance issues, let me know.
I suggest, though, that you move the logo using transform: translate() rather than top. Here you have a complete solution. I tried it in Chrome.
const docHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
const logos = document.querySelectorAll('.scroll-text');
const logoHeight = logos[0].offsetHeight;
// to get the pseudoelement's '#page::before' top we use getComputedStyle method
const barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let viewportHeight, barHeight, maxScrollDist, currentScrollPos, scrollFraction;
window.addEventListener('load', update);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
setSizes();
function update() {
currentScrollPos = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
scrollFraction = currentScrollPos / (docHeight - viewportHeight);
const translateDelta = barTopMargin + (scrollFraction * maxScrollDist);
logos.forEach(logo => logo.style.transform = `translateY(${translateDelta}px)`);
}
function setSizes() {
viewportHeight = window.innerHeight;
// to get the pseudoelement's '#page::before' height we use getComputedStyle method
barHeight = parseInt(getComputedStyle(document.querySelector('#page'), '::before').height);
maxScrollDist = barHeight - logoHeight;
update();
}

Related

Fade in opacity on fixed div during scroll up

I'm trying to make a fixed div fade in when the page scrolls. Using the code bellow for another div class and it works perfectly, however this fades out and I need to reverse it so the div fades in.
var turn = document.getElementById('turn');
window.addEventListener('scroll', function(e) {
// http://stackoverflow.com/a/28633515/962603
var scroll = window.pageYOffset || document.documentElement.scrollTop ||
document.body.scrollTop || 0;
turn.style.opacity = Math.max(0.1, Math.min(1, -scroll / 400 + 2));
});
I have tried swapping the min and max value but doesn't seem to work. Thanks!
Example with fixed div and fade in opacity during scroll:
var turn = document.getElementById('turn');
updateOpacity(turn, 0);
window.addEventListener('scroll', function(e) {
var limit = document.body.scrollHeight - window.innerHeight;
var scroll = window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop || 0;
updateOpacity(turn, scroll / limit);
randomFontColor(turn); // optional
});
function updateOpacity(element, opacity) {
element.style.opacity = opacity;
}
function randomFontColor(element) {
var randColor = Math.floor(Math.random() * 9999).toString();
var hexColor = "#" + randColor.padStart(6, 'FF');
element.style.color = hexColor;
}
body {
height: 2600px;
}
#turn {
left: 6%;
width: 88%;
position: fixed;
}
.highlight {
color: yellow;
line-height: 42px;
background: black;
font-weight: bold;
text-align: center;
}
<div id="turn" class="highlight">
Fixed top div with Fade in Effect on Scroll
</div>

Making a box move smoothly and slowly to a set position in JavaScript

<html>
<div id="my_box_realtime" style="background-color: red; position: absolute; min-width: 100px; min-height: 100px"></div>
<script type="text/javascript">
var x = bottom
var y = right
var d = document.getElementById('my_box_realtime');
var position = 0;
setInterval(function() {}, 500)
position += 1;
d.style.top = position + 'px';
d.style.left = position + 'px';
function my_box_realtime() {
if (position)
}
</script>
</html>
The box needs to move smoothly and slowly to a set coordinate of bottom 0 and right 0.
Any help would be great. Very new to this and it's an assignment I have.
For your problem, we need to:
maintain top & left values separately
Check on Every Interval if the Boxes Bottom & Right have reached the Target Distance in relation to the window
Update the Top & Left Values if necessary
Finally, when the Box is at the Target Location, Clear the Interval.
var d = document.getElementById('my_box_realtime');
var x = 0; // Bottom Target in px
var y = 0; // Right Target in px
var positionTop = 0;
var positionLeft = 0;
let interval = setInterval(function() {
const {
bottom,
right
} = d.getBoundingClientRect();
const clientW = window.innerWidth;
const clientH = window.innerHeight;
if (clientH - bottom !== x) {
positionTop += 1;
d.style.top = positionTop + 'px';
}
if (clientW - right !== y) {
positionLeft += 1;
d.style.left = positionLeft + 'px';
}
if (right === clientW && bottom === clientH) {
clearInterval(interval);
}
}, 50);
<div id="my_box_realtime" style="background-color: red; position: absolute; min-width: 100px; min-height: 100px"></div>

alert when scrollbar is at 75%

document.onscroll = function()
{
if (document.documentElement.scrollTop + window.innerHeight > document.documentElement.scrollHeight * 0.75)
{
var height = document.documentElement.height;
document.documentElement.height = height + 500;
alert('75%');
}
};
html {
height: 1000px;
}
I want this code to run every time the scrollbar reaches 75% of its height to add 500px then repeat the function when it reaches the 75% again.
The code works on my environment but if I scrolled fast I get multiple alerts and the 500px gets added more than once.
I tried the following:
document.documentElement.scrollTop + window.innerHeight == document.documentElement.scrollHeight * 0.75
but it never alerted.
There is no such height property on documentElement object. To update the height you have to access style property documentElement.
Try this.
document.onscroll = function()
{
if (document.documentElement.scrollTop + window.innerHeight > document.documentElement.scrollHeight * 0.75)
{
var height = document.documentElement.style.height;
document.documentElement.style.height = (+height.slice(0,-2) + 500) + "px";
alert('75%');
}
};
<!DOCTYPE html>
<html style="height: 1600px">
</html>
Hope this helps :).
EDIT: Attached screenshots.
Use console for better results
var sw_75 = false;
window.onscroll = function () {
if (sw_75) return;
if (document.documentElement.scrollTop + window.innerHeight > document.documentElement.scrollHeight * 0.75) {
console.log('75%');
sw_75 = true;
var body = document.getElementsByTagName('body')[0];
var height = parseInt(body.style.height.replace("px", ""));
body.style.height = (height + 500) + "px";
sw_75 = false;
}
};
html, body {
margin: 0px;
padding: 0px;
}
<body style="height:1000px;"></body>

Jquery transition Opacity based on var value

I am trying to animate an opacity value from 0 to 1, based on the scroll position within the viewport height. The code below sets variables for windowHeight and scrollTop, which can be combined to calculate percentageScrolled (0–100) of the viewport height. Based on this I am able to switch CSS values at set points, but instead I want to gradually change the opacity from 0–100 of percentageScrolled.
How can I adjust the code below to transition/animate the opacity?
Thanks.
$(window).on('scroll', function(){
// Vars
var windowHeight = $(window).height();
var scrollTop = $(this).scrollTop();
var percentageScrolled = (scrollTop*100)/windowHeight;
if( percentageScrolled < 100 ) {
$('.colour-overlay').css('opacity', '1');
} else {
$('.colour-overlay').css('opacity', '0');
}
});
You can remove the if and set the opacity to the percentage divided by 100
$(window).on('scroll', function() {
// Vars
var windowHeight = $(window).height();
var scrollTop = $(this).scrollTop();
$('.colour-overlay').css('opacity', scrollTop / windowHeight);
});
.colour-overlay {
display: block;
width: 20px;
height: 1200px;
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="colour-overlay"></div>
$(‘.colour-overlay’).css(opacity, percentageScrolled / 100);
Instead of if else statement.
Also as a general advice try to avoid using var, use const or let instead and if your project doesnt depend on jquery try to avoid it too.
const overlays = document.querySelectorAll(‘.colour-overlay’);
window.addEventListener('scroll', () => {
const windowHeight = window.offsetHeight;
const scrollTop = window.scrollTop;
const percentageScrolled = (scrollTop * 100) / windowHeight;
for (const overlay of overlays) {
overlay.style.opacity = percentageScrolled / 100;
}
});
This would be the pure js solution.
Dont know if i understood you right, but a wrote an example have a look.
$(document).on('scroll', function(){
// Vars
// use body instead of window, body will return the right height where window will return the view size
var windowHeight = $("body").height();
var scrollTop = $(this).scrollTop();
var percentageScrolled = Math.abs((((scrollTop / windowHeight) * 100) / 100 ));
$('.colour-overlay').css('opacity', percentageScrolled);
});
.colour-overlay{
background:red;
height:1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="colour-overlay"></div>

calculate centered absolute div inside relative

I'm using 2 Javscript methods to position a hovering button inside a static element on my page. The button that is centered is inputted inside the first element and uses position absolute. The code I'm using to get the parent element measurements:
// calculate if the element is in the visible window
function elementVisibleRect(element) {
element = $(element);
var rect = {
top: Math.round(element.offset().top),
left: Math.round(element.offset().left),
width: Math.round(element.outerWidth()),
height: Math.round(element.outerHeight())
};
var scrollTop = $(window).scrollTop();
var windowHeight = $(window).height();
var scrollBottom = scrollTop + windowHeight;
var elementBottom = Math.round(rect.height + rect.top);
if (scrollTop < rect.top && scrollBottom > elementBottom) {
return rect;
}
if (scrollTop > rect.top) {
rect.top = scrollTop;
}
if (scrollBottom < elementBottom) {
rect.height = scrollBottom - rect.top;
} else {
rect.height = windowHeight - (scrollBottom - elementBottom);
}
return rect;
}
and for using this information and centering the button inside
// center the element based on visible screen-frame
function elementPosition (element) {
var visibleRect = elementVisibleRect(element);
$('.editHoverButton').css({
top: visibleRect.top + ((visibleRect.height / 2) - ($('.editHoverButton').outerHeight() / 2)),
left: visibleRect.left + (visibleRect.width / 2) - ($('.editHoverButton').outerWidth() / 2)
});
}
Now my problem is that a third party library requires the parent DIV to change position from the browser default "static" to "relative" which breaks my calculations in the second function.
It might be late, but no matter what I try I can't seem to figure out how to get this working for when the parent element has position set to relative. I can't seem to get the maths quite right, and my head is beginning to hurt. Any suggestions?
EDIT - ADDED JSFIDDLE
http://jsfiddle.net/RhTY6/
Elements with absolute positioning are removed from the natural flow (e.g. they don't leave a space where they were) and positioned relative to their first parent with non-static positioning. Since the positioning of the right-hand box is relative (not static), you can position the button with top: 50%; and left: 50%;. This will make the top-left corner at the center of the parent. Then all you have to do is subtract half the element's height and width from the position, using margin-top and margin-left. This is much simpler than what you were doing, as you can see below:
JavaScript:
function elementPosition() {
$('.editHoverButton').css('margin-top', 0 - $('.editHoverButton').outerHeight() / 2);
$('.editHoverButton').css('margin-left', 0 - $('.editHoverButton').outerWidth() / 2);
};
CSS:
.editHoverButton {
position: absolute;
z-index: 99;
font-family: sans-serif;
font-size: 14px;
font-weight: normal;
background-color: #00bb00;
top: 50%;
left: 50%;
}
Nothing else has to change except to remove this from the elementPosition() function.
DEMO (Notice that the left one no longer works. This is because it is positioned static.)
EDIT--Using the same basic idea, this method should work:
The problem is that you have take the top and left positions of the element when defining rect. on the positioning calculations. Changing those to 0 (not the best method, but it works) fixes the problem for relative elements.
DEMO (Notice that the left one now does work. This is because it is positioned at 0,0 anyway.)
EDIT--This will work when the page scrolls:
This sets the container in a variable so that when the page scrolls, it can be repositioned automatically.
DEMO
EDIT: made it worked with your CSS and HTML (relative and absolute positioning) by altering the Script only.
The horizontal axis calcs were completely missing (I've applied the same calcs you applied to the vertical axis).
I've added some data and a ruler to help you finish the job: as you can see, it is (and it was, in your original fiddle) not perfectly centered (obviously you need to look at it when the container is smaller than the viewport), but this will be easy to work out.
Running Demo
Try to resize the fiddle window and to scroll both vertically and horizontally to see it works.
function elementVisibleRect(element) {
$("#data").html("");
element = $(element);
var rect = {
top: Math.round(element.offset().top),
left: Math.round(element.offset().left),
width: Math.round(element.outerWidth()),
height: Math.round(element.outerHeight())
};
var scrollTop = $(window).scrollTop();
var windowHeight = $(window).height();
var scrollBottom = scrollTop + windowHeight;
var elementBottom = Math.round(rect.height + rect.top);
var scrollLeft = $(window).scrollLeft();
var windowWidth = $(window).width();
var scrollRight = scrollLeft + windowWidth;
var elementRight = Math.round(rect.width + rect.left);
addData("rect.top", rect.top);
addData("rect.left", rect.left);
addData("rect.width", rect.width);
addData("rect.height", rect.height);
addData("scrollTop", scrollTop);
addData("windowHeight", windowHeight);
addData("scrollBottom", scrollBottom);
addData("elementBottom", elementBottom);
addData("scrollLeft", scrollLeft);
addData("windowWidth", windowWidth);
addData("scrollRight", scrollRight);
addData("elementRight", elementRight);
if (rect.top < scrollTop) {
rect.top = scrollTop;
}
if (scrollBottom < rect.top < scrollTop) {
rect.top = scrollTop;
}
if (scrollBottom < elementBottom) {
rect.height = scrollBottom - rect.top;
} else {
rect.height = windowHeight - (scrollBottom - elementBottom);
}
if (rect.left < scrollLeft) {
rect.left = scrollLeft;
}
if (scrollRight < rect.left < scrollLeft) {
rect.left = scrollLeft;
}
if (scrollRight < elementRight) {
rect.width = scrollRight - rect.left;
} else {
rect.width = windowWidth - (scrollRight - elementRight);
}
return rect;
}

Categories