Vai al contenuto
animorio
EN
Tutti i componenti

Il menu a coltellino

Menu, Fisica, SVG, HoverGSAP, Solo CSSImpegnativaPeso compresso: Demo 15 KBGSAP 28 KBGratisScarica lo zip

Il menu delle azioni di un progetto come un coltellino multiuso: chiuso è il manico di smalto rosso, con le guance d’acciaio e i dorsi degli attrezzi che si vedono di taglio. Premuto, gli attrezzi escono ruotando intorno al perno uno dopo l’altro, dal centro del ventaglio verso fuori, ognuno con la sua molla e lo scatto contro la tacca d’arresto; ogni attrezzo è un’azione incisa nell’acciaio, e Delete è la lama. Scelto un attrezzo, gli altri rientrano e lui fa il suo gesto prima di richiudersi per ultimo.

Il codice

Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nelle schede React e Vue la stessa demo è già un componente, che spegne le sue animazioni quando esce dalla pagina.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The pocket knife menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/saira@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- the shared light of the steel: each tool keeps its own copy of these gradients, turned against its own angle,
       so the reflections stay still while the tool turns through them -->
  <svg class="defs" width="0" height="0" aria-hidden="true" focusable="false">
    <defs>
      <linearGradient id="kn-steel" gradientUnits="userSpaceOnUse" x1="0" y1="-190" x2="0" y2="190">
        <stop offset="0" stop-color="#c3c9cf"/>
        <stop offset="0.13" stop-color="#f2f4f6"/>
        <stop offset="0.24" stop-color="#9aa3ab"/>
        <stop offset="0.34" stop-color="#dfe3e7"/>
        <stop offset="0.43" stop-color="#aab2b9"/>
        <stop offset="0.5" stop-color="#f6f8f9"/>
        <stop offset="0.57" stop-color="#959ea7"/>
        <stop offset="0.67" stop-color="#d5dade"/>
        <stop offset="0.79" stop-color="#a2aab2"/>
        <stop offset="0.9" stop-color="#ebeef0"/>
        <stop offset="1" stop-color="#b8bfc6"/>
      </linearGradient>
      <!-- the flat of the blade: a satin finish, a little warmer -->
      <linearGradient id="kn-satin" gradientUnits="userSpaceOnUse" x1="0" y1="-190" x2="0" y2="190">
        <stop offset="0" stop-color="#d6d8d9"/>
        <stop offset="0.16" stop-color="#f6f6f4"/>
        <stop offset="0.3" stop-color="#c3c7ca"/>
        <stop offset="0.45" stop-color="#eeefee"/>
        <stop offset="0.55" stop-color="#c9cdd0"/>
        <stop offset="0.7" stop-color="#f1f2f1"/>
        <stop offset="0.85" stop-color="#bcc1c5"/>
        <stop offset="1" stop-color="#e4e6e6"/>
      </linearGradient>
      <!-- the ground edge of the blade: polished like a mirror, so its reflections are hard -->
      <linearGradient id="kn-mirror" gradientUnits="userSpaceOnUse" x1="0" y1="-190" x2="0" y2="190">
        <stop offset="0" stop-color="#ffffff"/>
        <stop offset="0.1" stop-color="#5e666f"/>
        <stop offset="0.19" stop-color="#f4f6f8"/>
        <stop offset="0.27" stop-color="#3f464e"/>
        <stop offset="0.36" stop-color="#d7dce1"/>
        <stop offset="0.45" stop-color="#ffffff"/>
        <stop offset="0.52" stop-color="#4a525a"/>
        <stop offset="0.61" stop-color="#e8ebee"/>
        <stop offset="0.7" stop-color="#6b737c"/>
        <stop offset="0.8" stop-color="#fbfcfd"/>
        <stop offset="0.9" stop-color="#565e66"/>
        <stop offset="1" stop-color="#eef1f3"/>
      </linearGradient>
      <!-- a reflection crossing the steel: a bright core between two darker bands, so it shows on bright steel too -->
      <linearGradient id="kn-glint" x1="0" y1="0" x2="1" y2="0">
        <stop offset="0" stop-color="#1c232a" stop-opacity="0"/>
        <stop offset="0.2" stop-color="#1c232a" stop-opacity="0.5"/>
        <stop offset="0.36" stop-color="#ffffff" stop-opacity="0.85"/>
        <stop offset="0.5" stop-color="#ffffff" stop-opacity="1"/>
        <stop offset="0.64" stop-color="#ffffff" stop-opacity="0.85"/>
        <stop offset="0.8" stop-color="#1c232a" stop-opacity="0.5"/>
        <stop offset="1" stop-color="#1c232a" stop-opacity="0"/>
      </linearGradient>
      <linearGradient id="kn-brass" gradientUnits="userSpaceOnUse" x1="0" y1="-3.2" x2="0" y2="3.2">
        <stop offset="0" class="br-0"/>
        <stop offset="0.35" class="br-1"/>
        <stop offset="0.75" class="br-2"/>
        <stop offset="1" class="br-3"/>
      </linearGradient>
      <radialGradient id="kn-screw" cx="0.38" cy="0.32" r="0.78">
        <stop offset="0" stop-color="#ffffff"/>
        <stop offset="0.35" stop-color="#d3d8dc"/>
        <stop offset="0.8" stop-color="#737a81"/>
        <stop offset="1" stop-color="#3d4248"/>
      </radialGradient>
    </defs>
  </svg>

  <main class="stage">
    <!-- the pocket knife menu: the actions of a project as the tools of a multitool. Closed, it is the handle, red
         enamel on steel liners, with the backs of the tools showing along its edges. Pressed, the tools flick out
         round the pivot one after another, each on its own back spring, snap into their stops and fan out; each tool
         is an action, engraved with its name. The blade is Delete -->
    <div class="knife" data-knife>
      <div class="knife__body" data-body>
        <button class="handle" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="knife-menu" data-handle data-autoclic>
          <span class="handle__press">
            <svg class="handle__art" viewBox="0 0 204 60" aria-hidden="true">
              <defs>
                <linearGradient id="kn-liner" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0" stop-color="#eef1f3"/>
                  <stop offset="0.05" stop-color="#8b939a"/>
                  <stop offset="0.09" stop-color="#4b5258"/>
                  <stop offset="0.5" stop-color="#2c3035"/>
                  <stop offset="0.91" stop-color="#4b5258"/>
                  <stop offset="0.955" stop-color="#a7aeb5"/>
                  <stop offset="1" stop-color="#5d646b"/>
                </linearGradient>
                <linearGradient id="kn-enamel" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0" class="en-0"/>
                  <stop offset="0.07" class="en-1"/>
                  <stop offset="0.42" class="en-2"/>
                  <stop offset="0.8" class="en-3"/>
                  <stop offset="0.93" class="en-4"/>
                  <stop offset="1" class="en-5"/>
                </linearGradient>
                <radialGradient id="kn-depth" cx="0.5" cy="0.62" r="0.62">
                  <stop offset="0" class="en-glow" stop-opacity="0.4"/>
                  <stop offset="1" class="en-glow" stop-opacity="0"/>
                </radialGradient>
                <linearGradient id="kn-gloss" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0" stop-color="#ffffff" stop-opacity="0.66"/>
                  <stop offset="0.5" stop-color="#ffffff" stop-opacity="0.16"/>
                  <stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
                </linearGradient>
                <linearGradient id="kn-sweep" x1="0" y1="0" x2="1" y2="0">
                  <stop offset="0" stop-color="#ffffff" stop-opacity="0"/>
                  <stop offset="0.5" stop-color="#ffffff" stop-opacity="0.55"/>
                  <stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
                </linearGradient>
                <radialGradient id="kn-rivet" cx="0.36" cy="0.3" r="0.8">
                  <stop offset="0" stop-color="#ffffff"/>
                  <stop offset="0.3" stop-color="#dfe3e6"/>
                  <stop offset="0.75" stop-color="#7d848b"/>
                  <stop offset="1" stop-color="#454b51"/>
                </radialGradient>
                <clipPath id="kn-scale-clip"><rect x="2" y="5" width="200" height="50" rx="25"/></clipPath>
                <clipPath id="kn-rim-clip"><rect width="204" height="60" rx="30"/></clipPath>
              </defs>
              <!-- the steel liners, and between them the backs of the tools seen edge-on, each with its nail nick -->
              <rect width="204" height="60" rx="30" fill="url(#kn-liner)"/>
              <g class="handle__backs" clip-path="url(#kn-rim-clip)">
                <rect data-back="share" x="40" y="1" width="96" height="1.2" rx="0.6"/>
                <rect data-back="duplicate" x="36" y="2.3" width="116" height="1.1" rx="0.55"/>
                <rect data-back="rename" x="38" y="3.5" width="112" height="1" rx="0.5"/>
                <rect data-back="export" x="36" y="55.5" width="128" height="1.1" rx="0.55"/>
                <rect data-back="delete" x="34" y="56.7" width="134" height="1.3" rx="0.65"/>
              </g>
              <rect x="1.4" y="4.4" width="201.2" height="51.2" rx="25.6" fill="#0b0c0e"/>
              <rect x="2" y="5" width="200" height="50" rx="25" fill="url(#kn-enamel)"/>
              <g clip-path="url(#kn-scale-clip)">
                <ellipse cx="102" cy="38" rx="92" ry="22" fill="url(#kn-depth)"/>
                <rect x="13" y="7.2" width="178" height="15.5" rx="7.75" fill="url(#kn-gloss)"/>
                <rect class="handle__sweep" x="-70" y="0" width="46" height="60" fill="url(#kn-sweep)" data-sweep/>
              </g>
              <rect x="2.5" y="5.5" width="199" height="49" rx="24.5" fill="none" stroke="#ffffff" stroke-opacity="0.16"/>
              <!-- the pivot and the rivet at the far end, set into the enamel -->
              <circle class="en-well" cx="30" cy="30" r="7"/>
              <circle cx="30" cy="30" r="5.8" fill="url(#kn-rivet)"/>
              <circle class="en-well" cx="174" cy="30" r="5.8"/>
              <circle cx="174" cy="30" r="4.7" fill="url(#kn-rivet)"/>
            </svg>
            <!-- the steel plate inlaid in the enamel: it says what the knife is, and what it has just done -->
            <span class="plate" aria-hidden="true" data-plate>
              <span class="plate__face" data-face>
                <svg class="plate__icon" viewBox="0 0 24 24"><rect class="solid" x="9.2" y="15" width="13.6" height="5" rx="2.5"/><path class="solid fan" d="M9.9 15.6 3.3 3.9q-.2-.8.6-.4 6.9 4.6 8.6 11.1z"/></svg>
                <span class="plate__word">Actions</span>
              </span>
            </span>
          </span>
          <span class="sr" data-name>Actions</span>
        </button>

        <!-- the tools: each one turns round the pivot. The menu reads from the top tool to the bottom one -->
        <div class="tools" id="knife-menu" role="menu" aria-label="Project actions" tabindex="-1" data-menu inert>
          <div class="tool-slot" data-slot>
            <button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="share">
              <!-- the awl, with its sewing eye -->
              <svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
                <defs>
                  <linearGradient id="kn-steel-share" href="#kn-steel" data-light/>
                  <clipPath id="kn-clip-share"><path d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/></clipPath>
                </defs>
                <path class="tool__ring" d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/>
                <path class="tool__body" fill="url(#kn-steel-share)" d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/>
                <g clip-path="url(#kn-clip-share)">
                  <path class="tool__brush" d="M0 -9.5H170M0 -6H170M0 -2.5H170M0 1.5H170M0 5H170M0 8.5H170"/>
                  <path class="tool__facet" d="M106 -12C124 -12 141 -7 160 -1.3L157 -0.4C141 -4.4 124 -6.8 108 -7.2Z"/>
                  <rect class="tool__hole" x="126" y="-1.7" width="13" height="3.4" rx="1.7"/>
                  <path class="tool__lip" d="M127.5 2.2H137.5"/>
                  <path class="tool__nick" d="M40 -8.4Q46.5 -5.4 53 -8.4"/>
                  <path class="tool__lip" d="M40.5 -7.1Q46.5 -4.3 52.5 -7.1"/>
                  <rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
                </g>
                <path class="tool__lit" data-lit="top" d="M0 -11.5H106C124 -11.5 141 -6.6 159.5 -1"/>
                <path class="tool__lit" data-lit="bottom" d="M0 11.5H106C124 11.5 141 6.6 159.5 1"/>
                <path class="tool__hit" d="M0 -12H106C124 -12 141 -7 160 -1.3Q162 0 160 1.3C141 7 124 12 106 12H0A12 12 0 0 1 0 -12Z"/>
              </svg>
              <span class="tool__etch" style="--x0: 56; --x1: 124; --cy: 0">
                <svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.6 9.8H7.2a1.9 1.9 0 0 0-1.9 1.9v6.4A1.9 1.9 0 0 0 7.2 20h9.6a1.9 1.9 0 0 0 1.9-1.9v-6.4a1.9 1.9 0 0 0-1.9-1.9h-1.4"/><path d="M12 14.6V3.8M8.6 7.2 12 3.8l3.4 3.4"/></svg>
                <span class="tool__label">Share</span>
              </span>
            </button>
          </div>
          <div class="tool-slot" data-slot>
            <button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="duplicate" data-autoclic>
              <!-- the scissors: the upper blade opens on its screw -->
              <svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
                <defs>
                  <linearGradient id="kn-steel-duplicate" href="#kn-steel" data-light/>
                  <clipPath id="kn-clip-duplicate"><path d="M0 -12H128V12H0A12 12 0 0 1 0 -12Z"/><path d="M124 0.6L164 2.2Q166.4 3.2 164 4.4C153 8 140 12 124 12Z"/></clipPath>
                </defs>
                <path class="tool__ring" d="M0 -12H124C141 -12 154 -6.4 165 0.6Q167 2.4 164 4.4C153 8 140 12 124 12H0A12 12 0 0 1 0 -12Z"/>
                <path class="tool__body" fill="url(#kn-steel-duplicate)" d="M0 -12H128V12H0A12 12 0 0 1 0 -12Z"/>
                <path class="tool__body" fill="url(#kn-steel-duplicate)" d="M124 0.6L164 2.2Q166.4 3.2 164 4.4C153 8 140 12 124 12Z"/>
                <g clip-path="url(#kn-clip-duplicate)">
                  <path class="tool__brush" d="M0 -9.5H170M0 -6H170M0 -2.5H170M0 1.5H170M0 5H170M0 8.5H170"/>
                  <path class="tool__nick" d="M44 -8.4Q50.5 -5.4 57 -8.4"/>
                  <path class="tool__lip" d="M44.5 -7.1Q50.5 -4.3 56.5 -7.1"/>
                  <rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
                </g>
                <path class="tool__seam" d="M127.4 -11.4V11.4"/>
                <g data-blade>
                  <path class="tool__body" fill="url(#kn-steel-duplicate)" d="M124 -12C141 -12 154 -6.4 165 0.6Q166.6 1.6 164.4 2.2L124 0.6Z"/>
                  <path class="tool__seam" d="M128 0.7L164 2.2"/>
                  <path class="tool__lit" data-lit="top" d="M124 -11.5C141 -11.5 154 -6 164.6 0.8"/>
                </g>
                <circle class="tool__screw" cx="136" cy="0" r="3.4"/>
                <path class="tool__slot" d="M134.3 -1 137.7 1"/>
                <path class="tool__lit" data-lit="top" d="M0 -11.5H124"/>
                <path class="tool__lit" data-lit="bottom" d="M0 11.5H124C140 11.5 153 7.6 163.6 4"/>
                <path class="tool__hit" d="M0 -12H124C141 -12 154 -6.4 165 0.6Q167 2.4 164 4.4C153 8 140 12 124 12H0A12 12 0 0 1 0 -12Z"/>
              </svg>
              <span class="tool__etch" style="--x0: 52; --x1: 124; --cy: 0">
                <svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="8.6" y="8.6" width="11.4" height="11.4" rx="2.4"/><path d="M15.4 8.6V6.4A2.4 2.4 0 0 0 13 4H6.4A2.4 2.4 0 0 0 4 6.4V13a2.4 2.4 0 0 0 2.4 2.4h2.2"/></svg>
                <span class="tool__label">Duplicate</span>
              </span>
            </button>
          </div>
          <div class="tool-slot" data-slot>
            <button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="rename">
              <!-- the pen: a knurled barrel and a brass point -->
              <svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
                <defs>
                  <linearGradient id="kn-steel-rename" href="#kn-steel" data-light/>
                  <clipPath id="kn-clip-rename"><path d="M0 -11H119Q123 -11 124 -8H129L153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/></clipPath>
                </defs>
                <path class="tool__ring" d="M0 -11H119Q123 -11 124 -8H129L153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/>
                <path class="tool__body" fill="url(#kn-steel-rename)" d="M0 -11H119Q123 -11 124 -8H129L153 -3V3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/>
                <path class="tool__nib" d="M153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3Z"/>
                <g clip-path="url(#kn-clip-rename)">
                  <path class="tool__brush" d="M0 -8.5H170M0 -5H170M0 -1.5H170M0 2H170M0 5.5H170M0 9H170"/>
                  <rect class="tool__collar" x="124" y="-9" width="5" height="18"/>
                  <path class="tool__knurl" d="M132.5 -7.2V7.2M136 -6.5V6.5M139.5 -5.8V5.8M143 -5.1V5.1M146.5 -4.4V4.4"/>
                  <path class="tool__nick" d="M40 -7.6Q46.5 -4.8 53 -7.6"/>
                  <path class="tool__lip" d="M40.5 -6.3Q46.5 -3.7 52.5 -6.3"/>
                  <rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
                </g>
                <path class="tool__lit" data-lit="top" d="M0 -10.5H119Q122.6 -10.5 123.6 -8M129 -7.5L153 -2.6"/>
                <path class="tool__lit" data-lit="bottom" d="M0 10.5H119Q122.6 10.5 123.6 8M129 7.5L153 2.6"/>
                <path class="tool__hit" d="M0 -11H119Q123 -11 124 -8H129L153 -3L160.6 -1.3Q163.4 0 160.6 1.3L153 3L129 8H124Q123 11 119 11H0A11 11 0 0 1 0 -11Z"/>
              </svg>
              <span class="tool__etch" style="--x0: 54; --x1: 120; --cy: 0">
                <svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 19.5h4.2L19.4 8.8a2.1 2.1 0 0 0 0-3l-1.2-1.2a2.1 2.1 0 0 0-3 0L4.5 15.3z"/><path d="M13.6 6.2l4.2 4.2"/></svg>
                <span class="tool__label">Rename</span>
              </span>
            </button>
          </div>
          <div class="tool-slot" data-slot>
            <button class="tool" type="button" role="menuitem" tabindex="-1" data-tool="export">
              <!-- the cap lifter, with its screwdriver end -->
              <svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
                <defs>
                  <linearGradient id="kn-steel-export" href="#kn-steel" data-light/>
                  <clipPath id="kn-clip-export"><path d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/></clipPath>
                </defs>
                <path class="tool__ring" d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/>
                <path class="tool__body" fill="url(#kn-steel-export)" d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/>
                <g clip-path="url(#kn-clip-export)">
                  <path class="tool__brush" d="M0 -9.5H170M0 -6H170M0 -2.5H170M0 1.5H170M0 5H170M0 8.5H170"/>
                  <path class="tool__facet" d="M155 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L155 -0.6Z"/>
                  <path class="tool__nick" d="M44 8.4Q50.5 5.4 57 8.4"/>
                  <path class="tool__lip" d="M44.5 9.6Q50.5 6.8 56.5 9.6"/>
                  <rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
                </g>
                <path class="tool__lip" d="M147.6 -0.8Q142 -1.6 141 -5.6"/>
                <path class="tool__lit" data-lit="top" d="M0 -11.5H138.6Q141.2 -11.5 140.9 -9.4"/>
                <path class="tool__lit" data-lit="bottom" d="M0 11.5H160Q163.6 11.5 164 8.5"/>
                <path class="tool__hit" d="M0 12H160Q164 12 164.6 8.5V2.5Q164.4 0.6 161.6 0.2L148 -1.4Q141.6 -2.2 140.4 -6.2L141.4 -9.4Q141.8 -12 138.6 -12H0A12 12 0 0 0 0 12Z"/>
              </svg>
              <span class="tool__etch" style="--x0: 56; --x1: 132; --cy: 0">
                <svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v10.6M7.8 10.6 12 14.8l4.2-4.2"/><path d="M4.4 14.6v3.2A2.2 2.2 0 0 0 6.6 20h10.8a2.2 2.2 0 0 0 2.2-2.2v-3.2"/></svg>
                <span class="tool__label">Export</span>
              </span>
            </button>
          </div>
          <div class="tool-slot" data-slot>
            <button class="tool tool--blade" type="button" role="menuitem" tabindex="-1" data-tool="delete">
              <!-- the blade: a satin flat, a mirror ground edge and the name etched in red -->
              <svg class="tool__art" viewBox="-16 -24 192 48" aria-hidden="true">
                <defs>
                  <linearGradient id="kn-satin-delete" href="#kn-satin" data-light/>
                  <linearGradient id="kn-mirror-delete" href="#kn-mirror" data-light/>
                  <clipPath id="kn-clip-delete"><path d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/></clipPath>
                </defs>
                <path class="tool__ring" d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/>
                <path class="tool__body" fill="url(#kn-satin-delete)" d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/>
                <g clip-path="url(#kn-clip-delete)">
                  <path class="tool__brush" d="M0 -2H172M0 2H172M0 6H172M0 10H172"/>
                  <path class="tool__grind" fill="url(#kn-mirror-delete)" d="M22 -14H114C138 -14 156 -6.4 169.4 2.6L167 3.4C154 -1.2 136 -5.6 113 -5.6H25Q23 -5.6 22 -8Z"/>
                  <path class="tool__shoulder" d="M25 -5.6H113C136 -5.6 154 -1.2 167 3.4"/>
                  <path class="tool__nick" d="M40 9.6Q47 6.6 54 9.6"/>
                  <path class="tool__lip" d="M40.5 10.8Q47 8 53.5 10.8"/>
                  <rect class="tool__glint" x="-26" y="-26" width="52" height="52" data-glint/>
                </g>
                <path class="tool__edge" d="M22 -13.6H114C138 -13.6 156 -6 169 2.8"/>
                <path class="tool__run" pathLength="100" d="M22 -13.6H114C138 -13.6 156 -6 169 2.8" data-run/>
                <path class="tool__lit" data-lit="top" d="M22 -13.5H114C138 -13.5 156 -6 169 2.4"/>
                <path class="tool__lit" data-lit="bottom" d="M0 13.5H122C144 13.5 160 10.6 169.5 4.4"/>
                <path class="tool__hit" d="M0 14H122C144 14 160 11 170 4.6Q171.2 3.6 169.4 2.6C156 -6.4 138 -14 114 -14H22Q19 -14 18 -12H0A13 13 0 0 0 0 14Z"/>
              </svg>
              <span class="tool__etch" style="--x0: 52; --x1: 124; --cy: 4.2">
                <svg class="tool__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4.4 6.9h15.2M9.6 6.9V5.2A1.2 1.2 0 0 1 10.8 4h2.4a1.2 1.2 0 0 1 1.2 1.2v1.7"/><path d="M6.4 6.9 7.2 18.3A1.9 1.9 0 0 0 9.1 20h5.8a1.9 1.9 0 0 0 1.9-1.7l.8-11.4M10.2 10.6v5.6M13.8 10.6v5.6"/></svg>
                <span class="tool__label">Delete</span>
              </span>
            </button>
          </div>
        </div>
      </div>
    </div>
    <p class="sr" aria-live="polite" data-say></p>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Cambia valori, colori e testi con le manopole e guarda l’animazione rispondere dal vivo. Oppure scrivi tu nel codice. Poi scarica la cartella con le tue modifiche.

