Reputation: 684
I am trying to change the color of a SVG according to its current section background ...its working with shapes but as soon as i change it to a path it will jump the colors instead of fluid change...why is that?
This is what i want to achieve (fluid transition of colors)
var sections = document.querySelectorAll('section');
var logo = document.querySelector('#logo');
var nextColor = document.querySelector('#nextColor');
var currentColor = document.querySelector('#currentColor');
var initialHeight = logo.clientHeight;
var lastScrollPosition = 0;
// Set current section color
sections.forEach(function (section) {
var top = section.offsetTop - window.pageYOffset;
if (top <= logo.offsetTop && (top + section.clientHeight) >= logo.offsetTop) {
currentColor.setAttribute('fill', section.dataset.color);
}
});
window.addEventListener('scroll', function () {
sections.forEach(function (section) {
var top = section.offsetTop - window.pageYOffset;
var offset = top - logo.offsetTop;
// If the top edge of the section is behind the logo
if (top <= logo.offsetTop) {
// Make sure the section color has its initial height
currentColor.setAttribute('height', initialHeight);
// If the logo is still inside the section, fill it with the section color
var bottom = top + section.clientHeight;
if (bottom >= logo.offsetTop) {
currentColor.setAttribute('fill', section.dataset.color);
}
return;
}
// If logo collides with the lower section
if (offset <= logo.clientHeight) {
nextColor.setAttribute('fill', section.dataset.color);
currentColor.setAttribute('height', offset);
}
});
});
#logo {
position: fixed;
position: fixed;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
}
section {
height: 100vh
}
#dark {
background-color: #2a2a2a;
}
<div id="logo">
<svg height="100" width="100">
<rect id="nextColor" width="100" height="100"/>
<rect id="currentColor" y="0" width="100" height="100"/>
</svg>
</div>
<div class="sections">
<section data-color="#000000" id="light"></section>
<section data-color="#ffffff" id="dark"></section>
<section data-color="#cccccc" id="light2"></section>
</div>
This is what i got but its not transitioning smoothly like the working example above:
var sections = document.querySelectorAll('section');
var logo = document.querySelector('#logo');
var nextColor = document.querySelector('#nextColor');
var currentColor = document.querySelector('#currentColor');
var initialHeight = logo.clientHeight;
var lastScrollPosition = 0;
// Set current section color
sections.forEach(function (section) {
var top = section.offsetTop - window.pageYOffset;
if (top <= logo.offsetTop && (top + section.clientHeight) >= logo.offsetTop) {
currentColor.setAttribute('fill', section.dataset.color);
}
});
window.addEventListener('scroll', function () {
sections.forEach(function (section) {
var top = section.offsetTop - window.pageYOffset;
var offset = top - logo.offsetTop;
// If the top edge of the section is behind the logo
if (top <= logo.offsetTop) {
// Make sure the section color has its initial height
currentColor.setAttribute('height', initialHeight);
// If the logo is still inside the section, fill it with the section color
var bottom = top + section.clientHeight;
if (bottom >= logo.offsetTop) {
currentColor.setAttribute('fill', section.dataset.color);
}
return;
}
// If logo collides with the lower section
if (offset <= logo.clientHeight) {
nextColor.setAttribute('fill', section.dataset.color);
currentColor.setAttribute('height', offset);
}
});
});
#logo {
position: fixed;
position: fixed;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
}
section {
height: 100vh
}
#blue {
background-color: #b4da55;
}
<div id="logo">
<svg height="48" xmlns="http://www.w3.org/2000/svg">
<path height="48" id="nextColor" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5"/>
<path height="48" id="currentColor" y="0" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5"/>
</svg>
</div>
<div class="sections">
<section data-color="#b4da55" id="white"></section>
<section data-color="#ffffff" id="blue"></section>
<section data-color="#b4da55" id="white"></section>
</div>
Upvotes: 2
Views: 237
Reputation: 7403
This can be achieved by clipping the white logo within a rectangle.
Logic is the following:
Updated snipped below. If what happens is not totally clear to you, you may just move the rect
definition outside of clipPath
, this way you will visualize how its position and visibility evolve with the scrolling.
Side note: You may want to debounce the scroll event listener, to maintain a good performance in your page.
var logo = document.querySelector('#logo')
, clipRect = document.querySelector('#clipRect')
, blueSection = document.querySelector('#blue')
window.addEventListener('scroll', function () {
// position of the top of the blue section
var top = blueSection.offsetTop - window.pageYOffset
// position of the bottom of the blue section
, bottom = blueSection.offsetTop + blueSection.clientHeight - window.pageYOffset
// offset between top of the logo and top of the blue section
var offset = top - logo.offsetTop
if ( offset <= logo.clientHeight && bottom >= logo.offsetTop) {
// blue section overlaps with the logo
clipRect.setAttribute('y', Math.max(offset, 0))
clipRect.setAttribute('height', bottom - logo.offsetTop)
}
else {
// blue section does not overlap with the logo
// make sure the clip-path isn't visible
clipRect.setAttribute('y', 0)
clipRect.setAttribute('height', 0)
}
})
#logo {
position: fixed;
position: fixed;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
}
section {
height: 100vh
}
#blue {
background-color: #b4da55;
}
#currentColor {
fill: #b4da55;
}
#nextColor {
fill: white;
}
/*
following useful only for testing purpose
*/
#clipRect {
fill: red;
fill-opacity: .5;
}
<div id="logo">
<svg height="48" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clipping">
<rect id="clipRect" x="0" y="0" width="48" height="0"></rect>
</clipPath>
</defs>
<path height="48" id="currentColor" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5"/>
<path height="48" id="nextColor" d="M5.8 0v36.3h10.7V48l25.6-11.7V0H5.8zm10.5" clip-path="url(#clipping)"/>
</svg>
</div>
<div class="sections">
<section data-color="#b4da55" id="white"></section>
<section data-color="#ffffff" id="blue"></section>
<section data-color="#b4da55" id="white"></section>
</div>
Upvotes: 1
Reputation: 12458
Your code is not using the SVG rectangle shape the way I think that you think it is. To demonstrate this, I changed the rect
s to ellipse
s, and then changed the attributes as appropriate, i.e. y
to cy
, width
to rx
, and height
to ry
. The result shows that the shape is, first of all, only showing a portion of the whole shape (the bottom right quarter?) and, secondly, actually squashing the shape when the section border overlays it. You don't see these things in your original example because a quarter of a rectangle is still a rectangle, and a squashed rectangle is also still a rectangle. Thus, what you think is working in your original example actually isn't working. So the problem is not using the path, it's your original code.
If you only want to use rectangles, you can use your original code as is, I suppose, because it does produce the desired visual effect. However, if you were only using the rectangle as a proof-of-concept and your real end goal is using a path, then you need a different approach. You might consider using clipPath
/clip-path
to "cut" the path into parts as a section border is passing over it, but that would take some significant re-working of your code.
var sections = document.querySelectorAll('section');
var logo = document.querySelector('#logo');
var nextColor = document.querySelector('#nextColor');
var currentColor = document.querySelector('#currentColor');
var initialHeight = logo.clientHeight;
var lastScrollPosition = 0;
// Set current section color
sections.forEach(function (section) {
var top = section.offsetTop - window.pageYOffset;
if (top <= logo.offsetTop && (top + section.clientHeight) >= logo.offsetTop) {
currentColor.setAttribute('fill', section.dataset.color);
}
});
window.addEventListener('scroll', function () {
sections.forEach(function (section) {
var top = section.offsetTop - window.pageYOffset;
var offset = top - logo.offsetTop;
// If the top edge of the section is behind the logo
if (top <= logo.offsetTop) {
// Make sure the section color has its initial height
currentColor.setAttribute('ry', initialHeight);
// If the logo is still inside the section, fill it with the section color
var bottom = top + section.clientHeight;
if (bottom >= logo.offsetTop) {
currentColor.setAttribute('fill', section.dataset.color);
}
return;
}
// If logo collides with the lower section
if (offset <= logo.clientHeight) {
nextColor.setAttribute('fill', section.dataset.color);
currentColor.setAttribute('ry', offset);
}
});
});
#logo {
position: fixed;
position: fixed;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
}
section {
height: 100vh
}
#dark {
background-color: #2a2a2a;
}
<div id="logo">
<svg height="100" width="100">
<ellipse id="nextColor" rx="100" ry="100"/>
<ellipse id="currentColor" cy="0" rx="100" ry="100"/>
</svg>
</div>
<div class="sections">
<section data-color="#000000" id="light"></section>
<section data-color="#ffffff" id="dark"></section>
<section data-color="#cccccc" id="light2"></section>
</div>
Upvotes: 2