为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕神8447489 2023-06-15 16:53:14
[注意:寻找一种跨浏览器的解决方案,它不会像 ccprog 的答案中所见那样在每一波 goo 之间瞬间闪烁身体的背景;理想情况下,解决方案不应涉及等到第一个 wave 结束才开始显示第二个 wave,以便两个 wave 可以同时运行。我愿意放弃动态随机化的 goop 以获得理想的解决方案。]有谁知道我怎样才能让第二波橙色黏液 ( .goo-two) “切开”第一波棕色黏液 ( .goo-one) 和天蓝色容器 ( .goo-container) 以显示或暴露红色主体元素 ( body),或者就此而言,任何堆叠上下文中它下面的其他元素?是否可以?值得注意的是,我给容器 ( .goo-container) 一个坚实的背景的原因是因为我用它来掩盖网站其余部分的加载过程,因此我希望橙色粘液 ( .goo-two) 可以用来揭示内容。它变得更加棘手,因为橙色粘液在棕色粘液完成之前开始滴落,这将是将 contianer ( .goo-container) 的背景从skyblue更改为 的最佳时机transparent,尽管作为背景的半透明渐变可能仍可用于实现这。(要么是那个,要么是完全不同的东西,比如复制橙色层,用一个剪裁棕色路径,另一个剪裁天蓝色层。)有任何想法吗?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)) + mingooCont.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)
查看完整描述

3 回答

?
POPMUISE

TA贡献1765条经验 获得超5个赞

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

  • 我稍微重写了 gooey 过滤器代码以提高可读性,同时保持相同的效果。

  • 只有.goo-one和子 div 获得背景颜色。这使得.goo-two变得透明成为可能。

  • 这两个部分有不同的过滤器,但过滤器区域垂直增加,以便在过渡开始时到达屏幕底部。

  • 第一个滤镜使用天蓝色作为背景填充。

  • 第二个过滤器有一个棕色填充,但它的应用是相反的:它只显示在 goo 区域之外,而内部区域是空的。构成 goo 区域的 div 矩形不跨越整个.gooTwo. 为了也填充(并在倒置后为空)顶部,<div class="first">需要额外的部分。

  • 在第二个 goo 部分的过渡开始时,过滤器区域上限设置在屏幕下边界下方。这隐藏了天蓝色背景,同时第二个粘性部分变得可见。

  • 请注意,为了更好的浏览器兼容性,元素的 CSS 略有变化svg

  • 作为概念证明,在容器 div 中添加了一些内容。表明pointer-event: none需要a;否则无法与页面进行交互。

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)

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;

}

<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>


查看完整回答
反对 回复 2023-06-15
?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

首先,我将开始使用一个 div 和多个渐变来构建形状。


这是一个使用我们可以轻松定位的不固定渐变(相同宽度和不同高度)的想法:


Hide code snippet


:root {

  --c:linear-gradient(red,red);

}

div.goo-container {

  position:fixed;

  top:0;

  left:-20px;

  right:-20px;

  height:200px;

  background:

     var(--c) calc(0*100%/9) 0/calc(100%/10) 80%,

     var(--c) calc(1*100%/9) 0/calc(100%/10) 60%,

     var(--c) calc(2*100%/9) 0/calc(100%/10) 30%,

     var(--c) calc(3*100%/9) 0/calc(100%/10) 50%,

     var(--c) calc(4*100%/9) 0/calc(100%/10) 59%,

     var(--c) calc(5*100%/9) 0/calc(100%/10) 48%,

     var(--c) calc(6*100%/9) 0/calc(100%/10) 36%,

     var(--c) calc(7*100%/9) 0/calc(100%/10) 70%,

     var(--c) calc(8*100%/9) 0/calc(100%/10) 75%,

     var(--c) calc(9*100%/9) 0/calc(100%/10) 35%;

  background-repeat:no-repeat;

  filter: url('#goo-filter');

}

<div class='goo-container'>

</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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

我们也可以有可变宽度,这里需要 JS 来生成所有这些宽度:


Hide code snippet


:root {

  --c:linear-gradient(red,red);

}

