Custom element hearts for Valentine's Day

tirithen

Fredrik Söderström

Posted on February 13, 2022

Custom element hearts for Valentine's Day

Tomorrow is valentines day, so what could be more fitting than making a custom valentine-hearts element for your web page?

Coding might be for work, but sometimes it should just be for fun.

Lets create a custom element that can be added to a page like this:

<!DOCTYPE html>
<html lang="en">
    <head>
        ...
        <script type="module" src="/valentine-hearts.js"></script>
    </head>
    <body>
        <p>
            Lorem, ipsum dolor sit amet consectetur adipisicing elit.
            ...
        </p>
        <valentine-hearts></valentine-hearts>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Time for some fun!

class ValentineHearts extends HTMLElement {
    #shadowRoot;

    constructor() {
        super();
        this.#createShadowRoot();
    }

    #createShadowRoot() {
        this.#shadowRoot = this.attachShadow({ mode: 'open' });
        this.#shadowRoot.innerHTML = `
            <style>
                :host {
                    --heart-size: 1em;
                    --heart-color: red;
                    --heart-speed: 30s;

                    pointer-events: none;
                    display: block;
                    position: absolute;
                    top: 0;
                    left: 0;
                    width: 100%;
                    height: 100%;
                    z-index: 10000;
                    overflow: hidden;
                }

                heart {
                    opacity: 0;
                    display: block;
                    width: var(--heart-size);
                    height: var(--heart-size);
                    position: absolute;
                    left: calc(var(--heart-x-offset) - 4%);
                    background: var(--heart-color);
                    transform: rotate(45deg) scale(0);
                    transition: top var(--heart-speed) linear,
                                opacity 1s ease-in-out,
                                transform 1s ease-in-out;
                    animation-duration: 6s;
                    animation-iteration-count: infinite;
                    will-change: top, left, opacity, transform;
                }

                heart::before {
                    content: ' ';
                    display: block;
                    width: var(--heart-size);
                    height: var(--heart-size);
                    border-radius: calc(var(--heart-size) / 2);
                    position: absolute;
                    top: 0;
                    left: calc(var(--heart-size) * -0.5);
                    background: var(--heart-color);
                }

                heart::after {
                    content: ' ';
                    display: block;
                    width: var(--heart-size);
                    height: var(--heart-size);
                    border-radius: calc(var(--heart-size) / 2);
                    position: absolute;
                    top: calc(var(--heart-size) * -0.5);
                    left: 0;
                    background: var(--heart-color);
                }

                @keyframes sway {
                    0% {
                        left: calc(var(--heart-x-offset) - 4%);
                    }

                    50% {
                        left: calc(var(--heart-x-offset) + 4%);
                    }

                    100% {
                        left: calc(var(--heart-x-offset) - 4%);
                    }
                }
            </style>
        `;
    }

    connectedCallback() {
        setInterval(() => this.#spawnHeart(), 1000);

        for (let i = 0; i < 20; i++) {
            this.#spawnHeart(Math.round(Math.random() * 100));
        }

        this.addEventListener('resize', () => this.#resize());
        this.#resize();
    }

    #resize() {
        this.style.width = `${window.clientWidth}px`;
        this.style.height = `${document.body.clientHeight}px`;
    }

    #spawnHeart(top) {
        const heart = document.createElement('heart');

        heart.style.top = `${top || Math.round(Math.random() * 25 + 75)}%`;
        heart.style.setProperty('--heart-x-offset', `${Math.random() * 100}%`);

        const brightness = Math.random();
        heart.style.setProperty(
            '--heart-color',
            `rgb(255, ${Math.round(brightness * 150)}, ${Math.round(brightness * 50)})`
        );

        this.#shadowRoot.appendChild(heart);

        setTimeout(() => {
            heart.style.animationName = 'sway';
        }, Math.random() * 3000);

        setTimeout(() => {
            heart.style.top = '-2%';
            heart.style.opacity = 0.5;
            heart.style.transform = `rotate(45deg) scale(${Math.random() * 0.4 + 0.8})`;

            function removeHeart(event) {
                if (event.propertyName === 'top') {
                    heart.parentNode.removeChild(heart);
                    heart.removeEventListener('transitionend', removeHeart);
                }
            }

            heart.addEventListener('transitionend', removeHeart);
        }, 100);
    }
}

customElements.define('valentine-hearts', ValentineHearts);
Enter fullscreen mode Exit fullscreen mode

Happy Valentine's Day!

💖 💪 🙅 🚩
tirithen
Fredrik Söderström

Posted on February 13, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Custom element hearts for Valentine's Day
javascript Custom element hearts for Valentine's Day

February 13, 2022