“剪裁”背景以在堆叠上下文中查看自身下方

Pri*_*Nom 10 javascript css svg mask clip

[注意:寻找一个跨浏览器的解决方案,在ccprog的回答中看到,在每一波goo之间不会瞬间闪烁身体的背景;理想情况下,解决方案不应涉及等到第一波结束后才开始显示第二波,以便两个波可以同时运行。我愿意放弃动态随机化的 goop 以获得理想的解决方案。]

有谁知道我如何让第二波橙色粘液 ( .goo-two) “切穿”第一波棕色.goo-one粘液( ) 和天蓝色容器 ( .goo-container) 以显示或暴露红色主体元素 ( body) 或者,就此而言,任何在堆叠上下文中它下面的其他元素?是否可以?

值得注意的是,我给容器 ( .goo-container) 一个坚实的背景是因为我用它来掩盖网站其余部分的加载过程,我希望橙色粘液 ( .goo-two) 可以用来显示内容。因为橙色咕开始棕色咕完成,这将是改变contianer(背景完美时空之前淋漓它会变得棘手.goo-container)从skybluetransparent,虽然半透明渐变作为背景可能可以用来仍然实现这个。(要么是那个,要么是完全不同的东西,比如复制橙色层并使用一个来剪辑棕色路径,另一个来剪辑天蓝色层。)

有任何想法吗?

const
  gooCont = document.querySelector('div.goo-container'),
  gooOne = gooCont.querySelector('div.goo-one'),
  gooTwo = gooCont.querySelector('div.goo-two'),
  rand = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min

gooCont.style.setProperty('--translateY', `translateY(-${innerWidth * 0.21 / innerHeight * 100 + 100}%)`)
generateGoo(gooOne)

function generateGoo(goo) {
  const
    randQty = rand(20,30),
    unit = innerWidth / (randQty - 1) / innerWidth * 100
  if (getComputedStyle(goo).display === 'none') goo.style.display = 'block'
  for (let i = 0; i < randQty; i++) {
    const
      div = document.createElement('div'),
      minWidthPx = innerWidth < 500 ? innerWidth * 0.1 : innerWidth * 0.05,
      minMaxWidthPx = innerWidth < 500 ? innerWidth * 0.2 : innerWidth * 0.1,
      widthPx = rand(minWidthPx, minMaxWidthPx),
      widthPerc = widthPx / innerWidth * 100,
      heightPx = rand(widthPx / 2, widthPx * 3),
      heightPerc = heightPx / gooCont.getBoundingClientRect().height * 100,
      translateY = rand(45, 70),
      targetTranslateY = rand(15, 100),
      borderRadiusPerc = rand(40, 50)
    div.style.width = widthPerc + '%'
    div.style.height = heightPerc + '%'
    div.style.left = i * unit + '%'
    div.style.transform = `translate(-50%, ${translateY}%)`
    div.style.borderRadius = borderRadiusPerc + '%'
    div.setAttribute('data-translate', targetTranslateY)
    goo.appendChild(div)
  }
  goo.style.transform = `translateY(0)`
  goo.childNodes.forEach(
    v => v.style.transform = `translateY(${v.getAttribute('data-translate')}%)`
  )
}

setTimeout(() => {
  gooTwo.innerHTML = ''
  generateGoo(gooTwo)
}, 2300)
Run Code Online (Sandbox Code Playgroud)
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  background: red;
}

div.goo-container {
  --translateY: translateY(-165%);
  z-index: 1;
  width: 100%;
  height: 100%;
  position: fixed;
  overflow: hidden;
  background: skyblue;
}

div.goo-container > div.goo-one,
div.goo-container > div.goo-two {
  width: 100%;
  height: 100%;
  position: absolute;
  transform: var(--translateY);
  filter: url('#goo-filter');
  background: #5b534a;
  transition: transform 2.8s linear;
}

div.goo-container > div.goo-one > div,
div.goo-container > div.goo-two > div {
  position: absolute;
  bottom: 0;
  background: #5b534a;
  transition: transform 2.8s linear;
}

div.goo-container > div.goo-two {
  display: none;
  transition: transform 2.8s linear;
}

div.goo-container > div.goo-two,
div.goo-container > div.goo-two > div {
  background: orange;
}

svg {
  /* Prevents effect on Firefox */
  /* display: none; */
}
Run Code Online (Sandbox Code Playgroud)
<div class='goo-container'>
  <div class='goo-one'></div>
  <div class='goo-two'></div>
</div>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1'>
  <defs>
    <filter id='goo-filter'>
      <feGaussianBlur in='SourceGraphic' stdDeviation='10' result='blur' />
      <feColorMatrix in='blur' mode='matrix' values='1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7' result='goo' />
      <feBlend in='SourceGraphic' in2='goo' />
    </filter>
  </defs>
</svg>
Run Code Online (Sandbox Code Playgroud)

ccp*_*rog 7