div.goo-container {

  position:fixed;

  top:0;

  left:-20px;

  right:-20px;

  height:200px;

  background:

     var(--c) 0     0/20px 80%,

     var(--c) 20px  0/80px 60%,

     var(--c) 100px 0/10px 30%,

     var(--c) 110px 0/50px 50%,

     var(--c) 160px 0/30px 59%,

     var(--c) 190px 0/80px 48%,

     var(--c) 270px 0/10px 36%,

     var(--c) 280px 0/20px 70%,

     var(--c) 300px 0/50px 75%,

     var(--c) 350px 0/80px 35%

     /* and so on ... */;

  background-repeat:no-repeat;

  filter: url('#goo-filter');

}

<div class='goo-container'>

</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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

然后用更多的 CSS 我们可以有我们的第一个动画:


Hide code snippet


:root {

  --c:linear-gradient(red,red);

}

div.goo-container {

  position:fixed;

  height:100vh;

  top:0;

  left:0;

  right:0;

  background:red;

  transform:translateY(-150vh);

  animation:move 3s 1s forwards;

}


div.goo-container::after {

  position:absolute;

  content:"";

  top:100%;

  left:-20px;

  right:-20px;

  height:50vh;

  margin:0 -20px;

  background:

     var(--c) calc(0*100%/9) 0/calc(100%/10) 80%,

     var(--c) calc(1*100%/9) 0/calc(100%/10) 60%,

     var(--c) calc(2*100%/9) 0/calc(100%/10) 30%,

     var(--c) calc(3*100%/9) 0/calc(100%/10) 50%,

     var(--c) calc(4*100%/9) 0/calc(100%/10) 59%,

     var(--c) calc(5*100%/9) 0/calc(100%/10) 48%,

     var(--c) calc(6*100%/9) 0/calc(100%/10) 36%,

     var(--c) calc(7*100%/9) 0/calc(100%/10) 70%,

     var(--c) calc(8*100%/9) 0/calc(100%/10) 75%,

     var(--c) calc(9*100%/9) 0/calc(100%/10) 35%;

  background-repeat:no-repeat;

  filter: url('#goo-filter');

}

div.goo-container::before {

  position:absolute;

  content:"";

  top:100%;

  height:150vh;

  background:blue;

  left:0;

  right:0;

}


@keyframes move {

  to {

     transform:translateY(0);

  }

}

<div class='goo-container'>

</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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

仍然不完美,但我们也可以添加一些渐变动画来调整大小:


Hide code snippet


:root {

  --c:linear-gradient(red,red);

}

div.goo-container {

  position:fixed;

  height:100vh;

  top:0;

  left:0;

  right:0;

  background:red;

  transform:translateY(-150vh);

  animation:move 5s 0.5s forwards;

}


div.goo-container::after {

  position:absolute;

  content:"";

  top:100%;

  left:-20px;

  right:-20px;

  height:50vh;

  margin:0 -20px;

  background:

     var(--c) calc(0*100%/9) 0/calc(100%/10) 80%,

     var(--c) calc(1*100%/9) 0/calc(100%/10) 60%,

     var(--c) calc(2*100%/9) 0/calc(100%/10) 30%,

     var(--c) calc(3*100%/9) 0/calc(100%/10) 50%,

     var(--c) calc(4*100%/9) 0/calc(100%/10) 59%,

     var(--c) calc(5*100%/9) 0/calc(100%/10) 48%,

     var(--c) calc(6*100%/9) 0/calc(100%/10) 36%,

     var(--c) calc(7*100%/9) 0/calc(100%/10) 70%,

     var(--c) calc(8*100%/9) 0/calc(100%/10) 75%,

     var(--c) calc(9*100%/9) 0/calc(100%/10) 35%;

  background-repeat:no-repeat;

  filter: url('#goo-filter');

  animation:grad 4.5s 1s forwards;

}

div.goo-container::before {

  position:absolute;

  content:"";

  top:100%;

  height:150vh;

  background:blue;

  left:0;

  right:0;

}


@keyframes move {

  to {

     transform:translateY(0);

  }

}

@keyframes grad {

  to {

     background-size:

     calc(100%/10) 50%,

     calc(100%/10) 75%,

     calc(100%/10) 20%,

     calc(100%/10) 60%,

     calc(100%/10) 55%,

     calc(100%/10) 80%,

     calc(100%/10) 23%,

     calc(100%/10) 80%,

     calc(100%/10) 90%,

     calc(100%/10) 20%;

  }

}

<div class='goo-container'>

</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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

上面有点棘手,因为每个渐变的位置将取决于所有前一个渐变的大小(这里可能需要 JS 或 SASS 来生成代码)


对于第二个动画,我们将做同样的事情,但我们认为遮罩属性中的渐变层具有相反的效果(渐变层将被移除以查看剩余部分)


Hide code snippet


:root {

  --c:linear-gradient(red,red);

  background:pink;

}

div.goo-container {

  position:fixed;

  height:150vh;

  top:0;

  left:0;

  right:0;

  transform:translateY(-200vh);

  animation:move 8s 0.5s forwards;

  filter: url('#goo-filter');

}

div.goo-container > div {

  height:100%;

  background:red;

  -webkit-mask:

     var(--c) calc(0*100%/9) 0/calc(100%/10 + 4px) 40vh,

     var(--c) calc(1*100%/9) 0/calc(100%/10 + 4px) 30vh,

     var(--c) calc(2*100%/9) 0/calc(100%/10 + 4px) 15vh,

     var(--c) calc(3*100%/9) 0/calc(100%/10 + 4px) 20vh,

     var(--c) calc(4*100%/9) 0/calc(100%/10 + 4px) 29vh,

     var(--c) calc(5*100%/9) 0/calc(100%/10 + 4px) 35vh,

     var(--c) calc(6*100%/9) 0/calc(100%/10 + 4px) 12vh,

     var(--c) calc(7*100%/9) 0/calc(100%/10 + 4px) 50vh,

     var(--c) calc(8*100%/9) 0/calc(100%/10 + 4px) 48vh,

     var(--c) calc(9*100%/9) 0/calc(100%/10 + 4px) 40vh,

     linear-gradient(#fff,#fff);

  -webkit-mask-composite:destination-out;

  mask-composite:exclude;

  -webkit-mask-repeat:no-repeat;

  animation:mask 7.5s 1s forwards;

}


div.goo-container::after {

  position:absolute;

  content:"";

  top:100%;

  left:-20px;

  right:-20px;

  height:50vh;

  margin:0 -20px;

  background:

     var(--c) calc(0*100%/9) 0/calc(100%/10) 80%,

     var(--c) calc(1*100%/9) 0/calc(100%/10) 60%,

     var(--c) calc(2*100%/9) 0/calc(100%/10) 30%,

     var(--c) calc(3*100%/9) 0/calc(100%/10) 50%,

     var(--c) calc(4*100%/9) 0/calc(100%/10) 59%,

     var(--c) calc(5*100%/9) 0/calc(100%/10) 48%,

     var(--c) calc(6*100%/9) 0/calc(100%/10) 36%,

     var(--c) calc(7*100%/9) 0/calc(100%/10) 60%,

     var(--c) calc(8*100%/9) 0/calc(100%/10) 65%,

     var(--c) calc(9*100%/9) 0/calc(100%/10) 35%;

  background-repeat:no-repeat;

  filter: url('#goo-filter');

  animation:grad 7.5s 1s forwards;

}

div.goo-container::before {

  position:absolute;

  content:"";

  top:100%;

  height:150vh;

  background:blue;

  left:0;

  right:0;

}


@keyframes move {

  to {

     transform:translateY(150vh);

  }

}

@keyframes grad {

  to {

     background-size:

     calc(100%/10) 50%,

     calc(100%/10) 75%,

     calc(100%/10) 20%,

     calc(100%/10) 60%,

     calc(100%/10) 55%,

     calc(100%/10) 80%,

     calc(100%/10) 23%,

     calc(100%/10) 80%,

     calc(100%/10) 90%,

     calc(100%/10) 20%;

  }

}

@keyframes mask {

  to {

     -webkit-mask-size:

     calc(100%/10) 30vh,

     calc(100%/10) 10vh,

     calc(100%/10) 50vh,

     calc(100%/10) 45vh,

     calc(100%/10) 12vh,

     calc(100%/10) 22vh,

     calc(100%/10) 60vh,

     calc(100%/10) 10vh,

     calc(100%/10) 8vh,

     calc(100%/10) 35vh,

     auto;

  }

}

<h1>Lorem ipsum dolor sit amet</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu sodales lectus. Sed non erat accumsan, placerat purus quis, sodales mi. Suspendisse potenti. Sed eu viverra odio. </p>




<div class='goo-container'>

  <div></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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

我们做了一些代码优化,只保留一个元素:


Hide code snippet


:root {

  --c:linear-gradient(red,red);

  background:pink;

}

div.goo-container {

  position:fixed;

  top:0;

  left:0;

  right:0;

  bottom:0;

  transform:translateY(-150%);

  animation:move 8s 0.5s forwards;

  filter: url('#goo-filter');

}


div.goo-container::after {

  position:absolute;

  content:"";

  top:-50%;

  left:0;

  right:0;

  bottom:-50%;

  background:

     var(--c) calc(0*100%/9) 0/calc(100%/10) calc(100% - 40vh),

     var(--c) calc(1*100%/9) 0/calc(100%/10) calc(100% - 30vh),

     var(--c) calc(2*100%/9) 0/calc(100%/10) calc(100% - 35vh),

     var(--c) calc(3*100%/9) 0/calc(100%/10) calc(100% - 50vh),

     var(--c) calc(4*100%/9) 0/calc(100%/10) calc(100% - 10vh),

     var(--c) calc(5*100%/9) 0/calc(100%/10) calc(100% - 15vh),

     var(--c) calc(6*100%/9) 0/calc(100%/10) calc(100% - 30vh),

     var(--c) calc(7*100%/9) 0/calc(100%/10) calc(100% - 28vh),

     var(--c) calc(8*100%/9) 0/calc(100%/10) calc(100% - 30vh),

     var(--c) calc(9*100%/9) 0/calc(100%/10) calc(100% - 50vh);

  background-repeat:no-repeat;

  -webkit-mask:

     var(--c) calc(0*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 20vh),

     var(--c) calc(1*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 10vh),

     var(--c) calc(2*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 50vh),

     var(--c) calc(3*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 30vh),

     var(--c) calc(4*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 35vh),

     var(--c) calc(5*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 10vh),

     var(--c) calc(6*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 50vh),

     var(--c) calc(7*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 40vh),

     var(--c) calc(8*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 45vh),

     var(--c) calc(9*100%/9) 100%/calc(100%/10 + 4px) calc(100% - 35vh);

  -webkit-mask-repeat:no-repeat;

  filter: inherit;

  animation: inherit;

  animation-name:grad, mask;

}

div.goo-container::before {

  position:absolute;

  content:"";

  top:50%;

  bottom:-150%;

  background:blue;

  left:0;

  right:0;

}


@keyframes move {

  to {

     transform:translateY(200%);

  }

}

@keyframes grad {

  to {

     background-size:

     calc(100%/10) calc(100% - 10vh),

     calc(100%/10) calc(100% - 50vh),

     calc(100%/10) calc(100% - 30vh),

     calc(100%/10) calc(100% - 10vh),

     calc(100%/10) calc(100% - 40vh),

     calc(100%/10) calc(100% - 25vh),

     calc(100%/10) calc(100% - 32vh),

     calc(100%/10) calc(100% - 18vh),

     calc(100%/10) calc(100% - 50vh),

     calc(100%/10) calc(100% - 10vh);

  }

}

@keyframes mask {

  to {

     -webkit-mask-size:

     calc(100%/10) calc(100% - 10vh),

     calc(100%/10) calc(100% - 50vh),

     calc(100%/10) calc(100% - 10vh),

     calc(100%/10) calc(100% - 30vh),

     calc(100%/10) calc(100% - 32vh),

     calc(100%/10) calc(100% - 40vh),

     calc(100%/10) calc(100% - 50vh),

     calc(100%/10) calc(100% - 25vh),

     calc(100%/10) calc(100% - 18vh),

     calc(100%/10) calc(100% - 10vh);

  }

}

<h1>Lorem ipsum dolor sit amet</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu sodales lectus. Sed non erat accumsan, placerat purus quis, sodales mi. Suspendisse potenti. Sed eu viverra odio. </p>




<div class='goo-container'></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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

最后是使用 SASS 生成渐变层和遮罩层的动态解决方案:https://codepen.io/t_afif/pen/oNzxYgV


更新


不使用面具的另一个想法。诀窍是使渐变居中。此解决方案将有更多支持,但底部和顶部形状都是对称的


Hide code snippet


:root {

  --c:linear-gradient(red,red);

  background:pink;

}

div.goo-container {

  position:fixed;

  top:0;

  left:0;

  right:0;

  bottom:0;

  transform:translateY(-150%);

  animation:move 8s 0.5s forwards;

  filter: url('#goo-filter');

}


div.goo-container::after {

  position:absolute;

  content:"";

  top:-50%;

  left:0;

  right:0;

  bottom:-50%;

  background:

     var(--c) calc(0*100%/9) 50%/calc(100%/10) calc(100% - 80vh),

     var(--c) calc(1*100%/9) 50%/calc(100%/10) calc(100% - 60vh),

     var(--c) calc(2*100%/9) 50%/calc(100%/10) calc(100% - 70vh),

     var(--c) calc(3*100%/9) 50%/calc(100%/10) calc(100% - 100vh),

     var(--c) calc(4*100%/9) 50%/calc(100%/10) calc(100% - 20vh),

     var(--c) calc(5*100%/9) 50%/calc(100%/10) calc(100% - 30vh),

     var(--c) calc(6*100%/9) 50%/calc(100%/10) calc(100% - 60vh),

     var(--c) calc(7*100%/9) 50%/calc(100%/10) calc(100% - 56vh),

     var(--c) calc(8*100%/9) 50%/calc(100%/10) calc(100% - 60vh),

     var(--c) calc(9*100%/9) 50%/calc(100%/10) calc(100% - 100vh);

  background-repeat:no-repeat;

  filter: inherit;

  animation:grad 8s 0.5s forwards;

}

div.goo-container::before {

  position:absolute;

  content:"";

  top:50%;

  bottom:-150%;

  background:blue;

  left:0;

  right:0;

}


@keyframes move {

  to {

     transform:translateY(200%);

  }

}

@keyframes grad {

  to {

     background-size:

     calc(100%/10) calc(100% - 20vh),

     calc(100%/10) calc(100% - 100vh),

     calc(100%/10) calc(100% - 60vh),

     calc(100%/10) calc(100% - 20vh),

     calc(100%/10) calc(100% - 80vh),

     calc(100%/10) calc(100% - 50vh),

     calc(100%/10) calc(100% - 64vh),

     calc(100%/10) calc(100% - 34vh),

     calc(100%/10) calc(100% - 100vh),

     calc(100%/10) calc(100% - 20vh);

  }

}

<h1>Lorem ipsum dolor sit amet</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu sodales lectus. Sed non erat accumsan, placerat purus quis, sodales mi. Suspendisse potenti. Sed eu viverra odio. </p>




<div class='goo-container'></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 20 -5' result='goo' />

      <feBlend in='SourceGraphic' in2='goo' />

    </filter>

  </defs>

</svg>

展开片段

还有一个 SASS 版本:https://codepen.io/t_afif/pen/wvzGoeJ


查看完整回答
反对 回复 2023-06-15
?
慕丝7291255

TA贡献1859条经验 获得超6个赞

这是避免所有过滤器、掩蔽和组合困难的尝试。它只是一些 bezier 路径的SMIL 动画,应该支持,没有任何错误。我还没有找到第一波和第二波同时出现在屏幕上的解决方案。

我承认最费力的部分是为路径设计算法,其他一切都相对简单。

“goo”是跨客户区移动的具有上下边界的区域,同时路径的形式发生变化。我试图在代码注释中描述哪些部分可以调整。路径组合的基本结构确保了一个重要的限制:作为一个整体的路径对于动画的不同关键帧不能有不同的路径命令序列,否则平滑的动画将失败。换个号码应该没问题。

在 goo 的后面是一个不透明的矩形,它最初隐藏了内容。当 goo 在屏幕上运行时,它会在适当的时间隐藏。

动画的时间在<set><animate>元素的属性中定义。请注意 goo 动画运行了 6 秒,而背景矩形的隐藏发生在 3 秒后。此分布匹配属性的值<animate keyTimes>0;0.5;1,您可以将其读作 0%、50%、100% 作为关键帧的计时。触发器必须与中间关键帧匹配的时间<set>,因为那是 goo 覆盖整个客户区域的时间。

const

  rand = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,

  flatten = (x, y) => `${x.toFixed(2)},${y.toFixed(2)}`


function randomPoints(width, height) {

  const

    from = [],

    to = []


  let x = 0, old_extent = 0

  while (x + old_extent < width) {

    //width of a single goo tongue

    const extent = rand(5, 20)

    // rand() part: distance between tongues

    x += (from.length ? 1.5 : 0) * (old_extent + extent) + rand(0, 5)

    const data = {

        x1: x - extent,

        x2: x + extent,

        // "roundness": how far will the lowest point of the tongue

        // stretch below its defining line (qualitative value)

        dty: extent * rand(0.4, 1.4)

      }


    // y: tongue postition above screen border at start

    // Note the -20 gives space for the "roundness" not to cross the threshold

    from.push({ ...data, y: rand(-50, -20) })

    // y: tongue postition below screen border at end

    // Note the 10 gives space for the "roundness" not to cross the threshold

    to.push({ ...data, y: rand(10, 105) + height })


    old_extent = extent

  }


  return { from, to }

}


function generatePath(points, path, back) {

  const qti = points.length

  let old_dtx, old_dty


  if (back) points.reverse()


  for (let i = 0; i < qti; i++) {

    const

      x1 = back ? points[i].x2 : points[i].x1,

      x2 = back ? points[i].x1 : points[i].x2,

      dtx = (x2 - x1) / 2

    let dty = 0


    if (i == 0) {

      path.push(

        back ? 'L' : 'M', 

        flatten(x1, points[i].y), 

        'Q',

        flatten(x1 + dtx, points[i].y),        

        flatten(x2, points[i].y)

      );

    } else {

      if (i !== qti - 1) {

        const

          y0 = points[i - 1].y,

          y1 = points[i].y,

          y2 = points[i + 1].y,

          // the numbers give a weight to the "roundness" value for different cases:

          // a tongue stretching below its neighbors = 1 (rounding downwards)

          // a tongue laging behind below its neighbors = -0.1 (rounding upwards)

          // other cases = 0.5

          down = y1 > y0 ? y1 > y2 ? 1 : 0.5 : y1 > y2 ? 0.5 : -0.1

        dty = points[i].dty * down //min absichern

      }


      path.push(

        'C', 

        flatten(points[i - 1][back ? 'x1' : 'x2'] + old_dtx / 2, points[i - 1].y - old_dty / 2),

        flatten(x1 - dtx / 2, points[i].y - dty / 2),

        flatten(x1, points[i].y), 

        'Q',

        flatten(x1 + dtx, points[i].y + dty),

        flatten(x2, points[i].y)

      );

    }

    old_dtx = dtx, old_dty = dty

  }


  if (back) { 

    points.reverse()

    path.push('Z')

  }

}


function generateArea(width, height) {

  const

    // tongue control points for first wave

    firstPoints = randomPoints(width, height),

    // tongue control points for second wave

    secondPoints = randomPoints(width, height),

    start = [],

    mid = [],

    end = []


  // first keyframe

  generatePath(firstPoints.from, start, false)

  generatePath(secondPoints.from, start, true)


  // second keyframe

  generatePath(firstPoints.to, mid, false)

  generatePath(secondPoints.from, mid, true)


  // third keyframe

  generatePath(firstPoints.to, end, false)

  generatePath(secondPoints.to, end, true)

  

  return [

    start.join(' '), 

    mid.join(' '), 

    end.join(' ')

  ]

}


const rect = document.querySelector('svg').getBoundingClientRect()

const animate = document.querySelector('#gooAnimate')

const areas = generateArea(rect.width, rect.height)


animate.setAttribute('values', areas.join(';'))

animate.beginElement() // trigger animation start

body {

  position: relative;

  margin: 0;

}

#content {

  position: relative;

  box-sizing: border-box;

  background: #faa;

  width: 100vw;

  height: 100vh;

  padding: 1em;

}

svg {

  position: absolute;

  width: 100%;

  height: 100%;

  top: 0%;

  pointer-events: none;

}

#veil {

  fill: skyblue;

}

#goo {

  fill: #5b534a;

}

<div id="content">

  <h1>Lorem ipsum dolor sit amet</h1>

  <p>Lorem ipsum dolor sit amet, <a href="">consectetur</a> adipiscing elit. Nam eu sodales lectus. Sed non erat accumsan, placerat purus quis, sodales mi. Suspendisse potenti. Sed eu viverra odio. </p>


</div>

<svg xmlns="http://www.w3.org/2000/svg">

  <rect id="veil" width="100%" height="100%">

    <!-- background animation start time is relative to goo animation start time -->

    <set attributeName="display" to="none" begin="gooAnimate.begin+3s" fill="freeze" />

  </rect>

  <path id="goo" d="" >

    <animate id="gooAnimate" attributeName="d"

             begin="indefinite" dur="6s" fill="freeze" keyTimes="0;0.5;1" />

  </path>

</svg>


查看完整回答
反对 回复 2023-06-15
  • 3 回答
  • 0 关注
  • 133 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信