26 valori da cambiare

Oppure chiedila a Claude Code

Col connettore di Animorio, Claude Code la prende da qui con tutta la sua guida e la adatta al tuo progetto. Incolla questa richiesta:

Prendi «Il menu a coltellino» da Animorio e adattala al mio sito

Cosa fa

Su un fondo nero c’è solo il manico chiuso di un coltellino, alto 60 pixel: smalto rosso lucido con un riflesso lungo in alto, due guance d’acciaio che lo bordano, il perno a sinistra e un rivetto a destra, e in mezzo una targhetta d’acciaio incassata con un coltellino disegnato e la scritta ACTIONS. Lungo il bordo di sopra e quello di sotto, fra le guance, si vedono di taglio i dorsi degli attrezzi. Passandoci sopra, una luce scorre sullo smalto e la lama del coltellino della targhetta si apre un poco. Premendolo, la targhetta dice CLOSE e gli attrezzi escono girando intorno al perno, uno dopo l’altro: prima la penna, che passa sopra il manico, e l’apribottiglie, che passa sotto, poi le forbici e la lama, infine il punteruolo. Ognuno parte con lo slancio del pollice, rallenta, e nell’ultimo tratto la molla lo tira contro la tacca d’arresto: sbatte, rimbalza appena e si ferma, il manico prende il colpo con un piccolo scatto e un riflesso corre lungo l’acciaio. Aperti, gli attrezzi fanno un ventaglio che punta a sinistra e il coltellino scivola perché il tutto resti al centro. Su ogni attrezzo sono incisi il nome e l’icona dell’azione, letti dalla punta verso il manico: Share sul punteruolo con la cruna, Duplicate sulle forbici, Rename sulla penna con la punta d’ottone, Export sull’apribottiglie con il cacciavite, e Delete, inciso in rosso, sulla lama, lucidata a specchio sul filo e un po’ staccata dalle altre. L’acciaio prende la luce secondo il suo angolo: i riflessi restano fermi mentre gli attrezzi girano, e si illumina il bordo che guarda la lampada. Passando sopra un attrezzo, si sfila un poco verso fuori, si schiarisce, l’incisione si fa più marcata e un riflesso gli corre sopra. Scegliendolo, gli altri rientrano nel manico dal più esterno, la targhetta dice il nome dell’azione e l’attrezzo scelto fa il suo gesto: il punteruolo affonda, le forbici tagliano due volte, la penna scrive, l’apribottiglie fa leva, la lama taglia con un lampo lungo il filo. Poi si richiude per ultimo, con lo scatto, e la targhetta dice per un momento che cosa è stato fatto (LINK COPIED, DUPLICATED, RENAMED, EXPORTED, DELETED in rosso) prima di tornare ad ACTIONS. Da tastiera il manico si apre con Invio, Spazio o le frecce, le frecce passano da un attrezzo all’altro, che si sfila col fuoco, l’iniziale porta all’attrezzo che comincia così, Esc e Tab chiudono.

