Skip to content

Instantly share code, notes, and snippets.

@sufianrhazi
Last active November 21, 2024 17:10
Show Gist options
  • Save sufianrhazi/39fb369c1745e73f23fa9a99573858e4 to your computer and use it in GitHub Desktop.
Save sufianrhazi/39fb369c1745e73f23fa9a99573858e4 to your computer and use it in GitHub Desktop.
(function () {
const dancers = [
["(•_•)", "<) )╯", " / \\ "],
["(•_•)", "\\( (>", " / \\ "],
["(•_•)", "╰( (>", " / \\ "],
["(•_•)", "<) )/", " / \\ "],
];
class DancingManElement extends HTMLElement {
// Call attributeChangedCallback() when any of these attributes change
static observedAttributes = ["left", "right"];
constructor() {
super();
// Local state (our stuff is prefixed with _)
this._leftSide = true;
this._clicks = 0;
// mode: "open" = the ShadowRoot instance will be at this.shadowRoot
this.attachShadow({ mode: "open" });
// We're a button, so respond to clicks:
this.addEventListener("click", (e) => {
e.preventDefault();
this._clicks += 1;
this._leftSide = !this._leftSide;
this._rerender();
});
// A fun font would be nice to have, so load it here
const font = new FontFace(
"Comic Neue",
"url(https://fonts.gstatic.com/s/comicneue/v8/4UaErEJDsxBrF37olUeD_xHM8pxULilENlY.woff2)",
{
style: "normal",
weight: "700",
},
);
font.load().then((loaded_face) => {
// In order to use the font in our custom element, we must add it to the document
document.fonts.add(loaded_face);
});
}
connectedCallback() {
// When mounted to the document, we rerender
this._rerender();
}
attributeChangedCallback(name, oldValue, newValue) {
// Each attribute we observe changes the content
this._rerender();
}
_rerender() {
// Default message values:
const leftMessage = this.getAttribute("left") || "groovy";
const rightMessage = this.getAttribute("right") || "dig it";
const dancer = dancers[this._clicks % dancers.length].join("\n");
// Simplistic render to the Shadow DOM
this.shadowRoot.innerHTML = `
<style>
/* Shine animation for when the element is (re)rendered */
@keyframes shine {
0% { transform: translateX(100%); }
100% { transform: translateX(-100%); }
}
.dancing-man {
/* 3 even columns: left dancer right */
display: inline-grid;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
gap: 12px;
/* Looks like a badge/button */
cursor: pointer;
border: 2px black solid;
border-radius: 50%;
background: linear-gradient(30deg, #e0e0e0, #f9f9f9, #e0e0e0);
padding: 8px 16px;
color: #000000;
/* An offset container for the shine animation */
position: relative;
overflow: hidden;
}
.dancing-man::before {
/* The shine animation gradient */
content: "";
position: absolute;
width: 100%;
height: 100%;
transform: translateX(-100%);
background: linear-gradient(
30deg,
transparent 20%,
transparent 40%,
rgb(255, 255, 255, 0.4) 50%,
rgb(255, 255, 255, 0.4) 55%,
transparent 70%,
transparent 100%
);
animation: shine 400ms 100ms 1 ease-in;
}
.dancing-man:active {
/* Darker colors while pressed */
background: linear-gradient(30deg, #c4c4c4, #f9f9f9, #c4c4c4);
border-color: #404040;
}
.message_left,
.message_right {
font-family: 'Comic Neue';
font-weight: 700;
font-style: normal;
visibility: hidden;
}
.dancing-man.right .message_right,
.dancing-man.left .message_left {
visibility: visible;
}
</style>
<button class="dancing-man ${this._leftSide ? "left" : "right"}">
<div class="message_left">${leftMessage}</div>
<pre class="dancer">${dancer}</pre>
<div class="message_right">${rightMessage}</div>
</button>
`;
}
}
customElements.define("dancing-man", DancingManElement);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment