A Pen by Joshua van Boxtel on CodePen.
Last active
December 15, 2023 19:58
-
-
Save lostintangent/24fe9c1ff087947f5c19b847dde0b89d to your computer and use it in GitHub Desktop.
π Flood the Valley
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.background | |
each _ in Array(5) | |
span | |
.cloud | |
each _ in Array(8) | |
span | |
//- .fps | |
//- .rain-count | |
.reset Reset | |
//- .debug Debug | |
.title mouse moves cloud Β° click to rain Β° FLOOD the VALLEY | |
.ground-grid |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let rows = [ | |
[ | |
0, | |
0, | |
0, | |
5, | |
6, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
5 | |
], | |
[ | |
6, | |
0, | |
5, | |
4, | |
4, | |
6, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
5, | |
4 | |
], | |
[ | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
6, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
5, | |
4, | |
4, | |
4 | |
], | |
[ | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
6, | |
0, | |
5, | |
6, | |
0, | |
0, | |
5, | |
4, | |
6, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
4, | |
4, | |
4, | |
4 | |
], | |
[ | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
4, | |
6, | |
0, | |
5, | |
4, | |
6, | |
0, | |
0, | |
0, | |
5, | |
4, | |
4, | |
4, | |
4 | |
] | |
]; | |
const rowsBackup = JSON.parse(JSON.stringify(rows)); | |
const cloudW = 110; | |
const density = 0.0333; | |
const maxDroplets = 60; | |
let raining = false; | |
const times = []; | |
const rainSpeed = 7; | |
const rainIncrease = 0.025; | |
const spreadIncrement = 0.01; // how fast the water spreads | |
const spreadCutoff = 0.01; // determines when the water should consider itself "equalized" | |
let reset = false; | |
let fps; | |
$(".reset").on("click", function (e) { | |
reset = true; | |
}); | |
$(".debug").on("click", function () { | |
console.log(rows); | |
}); | |
$(document).on("mousemove", function (e) { | |
$(".cloud").css("left", e.pageX); | |
}); | |
$(document).on("mousedown", function () { | |
raining = true; | |
}); | |
$(document).on("mouseup", function () { | |
raining = false; | |
}); | |
function mainLoop() { | |
if (reset) { | |
console.log("resetting flood..."); | |
rows = JSON.parse(JSON.stringify(rowsBackup)); | |
$(".raindrop").remove(); | |
reset = false; | |
} else { | |
if (raining && $(".raindrop").length < maxDroplets) { | |
addDrop(); | |
} | |
moveDrops(); | |
calcDrops(); | |
spreadWater(); | |
} | |
calcFPS(); | |
drawRows(); | |
requestAnimationFrame(mainLoop); | |
} | |
requestAnimationFrame(mainLoop); | |
function moveDrops() { | |
const screenH = $(window).outerHeight(); | |
const screenW = $(window).outerWidth(); | |
$(".raindrop").each(function () { | |
const currY = $(this).offset().top; | |
const newY = currY + rainSpeed; | |
const dropCol = parseInt($(this).data("col")); | |
const colHeight = getColHeight(dropCol); | |
if (newY > screenH - colHeight * (screenW * density)) { | |
let emptyRow = -1; | |
$.each(rows.slice().reverse(), function (index, row) { | |
if (row[dropCol] < 1) { | |
emptyRow = rows.length - 1 - index; | |
return false; | |
} | |
}); | |
if (emptyRow > -1) { | |
rows[emptyRow][dropCol] = Math.min( | |
1, | |
rows[emptyRow][dropCol] + rainIncrease | |
); | |
} | |
$(this).remove(); | |
} else { | |
$(this).css("transform", "translateY(" + newY + "px)"); | |
} | |
}); | |
} | |
function addDrop() { | |
const xRand = Math.random() * cloudW; | |
const cloudLeft = $(".cloud").offset().left; | |
const rainPos = cloudLeft + xRand; | |
const winW = $(window).outerWidth(); | |
const colSector = Math.floor((rainPos / winW / 3.33) * 100); | |
// console.log("adding raindrop to column " + colSector); | |
$("body").append('<div class="raindrop raindrop--new"></div>'); | |
$(".raindrop--new") | |
.removeClass("raindrop--new") | |
.css("left", rainPos) | |
.data("col", colSector); | |
} | |
function calcFPS() { | |
const now = performance.now(); | |
while (times.length > 0 && times[0] <= now - 1000) { | |
times.shift(); | |
} | |
times.push(now); | |
fps = times.length; | |
$(".fps").text("FPS: " + fps); | |
} | |
function calcDrops() { | |
$(".rain-count").text("Droplets: " + $(".raindrop").length); | |
} | |
function drawRows() { | |
const grid = $(".ground-grid"); | |
grid.empty(); | |
$.each(rows, function (index, row) { | |
$.each(row, function (index2, col) { | |
let removeSpillage = false; | |
let newBlock = $( | |
'<div class="ground-grid__block" data-row=' + | |
index + | |
" data-col=" + | |
index2 + | |
"></div>" | |
); | |
if (col > 6) { | |
newBlock.addClass("spillage"); | |
removeSpillage = true; | |
} | |
if (col == 6 || col == 8) { | |
newBlock.addClass("ground-grid__block--rightRound"); | |
} | |
if (col == 5 || col == 7) { | |
newBlock.addClass("ground-grid__block--leftRound"); | |
} | |
if (col == 4) { | |
newBlock.addClass("ground-grid__block--dirt"); | |
} | |
if (col == 2) { | |
newBlock.addClass("ground-grid__block--rightSlope"); | |
} | |
if (col == 3) { | |
newBlock.addClass("ground-grid__block--leftSlope"); | |
} | |
if (col <= 1 && col > 0) { | |
const level = col * 100; | |
const heavyWater = level >= 5; | |
const drawLevel = heavyWater ? level : 0; | |
newBlock | |
.addClass("ground-grid__block--water") | |
.append( | |
"<span data-level=" + | |
level + | |
' style="height:' + | |
(drawLevel + "%") + | |
'"></span>' | |
); | |
if (heavyWater) { | |
newBlock.addClass("heavyWater"); | |
} | |
} | |
grid.append(newBlock); | |
rows[index][index2] = removeSpillage | |
? rows[index][index2] - 2 | |
: rows[index][index2]; | |
}); | |
}); | |
} | |
function spreadWater() { | |
// remove spillage animations | |
$(".spillage").removeClass("spillage"); | |
// go through each water tile and spread water | |
$(".ground-grid__block--water").each(function () { | |
const row = $(this).data("row"); | |
const col = $(this).data("col"); | |
const level = $(this).find("span").data("level") / 100; | |
let addBelow = false; | |
// always check block below first | |
const belowBlockVal = row != rows.length - 1 ? rows[row + 1][col] : null; | |
if (belowBlockVal !== null && belowBlockVal < 1) { | |
rows[row + 1][col] += spreadIncrement; | |
if (rows[row + 1][col] > 1) { | |
rows[row + 1][col] = 1; | |
} | |
rows[row][col] -= spreadIncrement; | |
addBelow = true; | |
} | |
// check block to the left | |
const leftBlockVal = col != 0 ? rows[row][col - 1] : null; | |
if ( | |
leftBlockVal !== null && | |
leftBlockVal < 1 && | |
leftBlockVal < level && | |
Math.abs(leftBlockVal - level) > spreadCutoff && | |
!addBelow | |
) { | |
rows[row][col] -= spreadIncrement; | |
rows[row][col - 1] += spreadIncrement; | |
if (rows[row][col - 1] > 1) { | |
rows[row][col - 1] = 1; | |
} | |
// if not falling down, add spillage below | |
if (belowBlockVal !== null && belowBlockVal != 4 && belowBlockVal != 1) { | |
rows[row + 1][col] = belowBlockVal == 5 ? 7 : 8; | |
} | |
} | |
// check block to the right | |
const rightBlockVal = col != 29 ? rows[row][col + 1] : null; | |
if ( | |
rightBlockVal !== null && | |
rightBlockVal < 1 && | |
rightBlockVal < level && | |
Math.abs(rightBlockVal - level) > spreadCutoff && | |
!addBelow | |
) { | |
rows[row][col] -= spreadIncrement; | |
rows[row][col + 1] += spreadIncrement; | |
if (rows[row][col + 1] > 1) { | |
rows[row][col + 1] = 1; | |
} | |
// if not falling down, add spillage below | |
if (belowBlockVal !== null && belowBlockVal != 4 && belowBlockVal != 1) { | |
rows[row + 1][col] = belowBlockVal == 5 ? 7 : 8; | |
} | |
} | |
// if tile is essentially empty, drain it | |
if (level < spreadIncrement) { | |
rows[row][col] = 0; | |
} | |
}); | |
} | |
function getColHeight(col) { | |
let height = 0; | |
$.each(rows.slice().reverse(), function (index, row) { | |
const rowCol = row[col]; | |
if (rowCol == 0) { | |
return false; | |
} else if (rowCol <= 1) { | |
height += rowCol; | |
} else { | |
height += 1; | |
} | |
}); | |
return height; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url("https://fonts.googleapis.com/css?family=Major+Mono+Display&display=swap"); | |
$ground: #481d13; | |
$density: 3.33vw; | |
$water: lighten(blue, 10%); | |
html, | |
body { | |
height: 100%; | |
width: 100%; | |
overflow: hidden; | |
background: #cfe3f1; | |
} | |
.title { | |
user-select: none; | |
position: absolute; | |
font-family: "Major Mono Display", monospace; | |
color: $water; | |
top: 16px; | |
left: 16px; | |
right: 60px; | |
font-size: 22px; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
word-spacing: -5px; | |
letter-spacing: -1.5px; | |
padding-bottom: 15px; | |
&::before { | |
content: ""; | |
position: absolute; | |
bottom: 0; | |
opacity: 0.25; | |
left: 0px; | |
height: 3px; | |
width: 87px; | |
background: $water; | |
} | |
} | |
.background { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
opacity: 0.5; | |
span { | |
width: 35vw; | |
height: 35vw; | |
background: linear-gradient( | |
135deg, | |
rgb(181 189 200 / 94%) 0%, | |
#769cbd 36%, | |
#126b2e 100% | |
); | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
transform: rotate(45deg) translateY(50%); | |
border-radius: 40px; | |
&:nth-of-type(2) { | |
left: 25%; | |
} | |
&:nth-of-type(3) { | |
left: 50%; | |
} | |
&:nth-of-type(4) { | |
left: 75%; | |
} | |
&:nth-of-type(5) { | |
left: 100%; | |
} | |
} | |
} | |
.fps { | |
position: absolute; | |
top: 110px; | |
right: 10px; | |
font: 400 16px/1 sans-serif; | |
} | |
.rain-count { | |
position: absolute; | |
top: 135px; | |
right: 10px; | |
font: 400 16px/1 sans-serif; | |
} | |
.reset, | |
.debug { | |
position: absolute; | |
top: 10px; | |
right: 10px; | |
font: 400 16px/1 sans-serif; | |
padding: 10px; | |
border: 1px solid black; | |
cursor: pointer; | |
&:hover { | |
background: black; | |
color: white; | |
} | |
} | |
.debug { | |
top: 60px; | |
} | |
.raindrop { | |
position: absolute; | |
top: 0px; | |
width: 2px; | |
height: 5px; | |
background: $water; | |
transform: translateY(150px); | |
z-index: 0; | |
} | |
.ground-grid { | |
display: flex; | |
flex-wrap: wrap; | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
width: 100%; | |
&__block { | |
width: $density; | |
padding-top: $density; | |
// border: 1px dashed black; | |
box-sizing: border-box; | |
position: relative; | |
&--water { | |
span { | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
background: $water; | |
&:not([style="height:0%"])::before, | |
&:not([style="height:0%"])::after { | |
content: ""; | |
position: absolute; | |
z-index: 0; | |
background: $water; | |
top: 0; | |
right: 90%; | |
height: 3vw; | |
width: 1vw; | |
border-radius: 15px; | |
} | |
&:not([style="height:0%"])::after { | |
right: auto; | |
left: 90%; | |
} | |
} | |
&.heavyWater { | |
&::after { | |
content: ""; | |
position: absolute; | |
top: 100%; | |
left: 0; | |
right: 0; | |
height: 2vw; | |
background: $water; | |
} | |
} | |
} | |
&--dirt, | |
&--leftRound, | |
&--rightRound { | |
background: $ground; | |
&::after { | |
content: ""; | |
position: absolute; | |
z-index: 1; | |
width: 100%; | |
height: 100%; | |
background: $ground; | |
top: 0; | |
left: 0; | |
border-radius: 15px 0 0 0; | |
} | |
} | |
&--leftRound { | |
border-radius: 15px 0 0 0; | |
&.spillage { | |
&::before { | |
content: ""; | |
position: absolute; | |
right: 0; | |
bottom: 0; | |
background: $water; | |
top: -2px; | |
left: -2px; | |
border-radius: 15px 0 0 0; | |
} | |
} | |
} | |
&--rightRound { | |
border-radius: 0 15px 0 0; | |
&::after { | |
border-radius: 0 15px 0 0; | |
} | |
&.spillage { | |
&::before { | |
content: ""; | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
background: $water; | |
right: -2px; | |
top: -2px; | |
border-radius: 0 15px 0 0; | |
} | |
} | |
} | |
&--rightSlope { | |
&::after { | |
content: ""; | |
position: absolute; | |
top: 0px; | |
left: 0px; | |
width: 0; | |
height: 0; | |
border-style: solid; | |
border-width: 5vw 0 0 5vw; | |
border-color: transparent transparent transparent $ground; | |
} | |
} | |
&--leftSlope { | |
&::after { | |
content: ""; | |
position: absolute; | |
top: 0px; | |
left: 0px; | |
width: 0; | |
height: 0; | |
border-style: solid; | |
border-width: 0 0 5vw 5vw; | |
border-color: transparent transparent $ground transparent; | |
} | |
} | |
} | |
} | |
.cloud { | |
position: absolute; | |
top: 100px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 110px; | |
cursor: pointer; | |
z-index: 1; | |
&::after { | |
content: ""; | |
position: absolute; | |
background: white; | |
width: 70px; | |
height: 30px; | |
top: 27px; | |
left: 22px; | |
} | |
span { | |
position: absolute; | |
width: 40px; | |
height: 40px; | |
top: 0; | |
left: 0; | |
border-radius: 100%; | |
background: white; | |
box-shadow: 0px 0px 4px rgba(black, 0.1); | |
&:nth-of-type(1) { | |
top: 25px; | |
} | |
&:nth-of-type(2) { | |
width: 30px; | |
height: 30px; | |
top: 13px; | |
left: 20px; | |
} | |
&:nth-of-type(3) { | |
left: 40px; | |
} | |
&:nth-of-type(4) { | |
width: 30px; | |
height: 30px; | |
top: 12px; | |
left: 67px; | |
} | |
&:nth-of-type(5) { | |
width: 30px; | |
height: 30px; | |
top: 30px; | |
left: 80px; | |
} | |
&:nth-of-type(6) { | |
width: 23px; | |
height: 23px; | |
left: 24px; | |
top: 45px; | |
} | |
&:nth-of-type(7) { | |
width: 35px; | |
height: 35px; | |
left: 38px; | |
top: 38px; | |
} | |
&:nth-of-type(8) { | |
left: 60px; | |
top: 40px; | |
width: 30px; | |
height: 30px; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment