根据背景颜色滚动时更改 SVG 填充

时间:2021-05-24 19:15:55

标签: javascript html css reactjs

我正在构建一个 React 网站,该网站在滚动时使用 section 向上滑动 position: sticky 面板,但在使用 fixed 元素及其 color 时遇到问题section background-color

这是问题的一个简单片段。

html {
  font-family: sans-serif;
  font-size: 20px;
  font-weight: 700;
  text-transform: uppercase;
}

*,
 ::after,
 ::before {
  box-sizing: border-box;
}

body {
  margin: 0;
  overflow-x: hidden;
}

.nav {
  position: fixed;
  width: 100;
  top: 0;
  left: 0;
  right: 0;
  padding: 24px 28px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

svg {
  fill: black; // Change to white over dark backgrounds
}

.sticky {
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  position: sticky;
  top: -1px;
}

.dark {
  background-color: #1e1e1e;
  color: #fff;
}
<div class="nav"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="32px" id="Layer_1" style="enable-background:new 0 0 32 32;" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve"><path d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2  s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2  S29.104,22,28,22z"/></svg></div>
<main>
  <div class="sticky">Section Light</div>
  <div class="sticky dark">Section Dark</div>
</main>

我看到了其他示例,当重叠元素定位为 absolute 时,但是由于每个 sticky 面板使用的 section 位置,它们在这种情况下不起作用。此外,由于某些部分使用背景图像,因此无法使用 mix-blend-mode,这看起来很糟糕。

其次,网站是用 GatsbyReact 构建的,我可以访问每个部分的背景颜色。

在不同的背景颜色上滚动时如何更改 menu svg 的颜色?我只想要白色或黑色。

1 个答案:

答案 0 :(得分:1)

我们可以使用intersectionObserver来告诉我们新部分何时靠近视口顶部并相应地更改菜单图标的颜色。相反,当某个部分从顶部附近出来时,根据其前一个兄弟元素(即下面的那个)的暗状态更改菜单。

这段代码表明:

const svg = document.querySelector('svg');
let callback = (entries, observer) => {
  entries.forEach(entry => {
  //List from MDN:
    // Each entry describes an intersection change for one observed
    // target element:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
    //eventually make the gradient chosen depend on the % overlap and the color of the previous sibling element
    if (entry.isIntersecting) {
      svg.style.fill = (entry.target.classList.contains('dark')) ? 'url(#white)' : 'url(#black)';
    }
    else {
      svg.style.fill = (entry.target.previousElementSibling.classList.contains('dark')) ? 'url(#white)' : 'url(#black)';
    }
  });
};
let options = {
  threshold: [ 0.9, 0.94, 0.98 ] // need to work on these thresholds
}

let observer = new IntersectionObserver(callback, options);
const stickies = document.querySelectorAll('main div.sticky');
stickies.forEach( sticky => {
  observer.observe(sticky);
});
html {
  font-family: sans-serif;
  font-size: 20px;
  font-weight: 700;
  text-transform: uppercase;
}

*,
 ::after,
 ::before {
  box-sizing: border-box;
}

body {
  margin: 0;
  overflow-x: hidden;
}

.nav {
  position: fixed;
  width: 100;
  top: 0;
  left: 0;
  right: 0;
  rpadding: 24px 28px;
  padding: 2vh 28px;
  rpadding: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  z-index: 999;
}

svg {
  fill: black;
}

.sticky {
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  position: sticky;
  top: -1px;
  background-color: white;
}

.dark {
  background-color: #1e1e1e;
  color: #fff;
}
<div class="nav">
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="32px" id="Layer_1" style="enable-background:new 0 0 32 32;" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve">
  <defs>
    <linearGradient id="black" x1="0%" y1="0%" x2="100%" y2="0%"  gradientTransform="rotate(90)">
      <stop offset="0%"   stop-color="#000"/>
      <stop offset="100%" stop-color="#000"/>
    </linearGradient>
    <linearGradient id="white" x1="0%" y1="0%" x2="100%" y2="0%"  gradientTransform="rotate(90)">
      <stop offset="0%"   stop-color="#fff"/>
      <stop offset="100%" stop-color="#fff"/>
    </linearGradient>
  </defs>
    <path d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2  s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2  S29.104,22,28,22z"/>
    </svg>
</div>
<main>
  <div class="sticky">Section 1 Light</div>
  <div class="sticky dark">Section 2 Dark</div>
  <div class="sticky">Section 3 Light</div>
  <div class="sticky">Section 4 Light</div>
  <div class="sticky">Section 5 Light</div>
  <div class="sticky dark">Section 6 Dark</div>
</main>

为了让菜单图标栏随着部分的上升或下降而改变,我们可以感知重叠的比例,并使用适当的线性渐变设置 svg 填充颜色 - 具有三分之一、三分之二和完全填充的线性渐变。上面的代码片段设置了阈值并开始使用线性梯度,但它并不完整 - 必须进行更多的计算才能使 %s 正确。我希望在某个时候有时间回到这个话题。

请注意,为了在具有不同纵横比的视口上进行此观察,导航的顶部填充已更改为相对值。

相关问题