Come funziona

Attrezzi che girano sul perno

Ogni attrezzo è un button disegnato in SVG col perno nell’origine, e lo script gli dà a ogni fotogramma rotate() per l’angolo e translateX() per quanto si sfila. Gli angoli d’arrivo vengono da SPREAD: un ventaglio che punta via dal manico, con la lama più staccata. Quelli sopra il centro aprono passando sopra il manico, quelli sotto passando sotto; si aprono dal centro verso fuori e si chiudono al contrario, così nessuno attraversa mai un attrezzo già fermo.

La fisica di una molla a scatto

Ogni attrezzo ha un angolo e una velocità. Parte con lo slancio del pollice (FLICK), lo frena l’attrito dello snodo, lo tira una forza costante e negli ultimi 34 gradi la molla scatta (SNAP, che cresce col quadrato della vicinanza). Contro l’arresto rimbalza con BOUNCE della sua velocità e la molla lo riporta giù, finché si posa. Gira a mano sul ticker di GSAP in passi da 1/240 di secondo, col passo del fotogramma limitato a 1/30.

Il colpo e il riflesso

A ogni urto contro l’arresto il manico riceve una spinta su una molla rigida, una rotazione di qualche decimo di grado intorno al perno, e sull’attrezzo parte un riflesso: un rettangolo inclinato con un nucleo bianco fra due bande scure, ritagliato sulla sagoma, che corre dal perno alla punta. I colpi più piccoli del rimbalzo non lo fanno ripartire. Il coltellino scivola di lato di quanto il ventaglio è aperto, per restare al centro.

Una luce che resta ferma

L’acciaio è un linearGradient in userSpaceOnUse che a ogni fotogramma ruota al contrario dell’attrezzo (gradientTransform), così le bande dei riflessi restano ferme nella stanza e l’attrezzo ci passa attraverso. I due bordi lunghi hanno una linea chiara che si accende secondo il prodotto scalare fra la loro normale e la direzione della lampada. L’ombra sta su un livello fermo intorno a ogni attrezzo, perché cada sempre dalla stessa parte.

Il gesto e la targhetta

Scelto un attrezzo, gli altri rientrano e lui suona il suo gesto con una timeline di GSAP sui suoi numeri (giro, scorrimento, apertura delle forbici); finito il gesto, aspetta che tutti gli altri siano a casa e poi si richiude. La targhetta cambia parole rotolando: prima esce la vecchia, poi entra la nuova. Una parola troppo lunga si stringe con l’asse wdth del carattere Saira finché ci sta.

Un menu vero

Il manico è un button con aria-haspopup, aria-expanded e aria-controls; gli attrezzi sono menuitem in un role menu, inert finché il coltellino è chiuso. Frecce su e giù, Home, End, l’iniziale, Invio e Spazio, Esc e Tab che chiudono e riportano il fuoco al manico. Il fuoco è una linea ambra che segue la sagoma dell’attrezzo. Prende il puntatore solo la sagoma dell’acciaio, un poco allargata, e il risultato lo legge un’area aria-live.