我很确定这不是最佳变体,但它似乎可行,至少在 Firefox 中是这样。Chrome 在动画每个部分的初始帧方面存在一些问题。

  • 我已经稍微重写了粘性过滤器代码,以提高可读性,同时保持相同的效果。有关解释,请参阅我的文章
  • 只有.goo-one和子 div 获得背景颜色。这使得.goo-two变得透明成为可能。
  • 这两个部分得到不同的过滤器,但过滤器区域垂直增加,以便在过渡开始时到达屏幕底部。
  • 第一个过滤器使用天蓝色作为背景填充。
  • 第二个过滤器有一个棕色填充,但它的应用是相反的:它只显示在粘性区域之外,而内部区域则为空。组成粘性区域的 div 矩形不跨越整个.gooTwo. 为了还填充(并在反转后清空)顶部,需要额外<div class="first">的。
  • 在第二个粘性部分的过渡开始时,过滤区域上限设置在屏幕下边界以下。这将隐藏天蓝色背景,同时第二个粘性部分变得可见。
  • 请注意svg元素CSS 中的细微变化,以提高浏览器兼容性。
  • 作为概念证明,在容器 div 中添加了一些内容。它表明需要 a pointer-event: none;否则将无法与页面交互。

const
  gooCont = document.querySelector('div.goo-container'),
  gooOne = gooCont.querySelector('div.goo-one'),
  gooTwo = gooCont.querySelector('div.goo-two'),
  filterOne = document.querySelector('#goo-filter-one')
  rand = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min

gooCont.style.setProperty('--translateY', `translateY(-${innerWidth * 0.21 / innerHeight * 100 + 100}%)`)
generateGoo(gooOne)

function generateGoo(goo) {
  const
    randQty = rand(20,30),
    unit = innerWidth / (randQty - 1) / innerWidth * 100

  if (getComputedStyle(goo).display === 'none') goo.style.display = 'block'
  goo.removeAttribute('y')

  for (let i = 0; i < randQty; i++) {
    const
      div = document.createElement('div'),
      minWidthPx = innerWidth < 500 ? innerWidth * 0.1 : innerWidth * 0.05,
      minMaxWidthPx = innerWidth < 500 ? innerWidth * 0.2 : innerWidth * 0.1,
      widthPx = rand(minWidthPx, minMaxWidthPx),
      widthPerc = widthPx / innerWidth * 100,
      heightPx = rand(widthPx / 2, widthPx * 3),
      heightPerc = heightPx / gooCont.getBoundingClientRect().height * 100,
      translateY = rand(45, 70),
      targetTranslateY = rand(15, 100),
      borderRadiusPerc = rand(40, 50)
    div.style.width = widthPerc + '%'
    div.style.height = heightPerc + '%'
    div.style.left = i * unit + '%'
    div.style.transform = `translate(-50%, ${translateY}%)`
    div.style.borderRadius = borderRadiusPerc + '%'
    div.setAttribute('data-translate', targetTranslateY)
    goo.appendChild(div)
  }
  goo.style.transform = `translateY(0)`
  goo.childNodes.forEach(
    v => v.style.transform = `translateY(${v.getAttribute('data-translate')}%)`
  )
}

setTimeout(() => {
  gooTwo.innerHTML = '<div class="first"></div>'
  filterOne.setAttribute('y', '100%')
  generateGoo(gooTwo, true)
}, 2300)
Run Code Online (Sandbox Code Playgroud)
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  background: red;
}

div.goo-container {
  --translateY: translateY(-165%);
  z-index: 1;
  width: 100%;
  height: 100%;
  position: fixed;
  overflow: hidden;
}

div.goo-container > div {
  width: 100%;
  height: 100%;
  position: absolute;
  pointer-events: none;
  transform: var(--translateY);
  transition: transform 2.8s linear;
}

div.goo-container > div.goo-one {
  filter: url('#goo-filter-one');
  background: #5b534a;
}

div.goo-container > div.goo-two {
  display: none;
  filter: url('#goo-filter-two');
}

div.goo-container > div.goo-one > div,
div.goo-container > div.goo-two > div {
  position: absolute;
  bottom: 0;
  background: #5b534a;
  transition: transform 2.8s linear;
}

div.goo-container > div.goo-two > div.first {
  top: -10%;
  width: 100%;
  height: 110%;
}

svg {
  width: 0;
  height: 0;
}
Run Code Online (Sandbox Code Playgroud)
<div class='goo-container'>
  <div class='goo-one'></div>
  <div class='goo-two'></div>
  <p><a href="#">Click me</a> and read.</p>
</div>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1'>
  <filter id='goo-filter-one' height='200%'>
    <feGaussianBlur in='SourceGraphic' stdDeviation='10' result='blur' />
    <feComponentTransfer in='blur' result='goo'>
        <feFuncA type='linear' slope='18' intercept='-7' />
    </feComponentTransfer>
    <feFlood flood-color='skyblue' result='back' />
    <feMerge>
      <feMergeNode in='back' />
      <feMergeNode in='goo' />
    </feMerge>
  </filter>
  <filter id='goo-filter-two' height='200%'>
    <feGaussianBlur in='SourceGraphic' stdDeviation='10' result='blur' />
    <feComponentTransfer in='blur' result='goo'>
        <feFuncA type='linear' slope='18' intercept='-7' />
    </feComponentTransfer>
    <feFlood flood-color='#5b534a' result='back' />
    <feComposite operator='out' in='back' in2='goo' />
  </filter>
</svg>
Run Code Online (Sandbox Code Playgroud)