โ† SVG & icons

Waving Hand

A hand rocks back and forth from the wrist in a friendly wave, with little motion lines at the end of each swing, then settles with a wobble. Use the drawn cartoon hand, or make your own emoji or image wave.

Hello there ๐Ÿ‘‹

How to use it

  1. Paste the snippet just before </head>.
  2. Add data-waving-hand to an empty <span> for the drawn hand. Size it with font-size.
  3. Or put your own hand inside it, e.g. <span data-waving-hand>๐Ÿ‘‹</span>, and that waves instead.

Options

Add any of these attributes to the same element.

data-loopWave again every few seconds.
data-intervalPause between waves, when looping. Default 2400 ms.
data-angleHow far it swings, in degrees. Default 18.
data-hoverAlso wave when hovered or tapped.
data-colorSkin colour of the drawn hand. Default #fbbf24.

It waves when scrolled into view. Call animacious.wave(el) to wave it from code, e.g. when someone signs in. With reduced motion it stays still.

1 ยท Snippet
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
  (function () {
    // Waving Hand: a hand rocks back and forth from the wrist in a friendly
    // wave, then rests. Leave the element empty for a drawn cartoon hand, or
    // put your own in it (an emoji, an image, an SVG) and that waves instead.
    // Usage: <span data-waving-hand></span>   or   <span data-waving-hand>๐Ÿ‘‹</span>
    //   Size it with font-size.
    // Optional: data-loop (wave again every few seconds), data-interval (ms
    // between waves when looping, default 2400), data-angle (degrees of the
    // swing, default 18), data-hover (also wave when hovered or tapped),
    // data-color (skin colour of the drawn hand, default #fbbf24).
    // Script use: animacious.wave(el) waves it from code.
    var DEFAULTS = { interval: 2400, angle: 18, color: '#fbbf24' };
    var NS = 'http://www.w3.org/2000/svg';

    var css =
      '.wvh{display:inline-block;line-height:1;transform-origin:70% 85%}' +
      '.wvh-drawn{transform-origin:48% 92%}' +
      '.wvh svg{display:block;width:1em;height:1em;overflow:visible}' +
      '.wvh-line{opacity:0}';
    var style = document.createElement('style');
    style.textContent = css;
    document.head.appendChild(style);

    function reduced() { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; }

    // A cartoon hand: thick round strokes for the fingers, drawn twice, once
    // wider in a darker tone for the outline.
    function hand(color) {
      var parts =
        '<path d="M23 34 L20 13"/><path d="M30.5 31 L30.5 8.5"/><path d="M38 32 L39.5 11"/><path d="M44 36 L48 19"/><path d="M21 44 L10.5 33"/>' +
        '<rect x="18" y="28" width="29" height="25" rx="12"/>';
      return '<g stroke="#b45309" stroke-width="11.5" stroke-linecap="round" stroke-linejoin="round" fill="#b45309">' + parts + '</g>' +
        '<g stroke="' + color + '" stroke-width="7.5" stroke-linecap="round" stroke-linejoin="round" fill="' + color + '">' + parts + '</g>' +
        '<path d="M26 45 Q32 49 38 45" fill="none" stroke="#b45309" stroke-width="1.6" stroke-linecap="round" opacity=".45"/>' +
        '<rect x="20" y="51" width="25" height="10" rx="3" fill="#8b5cf6"/>' +
        // Motion lines either side, shown at the end of each swing.
        '<g fill="none" stroke="#a1a1aa" stroke-width="2.4" stroke-linecap="round">' +
        '<path class="wvh-line wvh-r" d="M55 12 Q59 17 58 23 M60 6 Q66 14 64 24"/>' +
        '<path class="wvh-line wvh-l" d="M8 18 Q4 23 5 29 M3 11 Q-3 19 -1 29"/></g>';
    }

    function wave(el) {
      var s = el._wvh;
      if (s.busy || reduced()) return;
      s.busy = true;
      var a = s.angle;
      var tl = anime.createTimeline({ onComplete: function () { s.busy = false; } });
      tl.add(el, {
        rotate: [
          { to: a, duration: 180, ease: 'out(2)' },
          { to: -a * 0.45, duration: 200, ease: 'inOut(2)' },
          { to: a, duration: 200, ease: 'inOut(2)' },
          { to: -a * 0.3, duration: 200, ease: 'inOut(2)' },
          { to: a * 0.6, duration: 200, ease: 'inOut(2)' },
          { to: 0, duration: 420, ease: 'outElastic(1, .6)' }
        ]
      });
      if (s.right) {
        tl.add(s.right, { opacity: [{ to: 1, duration: 90 }, { to: 0, duration: 220 }] }, 60)
          .add(s.left, { opacity: [{ to: 1, duration: 90 }, { to: 0, duration: 220 }] }, 260)
          .add(s.right, { opacity: [{ to: 1, duration: 90 }, { to: 0, duration: 220 }] }, 460);
      }
    }

    function setup(el) {
      var d = el.dataset;
      el.classList.add('wvh');
      var s = { angle: +d.angle || DEFAULTS.angle };
      el._wvh = s;
      if (!el.innerHTML.trim()) {
        el.classList.add('wvh-drawn');
        var svg = document.createElementNS(NS, 'svg');
        svg.setAttribute('viewBox', '0 0 64 64');
        svg.setAttribute('aria-hidden', 'true');
        svg.innerHTML = hand(d.color || DEFAULTS.color);
        el.appendChild(svg);
        s.right = svg.querySelector('.wvh-r');
        s.left = svg.querySelector('.wvh-l');
        if (!el.getAttribute('aria-label') && !el.closest('[aria-hidden="true"]')) {
          el.setAttribute('role', 'img');
          el.setAttribute('aria-label', 'A waving hand');
        }
      }
      if (d.hover !== undefined && d.hover !== 'false') {
        el.addEventListener('pointerenter', function () { wave(el); });
        el.addEventListener('pointerdown', function () { wave(el); });
      }
    }

    function play(el) {
      var d = el.dataset;
      var loop = d.loop !== undefined && d.loop !== 'false';
      var every = +d.interval || DEFAULTS.interval;
      var s = el._wvh;
      var tick = function () {
        if (s.visible) wave(el);
        if (loop) s.timer = setTimeout(tick, every + 1400);
      };
      s.timer = setTimeout(tick, 250);
    }

    function init() {
      var io = new IntersectionObserver(function (entries) {
        entries.forEach(function (e) {
          var s = e.target._wvh;
          s.visible = e.isIntersecting;
          if (e.isIntersecting && !s.started) {
            s.started = true;
            play(e.target);
          }
        });
      }, { threshold: 0.2 });
      document.querySelectorAll('[data-waving-hand]').forEach(function (el) {
        if (el._wvh) return;
        setup(el);
        if (!reduced()) io.observe(el);
      });
    }

    window.animacious = window.animacious || {};
    window.animacious.wave = function (el) { if (el._wvh) wave(el); };
    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
    else init();
  })();
</script>
2 ยท Your HTML
<h1>Hello there <span data-waving-hand data-hover>๐Ÿ‘‹</span></h1>

<!-- the drawn hand, waving every few seconds: -->
<span data-waving-hand data-loop style="font-size: 96px"></span>