Valori da cambiare

NomeFileDi partenzaCosa cambia
FLICKscript.js560La velocità dello slancio del pollice che fa partire un attrezzo, in gradi al secondo.
SNAPscript.js5600Quanto forte la molla tira l’attrezzo contro l’arresto nell’ultimo tratto, in gradi al secondo al quadrato.
BOUNCEscript.js0.3Quanta velocità tiene un attrezzo quando rimbalza sull’arresto (0 niente, 0,6 vivace).
STAGGERscript.js0.095I secondi fra la partenza di un attrezzo e quella del successivo.
SPREADscript.js22I gradi fra un attrezzo e l’altro nel ventaglio aperto; la lama resta un po’ più staccata.
LIFTscript.js6Quanto si sfila un attrezzo sotto il puntatore, in pixel.
HOLDscript.js1.6I secondi in cui la targhetta dice che cosa è stato fatto.
OPEN_WORDscript.js'Close'Che cosa dice la targhetta mentre il coltellino è aperto.
DONE_WORDSscript.js5Che cosa dice la targhetta dopo ogni attrezzo, dal primo in alto all’ultimo in basso.

Provali nel Playground

Quando usarla

Per il menu delle azioni su un file, un progetto o un elemento di una lista, in un sito o in un’app con un carattere da oggetto: uno strumento per creativi, un gestore di file, un portfolio, un negozio di attrezzi. Da quattro a sei azioni brevi; con di più il ventaglio si stringe e le scritte si coprono.

