jonnybarnes.uk/public/assets/frontend/snow-fall.js
Jonny Barnes 65a70a5440
Add some snow ❄️i
Also get to play with some web components
2023-12-17 14:15:45 +00:00

117 lines
No EOL
2.8 KiB
JavaScript

class Snow extends HTMLElement {
static random(min, max) {
return min + Math.floor(Math.random() * (max - min) + 1);
}
static attrs = {
count: "count", // default: 100
mode: "mode",
}
generateCss(mode, count) {
let css = [];
css.push(`
:host([mode="element"]) {
display: block;
position: relative;
overflow: hidden;
}
:host([mode="page"]) {
position: fixed;
top: 0;
left: 0;
right: 0;
}
:host([mode="page"]),
:host([mode="element"]) > * {
pointer-events: none;
}
:host([mode="element"]) ::slotted(*) {
pointer-events: all;
}
* {
position: absolute;
width: var(--snow-fall-size, 10px);
height: var(--snow-fall-size, 10px);
background: var(--snow-fall-color, rgba(255,255,255,.5));
border-radius: 50%;
}
`);
// using vw units (max 100)
let dimensions = { width: 100, height: 100 };
let units = { x: "vw", y: "vh"};
if(mode === "element") {
dimensions = {
width: this.firstElementChild.clientWidth,
height: this.firstElementChild.clientHeight,
};
units = { x: "px", y: "px"};
}
// Thank you @alphardex: https://codepen.io/alphardex/pen/dyPorwJ
for(let j = 1; j<= count; j++ ) {
let x = Snow.random(1, 100) * dimensions.width/100; // vw
let offset = Snow.random(-10, 10) * dimensions.width/100; // vw
let yoyo = Math.round(Snow.random(30, 100)); // % time
let yStart = yoyo * dimensions.height/100; // vh
let yEnd = dimensions.height; // vh
let scale = Snow.random(1, 10000) * .0001;
let duration = Snow.random(10, 30);
let delay = Snow.random(0, 30) * -1;
css.push(`
:nth-child(${j}) {
opacity: ${Snow.random(0, 1000) * 0.001};
transform: translate(${x}${units.x}, -10px) scale(${scale});
animation: fall-${j} ${duration}s ${delay}s linear infinite;
}
@keyframes fall-${j} {
${yoyo}% {
transform: translate(${x + offset}${units.x}, ${yStart}${units.y}) scale(${scale});
}
to {
transform: translate(${x + offset / 2}${units.x}, ${yEnd}${units.y}) scale(${scale});
}
}`)
}
return css.join("\n");
}
connectedCallback() {
// https://caniuse.com/mdn-api_cssstylesheet_replacesync
if(this.shadowRoot || !("replaceSync" in CSSStyleSheet.prototype)) {
return;
}
let count = parseInt(this.getAttribute(Snow.attrs.count)) || 100;
let mode;
if(this.hasAttribute(Snow.attrs.mode)) {
mode = this.getAttribute(Snow.attrs.mode);
} else {
mode = this.firstElementChild ? "element" : "page";
this.setAttribute(Snow.attrs.mode, mode);
}
let sheet = new CSSStyleSheet();
sheet.replaceSync(this.generateCss(mode, count));
let shadowroot = this.attachShadow({ mode: "open" });
shadowroot.adoptedStyleSheets = [sheet];
let d = document.createElement("div");
for(let j = 0, k = count; j<k; j++) {
shadowroot.appendChild(d.cloneNode());
}
shadowroot.appendChild(document.createElement("slot"));
}
}
customElements.define("snow-fall", Snow);