[ Note: Looking for a cross-browser solution that does not flash the body's background momentarily between each wave of goo as seen in ccprog's answer; Ideally, the solution should not involve waiting until the end of the first wave to begin displaying the second wave, so that both waves can run concurrently. I am willing to forego dynamically randomized goop in order for an ideal solution. ]
Does anybody know how I can make the second wave of orange goo (.goo-two
) "cut through" the first wave of brown goo (.goo-one
) and the skyblue container (.goo-container
) to show or expose the red body element (body
) or, for that matter, any other element below it in the stacking context? Is it possible?
Notably, the reason I have given the container (.goo-container
) a solid background is because I was using this to cover up the loading process of the rest of the website, whereby I was hoping the orange goo (.goo-two
) can be used to reveal the content. It gets even trickier because the orange goo starts dripping before the brown goo finishes, which would be the perfect time to change the background of the contianer (.goo-container
) from skyblue
to transparent
, although a semi-transparent gradient as the background can likely be used to still achieve this. (Either that or something altogether different like duplicating the orange layer and use one to clip the brown path and the other to clip skyblue layer.)
Any ideas?
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)
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; */
}
<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>
background-clip lets you control how far a background image or color extends beyond an element's padding or content.
The background-clip CSS property sets whether an element's background extends underneath its border box, padding box, or content box.
The CSS background-clip property specifies the painting area of the background. The property takes three different values: border-box - (default) the background is painted to the outside edge of the border. padding-box - the background is painted to the outside edge of the padding.
I am pretty sure this is not the optimal variant, but it seems to work out, at least in Firefox. Chrome has some issues with the initial frames of each part of the animation.
.goo-one
and the child divs get a background color. This makes it possible for .goo-two
to become transparent..gooTwo
. To also fill (and after inversion, empty) the top part, an extra <div class="first">
is needed.svg
element for better browser compatibility.pointer-event: none
is needed; otherwise no interaction with the page would be possible.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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With