Sul telefono

Il coltellino è largo 360 unità e a 390 pixel ci sta quasi in scala piena (circa 0,99): il ventaglio aperto resta dentro lo schermo e il manico chiuso è alto come sul computer. Il tocco apre e chiude come il clic; il dito che preme un attrezzo lo fa sfilare e illuminare come il puntatore, e lasciandolo lo sceglie.

Con il movimento ridotto

Niente giri, niente rimbalzi e niente colpi: il coltellino si dissolve e ricompare aperto o chiuso, l’attrezzo sotto il puntatore si schiarisce senza sfilarsi, e scegliendo un’azione l’attrezzo si accende, poi il coltellino si dissolve chiuso con la targhetta che dice che cosa è stato fatto. Le parole della targhetta si dissolvono invece di rotolare.

Trappole

  • Una sfumatura legata all’attrezzo girava con lui e la luce cambiava lato a ogni giro: le sfumature dell’acciaio sono in userSpaceOnUse e a ogni fotogramma ruotano al contrario dell’attrezzo, e i due bordi si accendono secondo l’angolo con la lampada.
  • Il drop-shadow messo sull’attrezzo girava insieme a lui e l’ombra cadeva verso l’alto quando l’attrezzo puntava a sinistra: l’ombra sta su un livello fermo intorno a ogni attrezzo.
  • Aprendo gli attrezzi nell’ordine della pagina, quelli che passano sopra il manico attraversavano quelli già aperti: ora si aprono dal centro del ventaglio verso fuori e si chiudono al contrario.
  • Il riflesso all’arrivo, bianco su acciaio chiaro, non si vedeva, e il secondo colpo del rimbalzo lo faceva ripartire più debole: ora è un nucleo bianco fra due bande scure, e lo fa ripartire solo un colpo più forte.
  • Nei fotogrammi lenti la fisica, col passo limitato a 1/30, rallenta mentre i tween di GSAP no: l’attrezzo scelto finiva il gesto e si chiudeva prima degli altri. Ora aspetta che tutti gli altri siano a casa.
  • Durante il cambio di parole la vecchia e la nuova si sovrapponevano nella targhetta: ora la vecchia esce prima che entri la nuova.
  • La striscia del dorso della lama, nella guancia, usciva oltre la punta arrotondata del manico: le strisce sono ritagliate sulla forma delle guance.
  • Il clic dell’anteprima del catalogo (detail 0) spostava il fuoco e accendeva l’anello: il fuoco si sposta solo quando lo usa la tastiera.

Librerie

GSAP 3.15.0, Saira (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 15 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 6 ottobre 2026

  • Computer: Fatto
  • Telefono: Fatto
  • Movimento ridotto: Fatto
  • Safari su iPhone: Da fare

Licenza d’uso

Puoi usarla nei tuoi progetti e in quelli dei tuoi clienti. Non puoi rivenderla né ridistribuirla così com’è. GSAP resta sotto la sua licenza Standard. Licenza completa.

Tutti i componenti