<input type="checkbox" name="playPause" id="playPause">
<div class="scene">
<div class="pivot">
<div class="boxFish">
<div class="face side">
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
</div>
<div class="face side">
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
</div>
<div class="face lid">
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
<div class="slice"></div>
</div>
<div class="anchor">
<div class="fin"></div>
<div class="fin"></div>
</div>
</div>
</div>
</div>
<label for="playPause"></label>
body {
background-image: linear-gradient(#036,#010,#000);
height: 100vh;
display: grid;
font-size: .3vmin;
}
.scene, .scene * {
position: absolute;
transform-style: preserve-3d;
}
.scene {
width: 100em;
aspect-ratio: 1;
place-self: center;
perspective: 250em;
}
:where(.pivot, .boxFish) {
inset: 0;
}
.pivot {
animation: tiltX 7s ease-in-out alternate infinite;
}
@keyframes tiltX {
0% {
rotate: x -10deg;
}
100% {
rotate: x -45deg;
}
}
.boxFish {
animation: tiltY 12s ease-in-out alternate infinite;
}
@keyframes tiltY {
0% {
rotate: y 80deg;
}
100% {
rotate: y 40deg;
}
}
.face {
--pyrH: calc(sin(60deg) - .5); /* 0.36602540378 */
inset-inline: 0;
height: calc(100em*var(--pyrH));
bottom: 50em;
transform-origin: 50% 100%;
backface-visibility: hidden;
}
.side {
transform: rotate(calc(var(--facetStep)*90deg)) translateY(-50em) scaleY(1.001);
animation: squeeze 3s linear alternate infinite;
}
@keyframes squeeze {
100% {
transform: rotate(calc(var(--facetStep)*90deg)) translateY(-50em) scaleY(.001);
}
}
.lid {
transform: translateZ(calc(50em*var(--facetDir))) rotateX(calc(-90deg*var(--facetDir))) scaleY(1.001);
animation: squash 3s linear alternate infinite;
--light: .33;
}
@keyframes squash {
100% {
transform: translateZ(calc(50em*var(--facetDir))) rotateX(calc(-90deg*var(--facetDir))) scaleY(.001);
}
}
.side:nth-child(odd) { --facetStep: 0; --facetStart: 2; --light: .5; }
.side:nth-child(2n) { --facetStep: 3; --facetStart: 4; --light: .5; }
.lid:nth-child(odd) { --facetDir: 1; --facetStart: 2; }
.lid:nth-child(2n) { --facetDir: -1; --facetStart: 0; }
.slice {
--diagAngle: calc(atan(var(--pyrH)/sin(-45deg))); /* 27.3678052deg */
--triangleW: calc(sin(45deg)/cos(var(--diagAngle))); /*0.796225217018em;*/
--triangleB: calc(sin(atan(sin(45deg))/2)*sin(60deg)*2); /* 0.524647623275 */
--triangleC: calc(2*sin(60deg)*sin(22.5deg)); /* 0.662827148071 */
--triAngleWB: calc(
asin(
( var(--triangleW)*var(--triangleW) + var(--triangleB)*var(--triangleB) - var(--triangleC)*var(--triangleC) ) /
(2 * var(--triangleW) * var(--triangleB) )
)
); /* asin( (0.796225217018*0.796225217018 + 0.524647623275*0.524647623275 - 0.662827148071*0.662827148071)/(2*0.796225217018*0.524647623275) ) = 34.22deg */
--triangleH: calc(var(--triangleB)*cos(var(--triAngleWB))); /* 43.382289309em */
width: calc(100em*var(--triangleW));
height: calc(100em*var(--triangleH));
--tipRatio: calc(var(--triangleB)*sin(var(--triAngleWB))/var(--triangleW));
--tipAngle: calc(asin( (sin(45deg)*sin(45deg)*sin(60deg)) / (var(--triangleH)) )); /* 86.4704302deg */
clip-path: polygon(0 0, calc(var(--tipRatio)*100%) 100%, 101% -1%);
right: 50%;
transform-origin: 100% 0;
transform:
rotateY(calc(45deg - 90deg*var(--sliceStep) - 45deg*var(--facetStart)))
rotate(var(--diagAngle))
rotateX(calc(var(--tipAngle)*var(--sliceDir)));
animation: shrink 3s cubic-bezier(.38,.26,.74,.57) alternate infinite;
}
@keyframes shrink {
100% {
clip-path: polygon(0 0, 49% calc(sin(45deg)*51em), 101% -1%);
}
}
.slice:is(:nth-child(1),:nth-child(2)) { --sliceStep: 0; }
.slice:is(:nth-child(3),:nth-child(4)) { --sliceStep: 1; }
.slice:is(:nth-child(5),:nth-child(6)) { --sliceStep: 2; }
.slice:is(:nth-child(7),:nth-child(8)) { --sliceStep: 3; }
.slice:nth-child(odd) { --sliceDir: 1; }
.slice:nth-child(2n) { --sliceDir: -1; }
.slice:nth-child(1) { background-color:hsl(45deg 100% calc(var(--light)*100%)); }
.slice:is(:nth-child(2),:nth-child(8)) { background-color:hsl(45deg 100% calc((var(--light) - .05)*100%)); }
.slice:is(:nth-child(3),:nth-child(7)) { background-color:hsl(45deg 100% calc((var(--light) - .1)*100%)); }
.slice:is(:nth-child(4),:nth-child(6)) { background-color:hsl(45deg 100% calc((var(--light) - .15)*100%)); }
.slice:nth-child(5) { background-color:hsl(45deg 100% calc((var(--light) - .2)*100%)); }
.slice::before {
content: '';
position: absolute;
inset: 0;
animation: fade 3s ease-in-out alternate infinite;
}
@keyframes fade {
0% {
opacity: 0;
}
}
.face:nth-child(2) .slice {
mask-image: radial-gradient(circle at 100% 0, transparent 10%, black 0);
mask-image: radial-gradient(circle at 100% 0, transparent 10%, black 0);
}
.face:nth-child(2) .slice:not(:nth-child(n+5))::after {
content: '';
position: absolute;
width: 10em;
aspect-ratio: 1;
left: 10em;
top: -5em;
background-image: radial-gradient(at 25% 25%, #fff 10%, #000 50%);
animation: eyePop 3s ease-in-out alternate infinite;
}
@keyframes eyePop {
0% {
border-radius: 50%;
}
}
.face:nth-child(1) .slice::before { background-color: hsl(45deg 100% 40%); }
.face:nth-child(2) .slice::before { background-color: hsl(45deg 100% 50%); }
.face:nth-child(3) .slice::before { background-color: hsl(45deg 100% 25%); }
.anchor {
inset: 0;
rotate: y 90deg;
animation: pucker 3s linear alternate infinite;
}
@keyframes pucker {
0% {
scale: calc(sin(60deg)*2) 1;
}
}
.fin {
width: 30em;
bottom: 50%;
aspect-ratio: 1;
border-radius: 50%;
border-bottom-right-radius: 0;
background-image: repeating-conic-gradient(at 100% 100%, #f808 0,#f800,#f808 5deg),linear-gradient(135deg, #f800, #f808);
transition: .5s ease-in-out;
clip-path: polygon(0 calc(100%*(1 - sin(45deg))), calc(100%*(1 - sin(45deg))) 0, 100% 0, 100% 100%, 0 100%);
animation: trim 3s ease-in-out alternate infinite;
transform-origin: 100% 100%;
animation: flap 3s ease-out infinite;
transform: scaleX(var(--finDir)) rotate(-45deg)
}
@keyframes flap {
0%, 100% {
rotate: y calc(-30deg*var(--finDir));
}
10% {
rotate: y calc(45deg*var(--finDir));
}
}
.fin:nth-child(odd) {
--finDir: 1;
right: 100%;
}
.fin:nth-child(2n) {
--finDir: -1;
right: 0;
}
@keyframes trim {
100% {
border-radius: 0;
clip-path: polygon(0 100%, 100% 0, 100% 0, 100% 100%, 0 100%);
}
}
/* GUI for play/pause */
input {
display: none;
}
label {
height: max(50em, 100px);
aspect-ratio: 1;
margin: min(7em, 35px);
align-self: start;
position: relative;
overflow: hidden;
}
label::before {
content: '';
position: absolute;
background: #fb0;
inset: calc((1 - sin(45deg))*50%);
aspect-ratio: 1;
transform: translateX(calc(-50%*sin(45deg))) rotate(45deg);
scale: 2 1;
clip-path: polygon(0 0, 50% 0, 50% 100%, 50% 100%, 50% 0, 100% 0, 100% 100%, 0 100%);
transition: .5s ease-in-out;
}
label:hover::before {
border-radius: 50%;
scale: 3 calc(2*sin(45deg));
}
input:not(:checked) ~ .scene :is(.pivot,.boxFish,.face,.slice,.anchor,.fin),
input:not(:checked) ~ .scene .slice::before,
input:not(:checked) ~ .scene .slice::after {
animation-play-state: paused;
}
input:checked ~ label::before {
transform: rotate(0);
scale: calc(sin(45deg)*200%);
clip-path: polygon(0 0, 40% 0, 40% 100%, 60% 100%, 60% 0, 100% 0, 100% 100%, 0 100%);
}
/* I talk about my low-poly pure-CSS renditions of submarine life on my blog:
Hashnode:
https://mackfitz.hashnode.dev/pure-css-low-poly-aquatic-life
DEV.to
https://dev.to/mackfitz/pure-css-low-poly-aquatic-life-ii5
*/
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.