本文へスキップ

CRG-20 — LIQUID FX

リキッドUI29点 在庫あり

ゼリーのように変形するスイッチやボタン。物理Spring+グーイの液体演出。 すべて画像不使用・クラス接頭辞 cg- 付きで、既存サイトに安全に積み込めます。

CRG-20-001

ジェリースイッチJelly Switch

.cg-lq-switch {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #FDFDFE, #F0F1F4 55%, #FAFBFC);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 -2px 5px rgba(30, 50, 80, .06);
  cursor: pointer;
}
.cg-lq-switch .goo {
  position: absolute;
  inset: 0;
  filter: url(#cg-lq-sw-goo);
}
.cg-lq-switch i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #B7EDFF, rgba(100, 217, 255, .85) 45%, #35A8DE);
}
.cg-lq-switch .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-switch .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-switch .shine {
  position: absolute;
  top: 14px;
  left: 18px;
  width: 22px;
  height: 12px;
  border-radius: 9999px;
  background: linear-gradient(180deg, rgba(255, 255, 255, .95), transparent);
  filter: blur(1px);
  pointer-events: none;
}
.cg-lq-switch[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 0 18px rgba(100, 217, 255, .35);
}
<button class="cg-lq-switch" type="button" role="switch" aria-checked="false"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-sw-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span><span class="shine"></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-switch');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var shine = btn.querySelector('.shine');
  var TRAVEL = 68;
  /* ミニSpring: pos/velを積分するだけの15行物理エンジン */
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    /* 速度からsquash&stretchを導出(ここがゼリーの正体) */
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    shine.style.transform = 'translateX(' + main.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 3116 B +JSORIGINAL
CRG-20-002

ぷるぷるジェリーボタンJelly Button

.cg-lq-btn {
  padding: 18px 44px;
  font-size: 16px;
  font-weight: 900;
  color: #fff;
  border: none;
  border-radius: 24px;
  background: radial-gradient(circle at 35% 25%, #FF9BB8, #E8557E 60%, #C23A62);
  box-shadow: 0 10px 24px rgba(232, 85, 126, .35),
    inset 0 2px 3px rgba(255, 255, 255, .55),
    inset 0 -4px 8px rgba(160, 30, 70, .35);
  cursor: pointer;
}
<button class="cg-lq-btn" type="button">ぷにっ</button>
(function () {
  var btn = document.querySelector('.cg-lq-btn');
  if (!btn) return;
  /* 押した瞬間に潰し、離すとSpringが勝手にぷるぷる収束する */
  var p = 0, v = 0, target = 0;
  btn.addEventListener('pointerdown', function () { target = 1; });
  window.addEventListener('pointerup', function () { target = 0; });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 320 - v * 11;
    v += f * dt; p += v * dt;
    btn.style.transform = 'scale(' + (1 + p * -.12 + Math.abs(v) * .006) + ',' + (1 + p * .1 - Math.abs(v) * .004) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1135 B +JSORIGINAL
CRG-20-003

グーイ分裂メニューGooey Menu

.cg-lq-menu { width: 200px; height: 150px; position: relative; }
.cg-lq-menu .wrap {
  position: absolute;
  inset: 0;
  filter: url(#cg-lq-menu-goo);
}
.cg-lq-menu .main {
  position: absolute;
  left: 72px;
  top: 46px;
  width: 56px;
  height: 56px;
  border: none;
  border-radius: 50%;
  font-size: 24px;
  color: #fff;
  background: #2FA3C4;
  cursor: pointer;
  z-index: 1;
  transition: transform .3s cubic-bezier(.3, 1.3, .5, 1);
}
.cg-lq-menu[data-open="true"] .main { transform: rotate(45deg); }
.cg-lq-menu i {
  position: absolute;
  left: 82px;
  top: 56px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: #2FA3C4;
  transition: transform .45s cubic-bezier(.3, 1.4, .5, 1);
}
.cg-lq-menu[data-open="true"] i:nth-of-type(1) { transform: translate(-64px, 0); transition-delay: 0s; }
.cg-lq-menu[data-open="true"] i:nth-of-type(2) { transform: translate(64px, 0); transition-delay: .05s; }
.cg-lq-menu[data-open="true"] i:nth-of-type(3) { transform: translate(0, -52px); transition-delay: .1s; }
<div class="cg-lq-menu"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-menu-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="7" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 24 -10"/></filter></svg><div class="wrap"><button class="main" type="button" aria-expanded="false" aria-label="メニュー">+</button><i></i><i></i><i></i></div></div>
(function () {
  var root = document.querySelector('.cg-lq-menu');
  if (!root) return;
  var main = root.querySelector('.main');
  main.addEventListener('click', function () {
    var open = root.getAttribute('data-open') === 'true';
    root.setAttribute('data-open', open ? 'false' : 'true');
    main.setAttribute('aria-expanded', open ? 'false' : 'true');
  });
})();
GROSS WT. 1804 B +JSORIGINAL
CRG-20-004

液体タブインジケーターLiquid Tab Indicator

.cg-lq-tabs {
  position: relative;
  display: inline-flex;
  gap: 4px;
  padding: 6px;
  border-radius: 9999px;
  background: #EDEEF2;
}
.cg-lq-tabs button {
  position: relative;
  padding: 10px 20px;
  border: none;
  background: none;
  font-size: 13px;
  font-weight: 700;
  color: #6B7280;
  cursor: pointer;
  transition: color .25s;
}
.cg-lq-tabs button.on { color: #fff; }
.cg-lq-tabs .ind {
  position: absolute;
  top: 6px;
  bottom: 6px;
  left: 0;
  width: 60px;
  border-radius: 9999px;
  background: linear-gradient(160deg, #55C0E0, #2FA3C4);
  box-shadow: 0 4px 12px rgba(47, 163, 196, .4);
}
<nav class="cg-lq-tabs"><i class="ind" aria-hidden="true"></i><button type="button" class="on">受信</button><button type="button">送信済み</button><button type="button">下書き</button></nav>
(function () {
  var nav = document.querySelector('.cg-lq-tabs');
  if (!nav) return;
  var ind = nav.querySelector('.ind');
  var btns = nav.querySelectorAll('button');
  var p = 0, v = 0, w = 0, wNow = 0, targetX = 0, targetW = 0;
  function retarget(btn) {
    targetX = btn.offsetLeft;
    targetW = btn.offsetWidth;
    btns.forEach(function (b) { b.classList.toggle('on', b === btn); });
  }
  btns.forEach(function (b) { b.addEventListener('click', function () { retarget(b); }); });
  retarget(nav.querySelector('.on'));
  p = targetX; wNow = targetW;
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (targetX - p) * 260 - v * 15;
    v += f * dt; p += v * dt;
    wNow += (targetW - wNow) * Math.min(1, dt * 14);
    /* 移動速度でインジケーターが液体のように伸びる */
    var stretch = Math.min(30, Math.abs(v) * .06);
    ind.style.left = (p - stretch / 2) + 'px';
    ind.style.width = (wNow + stretch) + 'px';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1886 B +JSORIGINAL
CRG-20-005

液体プログレスバーLiquid Progress

.cg-lq-prog .track {
  position: relative;
  width: 240px;
  height: 18px;
  border-radius: 9999px;
  background: #EDEEF2;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .08);
  overflow: hidden;
}
.cg-lq-prog .goo {
  position: absolute;
  inset: 0;
  filter: url(#cg-lq-prog-goo);
}
.cg-lq-prog i { position: absolute; background: linear-gradient(180deg, #55C0E0, #2FA3C4); }
.cg-lq-prog .fill { left: 0; top: 3px; bottom: 3px; width: 0; border-radius: 9999px; }
.cg-lq-prog .head { top: 1px; width: 16px; height: 16px; border-radius: 50%; }
<div class="cg-lq-prog"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-prog-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="4" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -8"/></filter></svg><div class="track"><div class="goo"><i class="fill"></i><i class="head"></i></div></div></div>
(function () {
  var root = document.querySelector('.cg-lq-prog');
  if (!root) return;
  var fill = root.querySelector('.fill');
  var head = root.querySelector('.head');
  var MAX = 224;
  var p = 0, v = 0, target = 0, goal = 0;
  /* デモ: 進捗が段階的に進む→完了でリセット(実運用はtargetに実進捗を入れる) */
  setInterval(function () {
    goal = goal >= 1 ? 0 : Math.min(1, goal + .25 + Math.random() * .2);
    target = goal * MAX;
    if (goal === 0) { p = 0; v = 0; }
  }, 1100);
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 190 - v * 12;
    v += f * dt; p += v * dt;
    var s = Math.max(-1, Math.min(1, v / 420));
    fill.style.width = Math.max(0, p) + 'px';
    head.style.transform = 'translateX(' + Math.max(0, p - 8) + 'px) scale(' + (1 + Math.abs(s) * .8) + ',' + (1 - Math.abs(s) * .35) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1891 B +JSORIGINAL
CRG-20-006

どこかをクリック

クリック水滴ぽちゃんDroplet Splash

.cg-lq-pond {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  overflow: hidden;
  background: linear-gradient(180deg, #D8F1FA, #A8DDF0);
  cursor: pointer;
}
.cg-lq-pond p {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  color: #2A7A99;
  pointer-events: none;
}
.cg-lq-pond .drop {
  position: absolute;
  width: 14px;
  height: 18px;
  border-radius: 50% 50% 50% 50% / 35% 35% 65% 65%;
  background: linear-gradient(180deg, #7FD4FF, #2FA3C4);
  pointer-events: none;
  animation: cg-lq-drop-fall .38s cubic-bezier(.4, 0, 1, 1) forwards;
}
.cg-lq-pond .ring {
  position: absolute;
  width: 20px;
  height: 20px;
  border: 3px solid rgba(47, 163, 196, .75);
  border-radius: 50%;
  pointer-events: none;
  animation: cg-lq-drop-ring .8s ease-out .3s forwards;
  opacity: 0;
}
@keyframes cg-lq-drop-fall {
  0% { transform: translateY(-90px) scale(1, 1.15); opacity: 0; }
  30% { opacity: 1; }
  85% { transform: translateY(0) scale(1, 1); }
  100% { transform: translateY(0) scale(1.5, .4); opacity: 0; }
}
@keyframes cg-lq-drop-ring {
  0% { transform: scale(.4); opacity: 1; }
  100% { transform: scale(3.4); opacity: 0; }
}
<div class="cg-lq-pond"><p>どこかをクリック</p></div>
(function () {
  var pond = document.querySelector('.cg-lq-pond');
  if (!pond) return;
  pond.addEventListener('click', function (e) {
    var r = pond.getBoundingClientRect();
    var x = e.clientX - r.left, y = e.clientY - r.top;
    var drop = document.createElement('i');
    drop.className = 'drop';
    drop.style.left = (x - 7) + 'px';
    drop.style.top = (y - 9) + 'px';
    var ring = document.createElement('i');
    ring.className = 'ring';
    ring.style.left = (x - 10) + 'px';
    ring.style.top = (y - 10) + 'px';
    pond.appendChild(drop);
    pond.appendChild(ring);
    setTimeout(function () { drop.remove(); ring.remove(); }, 1300);
  });
})();
GROSS WT. 1995 B +JSORIGINAL
CRG-20-007

磁力ジェリーボタンMagnetic Jelly

.cg-lq-mag-area {
  width: 260px;
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cg-lq-mag {
  padding: 18px 40px;
  font-size: 14px;
  font-weight: 900;
  letter-spacing: .12em;
  color: #fff;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(160deg, #55C0E0, #2273A8);
  box-shadow: 0 10px 24px rgba(47, 163, 196, .4);
  cursor: pointer;
}
<div class="cg-lq-mag-area"><button class="cg-lq-mag" type="button">MAGNET</button></div>
(function () {
  var area = document.querySelector('.cg-lq-mag-area');
  var btn = document.querySelector('.cg-lq-mag');
  if (!area || !btn) return;
  var p = { x: 0, y: 0 }, v = { x: 0, y: 0 }, t = { x: 0, y: 0 };
  area.addEventListener('mousemove', function (e) {
    var r = area.getBoundingClientRect();
    /* エリア中心からのオフセットに吸い付く(最大±26px) */
    t.x = Math.max(-26, Math.min(26, (e.clientX - r.left - r.width / 2) * .35));
    t.y = Math.max(-18, Math.min(18, (e.clientY - r.top - r.height / 2) * .35));
  });
  area.addEventListener('mouseleave', function () { t.x = 0; t.y = 0; });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    ['x', 'y'].forEach(function (k) {
      var f = (t[k] - p[k]) * 210 - v[k] * 10; /* 減衰弱め=離すとびよんびよん */
      v[k] += f * dt; p[k] += v[k] * dt;
    });
    var sp = Math.min(1, Math.abs(v.x) / 300);
    btn.style.transform = 'translate(' + p.x + 'px,' + p.y + 'px) scale(' + (1 + sp * .12) + ',' + (1 - sp * .08) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1643 B +JSORIGINAL
CRG-20-008

エリア内でマウスを動かす

ジェリーカーソルJelly Cursor

.cg-lq-cursor {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  overflow: hidden;
  background: #10141F;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: none;
}
.cg-lq-cursor p {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  color: #7FA3C4;
  pointer-events: none;
}
.cg-lq-cursor i {
  position: absolute;
  top: 0;
  left: 0;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: rgba(100, 217, 255, .7);
  mix-blend-mode: screen;
  pointer-events: none;
  opacity: 0;
}
<div class="cg-lq-cursor"><p>エリア内でマウスを動かす</p><i aria-hidden="true"></i></div>
(function () {
  var area = document.querySelector('.cg-lq-cursor');
  if (!area) return;
  var blob = area.querySelector('i');
  var p = { x: 140, y: 75 }, v = { x: 0, y: 0 }, t = { x: 140, y: 75 };
  area.addEventListener('mousemove', function (e) {
    var r = area.getBoundingClientRect();
    t.x = e.clientX - r.left;
    t.y = e.clientY - r.top;
    blob.style.opacity = 1;
  });
  area.addEventListener('mouseleave', function () { blob.style.opacity = 0; });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    ['x', 'y'].forEach(function (k) {
      var f = (t[k] - p[k]) * 170 - v[k] * 16;
      v[k] += f * dt; p[k] += v[k] * dt;
    });
    /* 速度方向に伸びる=移動中は水滴、静止で真円 */
    var sp = Math.min(1, Math.hypot(v.x, v.y) / 900);
    var ang = Math.atan2(v.y, v.x) * 180 / Math.PI;
    blob.style.transform = 'translate(' + p.x + 'px,' + p.y + 'px) rotate(' + ang + 'deg) scale(' + (1 + sp * .9) + ',' + (1 - sp * .4) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1776 B +JSORIGINAL
CRG-20-009

分裂合体ローダーGooey Loader

.cg-lq-loader { width: 120px; height: 60px; }
.cg-lq-loader .goo {
  position: relative;
  width: 100%;
  height: 100%;
  filter: url(#cg-lq-ld-goo);
}
.cg-lq-loader i {
  position: absolute;
  top: 15px;
  left: 45px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #2FA3C4;
  animation: cg-lq-ld-split 1.6s ease-in-out infinite;
}
.cg-lq-loader i:last-child { animation-name: cg-lq-ld-split2; }
@keyframes cg-lq-ld-split {
  0%, 100% { transform: translateX(0) scale(1); }
  50% { transform: translateX(-32px) scale(.85); }
}
@keyframes cg-lq-ld-split2 {
  0%, 100% { transform: translateX(0) scale(1); }
  50% { transform: translateX(32px) scale(.85); }
}
<div class="cg-lq-loader" role="status" aria-label="読み込み中"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-ld-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><div class="goo"><i></i><i></i></div></div>
GROSS WT. 1023 B ORIGINAL
CRG-20-010

ジェリーセグメント選択Jelly Segments

.cg-lq-seg {
  position: relative;
  display: inline-flex;
  margin: 0;
  padding: 5px;
  border: none;
  border-radius: 9999px;
  background: #EDEEF2;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .06);
}
.cg-lq-seg label { position: relative; z-index: 1; }
.cg-lq-seg input { position: absolute; opacity: 0; }
.cg-lq-seg span {
  display: inline-block;
  padding: 10px 22px;
  font-size: 13px;
  font-weight: 700;
  color: #6B7280;
  cursor: pointer;
  transition: color .25s;
}
.cg-lq-seg label:has(input:checked) span { color: #fff; }
.cg-lq-seg .knob {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  width: 60px;
  border-radius: 9999px;
  background: linear-gradient(160deg, #55C0E0, #2273A8);
  box-shadow: 0 4px 10px rgba(47, 163, 196, .4);
}
<fieldset class="cg-lq-seg"><i class="knob" aria-hidden="true"></i><label><input type="radio" name="cg-lq-plan" checked><span>日</span></label><label><input type="radio" name="cg-lq-plan"><span>週</span></label><label><input type="radio" name="cg-lq-plan"><span>月</span></label></fieldset>
(function () {
  var seg = document.querySelector('.cg-lq-seg');
  if (!seg) return;
  var knob = seg.querySelector('.knob');
  var labels = seg.querySelectorAll('label');
  var p = 5, v = 0, targetX = 5, targetW = 60;
  function retarget() {
    var checked = seg.querySelector('input:checked');
    var label = checked.closest('label');
    targetX = label.offsetLeft;
    targetW = label.offsetWidth;
  }
  labels.forEach(function (l) {
    l.querySelector('input').addEventListener('change', retarget);
  });
  retarget();
  p = targetX;
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (targetX - p) * 250 - v * 14;
    v += f * dt; p += v * dt;
    var s = Math.min(24, Math.abs(v) * .05); /* 速度分だけ横に溶けて伸びる */
    knob.style.left = (p - s / 2) + 'px';
    knob.style.width = (targetW + s) + 'px';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2022 B +JSORIGINAL
CRG-20-011
ぷにぷに

長押しで潰れて、離すと弾む

つまむと潰れるカードSquishy Card

.cg-lq-squish {
  width: 210px;
  padding: 26px 22px;
  text-align: center;
  border-radius: 20px;
  background: radial-gradient(circle at 40% 25%, #E6F8FF, #BDE9F8 70%);
  box-shadow: 0 12px 26px rgba(47, 163, 196, .25),
    inset 0 2px 3px rgba(255, 255, 255, .9),
    inset 0 -5px 10px rgba(47, 130, 170, .18);
  cursor: pointer;
  user-select: none;
}
.cg-lq-squish b { font-size: 17px; color: #1D6E8C; }
.cg-lq-squish p { margin: 6px 0 0; font-size: 11.5px; color: #4E93AC; }
<div class="cg-lq-squish" role="button" tabindex="0"><b>ぷにぷに</b><p>長押しで潰れて、離すと弾む</p></div>
(function () {
  var card = document.querySelector('.cg-lq-squish');
  if (!card) return;
  var p = 0, v = 0, target = 0;
  function press(on) { target = on ? 1 : 0; }
  card.addEventListener('pointerdown', function () { press(true); });
  window.addEventListener('pointerup', function () { press(false); });
  card.addEventListener('keydown', function (e) { if (e.key === ' ' || e.key === 'Enter') press(true); });
  card.addEventListener('keyup', function () { press(false); });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 300 - v * 10;
    v += f * dt; p += v * dt;
    card.style.transform = 'scale(' + (1 + p * .06 + Math.abs(v) * .004) + ',' + (1 - p * .14 - Math.abs(v) * .003) + ') translateY(' + p * 6 + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1479 B +JSORIGINAL
CRG-20-012

液体が満ちるボタンWave Fill

.cg-lq-wave {
  position: relative;
  padding: 18px 44px;
  font-size: 14px;
  font-weight: 900;
  letter-spacing: .12em;
  color: #1D6E8C;
  border: 2.5px solid #2FA3C4;
  border-radius: 9999px;
  background: #fff;
  overflow: hidden;
  cursor: pointer;
  transition: color .4s;
}
.cg-lq-wave span { position: relative; }
.cg-lq-wave i {
  position: absolute;
  left: -50%;
  right: -50%;
  top: 100%;
  height: 200%;
  border-radius: 42% 44% 0 0 / 14% 16% 0 0;
  background: linear-gradient(180deg, #55C0E0, #2273A8);
  transition: transform .7s cubic-bezier(.3, .8, .3, 1);
  animation: cg-lq-wave-sway 2.6s ease-in-out infinite alternate;
}
.cg-lq-wave:hover { color: #fff; }
/* 波が回転で揺れながらせり上がってくる */
.cg-lq-wave:hover i { transform: translateY(-78%); }
@keyframes cg-lq-wave-sway {
  from { margin-left: -14px; }
  to { margin-left: 14px; }
}
<button class="cg-lq-wave" type="button"><i aria-hidden="true"></i><span>HOVER ME</span></button>
GROSS WT. 980 B ORIGINAL
CRG-20-013

液体レンジスライダーLiquid Range Slider

.cg-lq-slider {
  width: 240px;
  padding: 8px 0;
  cursor: pointer;
  touch-action: none;
}
.cg-lq-slider .track {
  position: relative;
  height: 12px;
  border-radius: 9999px;
  background: #EDEEF2;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .08);
}
.cg-lq-slider .goo {
  position: absolute;
  inset: -8px 0;
  filter: url(#cg-lq-sl-goo);
}
.cg-lq-slider i { position: absolute; background: linear-gradient(160deg, #55C0E0, #2273A8); }
.cg-lq-slider .fill { left: 0; top: 8px; height: 12px; width: 0; border-radius: 9999px; }
.cg-lq-slider .thumb {
  top: 2px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  box-shadow: 0 4px 10px rgba(47, 163, 196, .45);
}
.cg-lq-slider:focus-visible .track { outline: 2px solid #2273A8; outline-offset: 3px; }
<div class="cg-lq-slider" role="slider" aria-valuemin="0" aria-valuemax="100" aria-valuenow="42" aria-label="音量" tabindex="0"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-sl-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="4" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -8"/></filter></svg><div class="track"><div class="goo"><i class="fill"></i><i class="thumb"></i></div></div></div>
(function () {
  var root = document.querySelector('.cg-lq-slider');
  if (!root) return;
  var track = root.querySelector('.track');
  var fill = root.querySelector('.fill');
  var thumb = root.querySelector('.thumb');
  var W = 240, THUMB = 24;
  var p = 0, v = 0, target = 0, dragging = false;
  function setFromClientX(x) {
    var r = track.getBoundingClientRect();
    target = Math.max(0, Math.min(1, (x - r.left) / r.width)) * (W - THUMB);
    root.setAttribute('aria-valuenow', Math.round(target / (W - THUMB) * 100));
  }
  track.addEventListener('pointerdown', function (e) {
    dragging = true;
    setFromClientX(e.clientX);
    track.setPointerCapture && track.setPointerCapture(e.pointerId);
  });
  track.addEventListener('pointermove', function (e) { if (dragging) setFromClientX(e.clientX); });
  window.addEventListener('pointerup', function () { dragging = false; });
  root.addEventListener('keydown', function (e) {
    var step = (W - THUMB) / 20;
    if (e.key === 'ArrowLeft') target = Math.max(0, target - step);
    if (e.key === 'ArrowRight') target = Math.min(W - THUMB, target + step);
    root.setAttribute('aria-valuenow', Math.round(target / (W - THUMB) * 100));
  });
  target = (W - THUMB) * .42; p = target;
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 260 - v * 16;
    v += f * dt; p += v * dt;
    /* ドラッグ速度でツマミが液体のように伸び縮みする */
    var s = Math.max(-1, Math.min(1, v / 600));
    fill.style.width = (p + THUMB / 2) + 'px';
    thumb.style.left = p + 'px';
    thumb.style.transform = 'scale(' + (1 + Math.abs(s) * .35) + ',' + (1 - Math.abs(s) * .25) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 3015 B +JSORIGINAL
CRG-20-014

エリア内でマウスを動かす

メタボール追従カーソルMetaball Cursor Trail

.cg-lq-meta {
  position: relative;
  width: 280px;
  height: 150px;
  border-radius: 14px;
  overflow: hidden;
  background: radial-gradient(circle at 30% 20%, #1B2A4A, #0B0F1C 70%);
  cursor: none;
}
.cg-lq-meta p {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  color: #6F8FC4;
  pointer-events: none;
}
.cg-lq-meta .goo {
  position: absolute;
  inset: 0;
  filter: url(#cg-lq-meta-goo);
}
.cg-lq-meta i {
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #B7EDFF, #64D9FF 55%, #2FA3C4);
  opacity: 0;
}
.cg-lq-meta i:nth-child(1) { width: 34px; height: 34px; margin: -17px 0 0 -17px; }
.cg-lq-meta i:nth-child(2) { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
.cg-lq-meta i:nth-child(3) { width: 20px; height: 20px; margin: -10px 0 0 -10px; }
<div class="cg-lq-meta"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-meta-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="8" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 24 -10"/></filter></svg><p>エリア内でマウスを動かす</p><div class="goo"><i></i><i></i><i></i></div></div>
(function () {
  var area = document.querySelector('.cg-lq-meta');
  if (!area) return;
  var blobs = area.querySelectorAll('.goo i');
  var t = { x: 140, y: 75 };
  var springs = [
    { p: { x: 140, y: 75 }, v: { x: 0, y: 0 }, stiff: 340, damp: 18 },
    { p: { x: 140, y: 75 }, v: { x: 0, y: 0 }, stiff: 200, damp: 16 },
    { p: { x: 140, y: 75 }, v: { x: 0, y: 0 }, stiff: 110, damp: 14 }
  ];
  area.addEventListener('mousemove', function (e) {
    var r = area.getBoundingClientRect();
    t.x = e.clientX - r.left;
    t.y = e.clientY - r.top;
    blobs.forEach(function (b) { b.style.opacity = 1; });
  });
  area.addEventListener('mouseleave', function () {
    blobs.forEach(function (b) { b.style.opacity = 0; });
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    springs.forEach(function (s, i) {
      ['x', 'y'].forEach(function (k) {
        var f = (t[k] - s.p[k]) * s.stiff - s.v[k] * s.damp;
        s.v[k] += f * dt; s.p[k] += s.v[k] * dt;
      });
      /* 追従の遅さが尾を引き、gooeyフィルターで手前のblobと融合する */
      blobs[i].style.transform = 'translate(' + s.p.x + 'px,' + s.p.y + 'px)';
    });
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2568 B +JSORIGINAL
CRG-20-015

ジェリーいいねボタンJelly Like Burst

.cg-lq-like {
  position: relative;
  display: inline-flex;
  padding: 12px 26px;
  border: none;
  border-radius: 9999px;
  background: #F1F0EC;
  box-shadow: inset 0 0 0 2px #E3E1DA;
  cursor: pointer;
  overflow: visible;
}
.cg-lq-like .wrap { display: inline-flex; align-items: center; gap: 8px; }
.cg-lq-like .heart { fill: #A8A6A0; transition: fill .2s; }
.cg-lq-like .count { font-size: 14px; font-weight: 800; color: #6B6A66; font-variant-numeric: tabular-nums; }
.cg-lq-like[aria-pressed="true"] .heart { fill: #E8557E; }
.cg-lq-like[aria-pressed="true"] .count { color: #C23A62; }
.cg-lq-like .spark {
  position: absolute;
  top: 50%;
  left: 26px;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: #E8557E;
  pointer-events: none;
  animation: cg-lq-like-pop .6s ease-out forwards;
}
@keyframes cg-lq-like-pop {
  0% { transform: translate(0, 0) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(0); opacity: 0; }
}
<button class="cg-lq-like" type="button" aria-pressed="false"><span class="wrap"><svg class="heart" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><path d="M12 21s-7.5-4.6-10-9.3C.6 8.4 2.4 5 6 5c2 0 3.4 1 6 3.6C14.6 6 16 5 18 5c3.6 0 5.4 3.4 4 6.7C19.5 16.4 12 21 12 21z"/></svg><b class="count">128</b></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-like');
  if (!btn) return;
  var countEl = btn.querySelector('.count');
  var heart = btn.querySelector('.heart');
  var count = 128;
  var scale = { p: 0, v: 0, t: 0 };
  var bump = { p: 0, v: 0, t: 0 };
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-pressed') === 'true';
    btn.setAttribute('aria-pressed', on ? 'false' : 'true');
    count += on ? -1 : 1;
    countEl.textContent = count;
    scale.t = 1; scale.v += 8; /* 押した瞬間にインパルスを与えて弾ませる */
    bump.t = 1;
    if (!on) {
      for (var i = 0; i < 6; i++) {
        (function (i) {
          var s = document.createElement('i');
          s.className = 'spark';
          var ang = (Math.PI * 2 / 6) * i;
          s.style.setProperty('--dx', (Math.cos(ang) * 30) + 'px');
          s.style.setProperty('--dy', (Math.sin(ang) * 30 - 10) + 'px');
          btn.appendChild(s);
          setTimeout(function () { s.remove(); }, 650);
        })(i);
      }
    }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var fs = (scale.t - scale.p) * 260 - scale.v * 14;
    scale.v += fs * dt; scale.p += scale.v * dt; scale.t = 0;
    var fb = (bump.t - bump.p) * 300 - bump.v * 16;
    bump.v += fb * dt; bump.p += bump.v * dt; bump.t = 0;
    heart.style.transform = 'scale(' + (1 + scale.p * .5) + ')';
    countEl.style.transform = 'translateY(' + (-bump.p * 6) + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2926 B +JSORIGINAL
CRG-20-016

ジェリースイッチ(サンセット)Jelly Switch — Sunset

.cg-lq-sw-sunset {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #FDFDFE, #F0F1F4 55%, #FAFBFC);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 -2px 5px rgba(30, 50, 80, .06);
  cursor: pointer;
}
.cg-lq-sw-sunset .goo { position: absolute; inset: 0; filter: url(#cg-lq-sunset-goo); }
.cg-lq-sw-sunset i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #FFE1A8, #FF9B54 45%, #E8557E);
}
.cg-lq-sw-sunset .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-sw-sunset .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-sw-sunset[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 0 18px rgba(255, 138, 84, .4);
}
<button class="cg-lq-sw-sunset" type="button" role="switch" aria-checked="false"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-sunset-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-sw-sunset');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var TRAVEL = 68;
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2588 B +JSORIGINAL
CRG-20-017

ジェリースイッチ(オーシャン)Jelly Switch — Ocean

.cg-lq-sw-ocean {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #FDFDFE, #F0F1F4 55%, #FAFBFC);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 -2px 5px rgba(30, 50, 80, .06);
  cursor: pointer;
}
.cg-lq-sw-ocean .goo { position: absolute; inset: 0; filter: url(#cg-lq-ocean-goo); }
.cg-lq-sw-ocean i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #C7F3FF, #3FC1E0 45%, #146FA8);
}
.cg-lq-sw-ocean .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-sw-ocean .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-sw-ocean[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 0 18px rgba(63, 193, 224, .4);
}
<button class="cg-lq-sw-ocean" type="button" role="switch" aria-checked="false"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-ocean-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-sw-ocean');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var TRAVEL = 68;
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2578 B +JSORIGINAL
CRG-20-018

ジェリースイッチ(フォレスト)Jelly Switch — Forest

.cg-lq-sw-forest {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #FDFDFE, #F0F1F4 55%, #FAFBFC);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 -2px 5px rgba(30, 50, 80, .06);
  cursor: pointer;
}
.cg-lq-sw-forest .goo { position: absolute; inset: 0; filter: url(#cg-lq-forest-goo); }
.cg-lq-sw-forest i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #DFF7CE, #6FCB6F 45%, #1F7A3D);
}
.cg-lq-sw-forest .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-sw-forest .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-sw-forest[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 0 18px rgba(111, 203, 111, .4);
}
<button class="cg-lq-sw-forest" type="button" role="switch" aria-checked="false"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-forest-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-sw-forest');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var TRAVEL = 68;
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2589 B +JSORIGINAL
CRG-20-019

ジェリースイッチ(グレープ)Jelly Switch — Grape

.cg-lq-sw-grape {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #FDFDFE, #F0F1F4 55%, #FAFBFC);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 -2px 5px rgba(30, 50, 80, .06);
  cursor: pointer;
}
.cg-lq-sw-grape .goo { position: absolute; inset: 0; filter: url(#cg-lq-grape-goo); }
.cg-lq-sw-grape i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #EBDCFF, #B27FE0 45%, #6A3D99);
}
.cg-lq-sw-grape .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-sw-grape .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-sw-grape[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 0 18px rgba(178, 127, 224, .4);
}
<button class="cg-lq-sw-grape" type="button" role="switch" aria-checked="false"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-grape-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-sw-grape');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var TRAVEL = 68;
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2579 B +JSORIGINAL
CRG-20-020

ジェリースイッチ(ミッドナイト)Jelly Switch — Midnight

.cg-lq-sw-midnight {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #1B1D24, #101116 55%, #16171D);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .35),
    inset 0 2px 4px rgba(255, 255, 255, .06),
    inset 0 -2px 5px rgba(0, 0, 0, .5);
  cursor: pointer;
}
.cg-lq-sw-midnight .goo { position: absolute; inset: 0; filter: url(#cg-lq-midnight-goo); }
.cg-lq-sw-midnight i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #E4FBFF, #7FE7FF 45%, #1C8FB0);
}
.cg-lq-sw-midnight .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-sw-midnight .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-sw-midnight[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .35),
    inset 0 2px 4px rgba(255, 255, 255, .06),
    inset 0 0 22px rgba(127, 231, 255, .55);
}
<button class="cg-lq-sw-midnight" type="button" role="switch" aria-checked="true"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-midnight-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-sw-midnight');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var TRAVEL = 68;
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  main.p = lazy.p = main.t = lazy.t = TRAVEL; /* 初期状態ON */
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2674 B +JSORIGINAL
CRG-20-021

ジェリースイッチ(ローズゴールド)Jelly Switch — Rose Gold

.cg-lq-sw-rosegold {
  position: relative;
  width: 140px;
  height: 72px;
  border: none;
  border-radius: 9999px;
  background: linear-gradient(180deg, #FFFCF8, #F7EDE6 55%, #FFFAF6);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 -2px 5px rgba(150, 90, 70, .1);
  cursor: pointer;
}
.cg-lq-sw-rosegold .goo { position: absolute; inset: 0; filter: url(#cg-lq-rosegold-goo); }
.cg-lq-sw-rosegold i {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, #FFF0E0, #FFC1D3 45%, #E8557E);
}
.cg-lq-sw-rosegold .knob { top: 6px; left: 6px; width: 60px; height: 60px; }
.cg-lq-sw-rosegold .tail { top: 18px; left: 18px; width: 36px; height: 36px; opacity: .85; }
.cg-lq-sw-rosegold .shine {
  position: absolute;
  top: 14px;
  left: 18px;
  width: 22px;
  height: 12px;
  border-radius: 9999px;
  background: linear-gradient(180deg, rgba(255, 235, 200, .95), transparent);
  filter: blur(1px);
  pointer-events: none;
}
.cg-lq-sw-rosegold[aria-checked="true"] {
  box-shadow: 0 10px 30px rgba(0, 0, 0, .08),
    inset 0 2px 4px rgba(255, 255, 255, .9),
    inset 0 0 18px rgba(232, 85, 126, .35);
}
<button class="cg-lq-sw-rosegold" type="button" role="switch" aria-checked="false"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-rosegold-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg><span class="goo"><i class="tail"></i><i class="knob"></i></span><span class="shine"></span></button>
(function () {
  var btn = document.querySelector('.cg-lq-sw-rosegold');
  if (!btn) return;
  var knob = btn.querySelector('.knob');
  var tail = btn.querySelector('.tail');
  var shine = btn.querySelector('.shine');
  var TRAVEL = 68;
  function makeSpring(stiff, damp, mass) {
    return { p: 0, v: 0, t: 0, step: function (dt) {
      var f = (this.t - this.p) * stiff - this.v * damp;
      this.v += (f / mass) * dt;
      this.p += this.v * dt;
    } };
  }
  var main = makeSpring(280, 16, 1.1);
  var lazy = makeSpring(150, 18, 1.6);
  btn.addEventListener('click', function () {
    var on = btn.getAttribute('aria-checked') === 'true';
    btn.setAttribute('aria-checked', on ? 'false' : 'true');
    main.t = lazy.t = on ? 0 : TRAVEL;
    if (navigator.vibrate) { try { navigator.vibrate(8); } catch (e) {} }
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    main.step(dt); lazy.step(dt);
    var s = Math.max(-1, Math.min(1, main.v / 540));
    var sx = 1 + Math.abs(s) * .4, sy = 1 - Math.abs(s) * .28;
    knob.style.transform = 'translateX(' + main.p + 'px) skewX(' + (-s * 8) + 'deg) scale(' + sx + ',' + sy + ')';
    tail.style.transform = 'translateX(' + lazy.p + 'px)';
    shine.style.transform = 'translateX(' + main.p + 'px)';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2997 B +JSORIGINAL
CRG-20-022

ぷるぷるジェリーボタン(オーシャン)Jelly Button — Ocean

.cg-lq-btn-ocean {
  padding: 18px 44px;
  font-size: 16px;
  font-weight: 900;
  color: #fff;
  border: none;
  border-radius: 24px;
  background: radial-gradient(circle at 35% 25%, #9BE3FF, #3FA9D6 60%, #1E6E9C);
  box-shadow: 0 10px 24px rgba(63, 169, 214, .35),
    inset 0 2px 3px rgba(255, 255, 255, .55),
    inset 0 -4px 8px rgba(20, 70, 110, .35);
  cursor: pointer;
}
<button class="cg-lq-btn-ocean" type="button">ぷにっ</button>
(function () {
  var btn = document.querySelector('.cg-lq-btn-ocean');
  if (!btn) return;
  var p = 0, v = 0, target = 0;
  btn.addEventListener('pointerdown', function () { target = 1; });
  window.addEventListener('pointerup', function () { target = 0; });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 320 - v * 11;
    v += f * dt; p += v * dt;
    btn.style.transform = 'scale(' + (1 + p * -.12 + Math.abs(v) * .006) + ',' + (1 + p * .1 - Math.abs(v) * .004) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1066 B +JSORIGINAL
CRG-20-023

ぷるぷるジェリーボタン(フォレスト)Jelly Button — Forest

.cg-lq-btn-forest {
  padding: 18px 44px;
  font-size: 16px;
  font-weight: 900;
  color: #fff;
  border: none;
  border-radius: 24px;
  background: radial-gradient(circle at 35% 25%, #B7F0B0, #4FAE58 60%, #276B33);
  box-shadow: 0 10px 24px rgba(79, 174, 88, .35),
    inset 0 2px 3px rgba(255, 255, 255, .55),
    inset 0 -4px 8px rgba(20, 70, 30, .35);
  cursor: pointer;
}
<button class="cg-lq-btn-forest" type="button">ぷにっ</button>
(function () {
  var btn = document.querySelector('.cg-lq-btn-forest');
  if (!btn) return;
  var p = 0, v = 0, target = 0;
  btn.addEventListener('pointerdown', function () { target = 1; });
  window.addEventListener('pointerup', function () { target = 0; });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 320 - v * 11;
    v += f * dt; p += v * dt;
    btn.style.transform = 'scale(' + (1 + p * -.12 + Math.abs(v) * .006) + ',' + (1 + p * .1 - Math.abs(v) * .004) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1067 B +JSORIGINAL
CRG-20-024

ぷるぷるジェリーボタン(グレープ)Jelly Button — Grape

.cg-lq-btn-grape {
  padding: 18px 44px;
  font-size: 16px;
  font-weight: 900;
  color: #fff;
  border: none;
  border-radius: 24px;
  background: radial-gradient(circle at 35% 25%, #E3C6FF, #9A5FD1 60%, #5E3688);
  box-shadow: 0 10px 24px rgba(154, 95, 209, .35),
    inset 0 2px 3px rgba(255, 255, 255, .55),
    inset 0 -4px 8px rgba(70, 30, 100, .35);
  cursor: pointer;
}
<button class="cg-lq-btn-grape" type="button">ぷにっ</button>
(function () {
  var btn = document.querySelector('.cg-lq-btn-grape');
  if (!btn) return;
  var p = 0, v = 0, target = 0;
  btn.addEventListener('pointerdown', function () { target = 1; });
  window.addEventListener('pointerup', function () { target = 0; });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 320 - v * 11;
    v += f * dt; p += v * dt;
    btn.style.transform = 'scale(' + (1 + p * -.12 + Math.abs(v) * .006) + ',' + (1 + p * .1 - Math.abs(v) * .004) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1066 B +JSORIGINAL
CRG-20-025

ジェリーチェックボックスJelly Checkbox

.cg-lq-check {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  font-weight: 700;
  color: #2A2622;
  cursor: pointer;
}
.cg-lq-check input { position: absolute; opacity: 0; width: 0; }
.cg-lq-check .box {
  position: relative;
  width: 28px;
  height: 28px;
  border-radius: 10px;
  background: #EDEEF2;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .08);
  display: flex;
  align-items: center;
  justify-content: center;
}
.cg-lq-check.on .box { background: linear-gradient(160deg, #55C0E0, #2273A8); }
.cg-lq-check .tick {
  width: 16px;
  height: 16px;
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  transition: stroke-dashoffset .28s ease .05s;
}
.cg-lq-check.on .tick { stroke-dashoffset: 0; }
<label class="cg-lq-check"><input type="checkbox"><span class="box"><svg class="tick" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.2 4.2L19 7"/></svg></span><b>利用規約に同意する</b></label>
(function () {
  var root = document.querySelector('.cg-lq-check');
  if (!root) return;
  var box = root.querySelector('.box');
  var input = root.querySelector('input');
  var p = 0, v = 0, target = 0;
  input.addEventListener('change', function () {
    root.classList.toggle('on', input.checked);
    target = 1; v += 9; /* チェックした瞬間にインパルスを与えてぷにっと弾ませる */
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 300 - v * 13;
    v += f * dt; p += v * dt; target = 0;
    box.style.transform = 'scale(' + (1 + p * .28) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1765 B +JSORIGINAL
CRG-20-026

ぷにぷに満足度レーティングJelly Rating Radios

.cg-lq-rating {
  display: inline-flex;
  gap: 14px;
  border: none;
  margin: 0;
  padding: 0;
}
.cg-lq-rating label { cursor: pointer; }
.cg-lq-rating input { position: absolute; opacity: 0; width: 0; }
.cg-lq-rating .face {
  display: block;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: #EDEEF2;
  transition: background .2s;
}
.cg-lq-rating input:checked + .face.bad { background: radial-gradient(circle at 35% 30%, #FFB3A8, #E85B4E); }
.cg-lq-rating input:checked + .face.mid { background: radial-gradient(circle at 35% 30%, #FFE29B, #E8AC2F); }
.cg-lq-rating input:checked + .face.good { background: radial-gradient(circle at 35% 30%, #B7F0B0, #4FAE58); }
<fieldset class="cg-lq-rating"><label><input type="radio" name="cg-lq-rate"><i class="face bad"></i></label><label><input type="radio" name="cg-lq-rate" checked><i class="face mid"></i></label><label><input type="radio" name="cg-lq-rate"><i class="face good"></i></label></fieldset>
(function () {
  var root = document.querySelector('.cg-lq-rating');
  if (!root) return;
  root.querySelectorAll('input').forEach(function (input) {
    var face = input.nextElementSibling;
    var p = 0, v = 0, target = 0;
    input.addEventListener('change', function () {
      if (!input.checked) return;
      target = 1; v += 10; /* 選ばれた顔だけがぷるんと膨らむ */
    });
    var last = performance.now();
    (function loop(now) {
      var dt = Math.min(.032, (now - last) / 1000);
      last = now;
      var f = (target - p) * 260 - v * 12;
      v += f * dt; p += v * dt; target = 0;
      face.style.transform = 'scale(' + (1 + Math.max(0, p) * .3) + ')';
      requestAnimationFrame(loop);
    })(last);
  });
})();
GROSS WT. 1712 B +JSORIGINAL
CRG-20-027

ぷにっと弾む通知バッジJelly Counter Badge

.cg-lq-cbadge {
  position: relative;
  width: 60px;
  height: 60px;
  border: none;
  border-radius: 18px;
  background: #fff;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .1);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #2A2622;
  cursor: pointer;
}
.cg-lq-cbadge .icon { opacity: .7; }
.cg-lq-cbadge .badge {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 24px;
  height: 24px;
  padding: 0 5px;
  border-radius: 12px;
  background: linear-gradient(160deg, #FF9BB8, #E8557E);
  color: #fff;
  font-size: 12px;
  font-weight: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 10px rgba(232, 85, 126, .4);
}
<button class="cg-lq-cbadge" type="button" aria-label="カート"><svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg><b class="badge">3</b></button>
(function () {
  var btn = document.querySelector('.cg-lq-cbadge');
  if (!btn) return;
  var badge = btn.querySelector('.badge');
  var count = 3;
  var p = 0, v = 0, target = 0;
  btn.addEventListener('click', function () {
    count++;
    badge.textContent = count;
    target = 1; v += 9; /* 数字が増えるたびバッジがぷにっと膨らむ */
  });
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - p) * 300 - v * 13;
    v += f * dt; p += v * dt; target = 0;
    badge.style.transform = 'scale(' + (1 + p * .5) + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 1787 B +JSORIGINAL
CRG-20-028

液体トースト通知Melting Toast

.cg-lq-toastarea {
  position: relative;
  width: 260px;
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.cg-lq-toastarea .trigger {
  padding: 14px 30px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #2FA3C4;
  border: none;
  border-radius: 10px;
  cursor: pointer;
}
.cg-lq-toastarea .goo-layer {
  position: absolute;
  inset: 0;
  filter: url(#cg-lq-toast-goo);
  pointer-events: none;
}
.cg-lq-toastarea .anchor,
.cg-lq-toastarea .toast {
  position: absolute;
  left: 50%;
  background: linear-gradient(160deg, #55C0E0, #2273A8);
  border-radius: 16px;
}
.cg-lq-toastarea .anchor { bottom: -20px; width: 40px; height: 40px; margin-left: -20px; border-radius: 50%; }
.cg-lq-toastarea .toast {
  bottom: -60px;
  width: 200px;
  height: 46px;
  margin-left: -100px;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
<div class="cg-lq-toastarea"><svg width="0" height="0" style="position:absolute"><filter id="cg-lq-toast-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="7" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 24 -10"/></filter></svg><button class="trigger" type="button">保存する</button></div>
(function () {
  var area = document.querySelector('.cg-lq-toastarea');
  if (!area) return;
  var btn = area.querySelector('.trigger');
  btn.addEventListener('click', function () {
    var old = area.querySelector('.goo-layer');
    if (old) old.remove();
    var layer = document.createElement('div');
    layer.className = 'goo-layer';
    var anchor = document.createElement('i');
    anchor.className = 'anchor';
    var toast = document.createElement('span');
    toast.className = 'toast';
    toast.textContent = '保存しました ✓';
    layer.appendChild(anchor);
    layer.appendChild(toast);
    area.appendChild(layer);
    var p = 60, v = 0, target = -50; /* 下から溶けて上がってくる */
    var last = performance.now();
    var elapsed = 0;
    (function loopUp(now) {
      var dt = Math.min(.032, (now - last) / 1000);
      last = now; elapsed += dt;
      var f = (target - p) * 210 - v * 15;
      v += f * dt; p += v * dt;
      toast.style.bottom = p + 'px';
      anchor.style.bottom = (p - 40) + 'px';
      if (elapsed < 2.4) requestAnimationFrame(loopUp);
      else {
        /* 一定時間後、液体が下に溶けて消える */
        target = -80;
        var last2 = performance.now();
        (function loopDown(now2) {
          var dt2 = Math.min(.032, (now2 - last2) / 1000);
          last2 = now2;
          var f2 = (target - p) * 210 - v * 15;
          v += f2 * dt2; p += v * dt2;
          toast.style.bottom = p + 'px';
          anchor.style.bottom = (p - 40) + 'px';
          if (p > -75) requestAnimationFrame(loopDown);
          else layer.remove();
        })(last2);
      }
    })(last);
  });
})();
GROSS WT. 2978 B +JSORIGINAL
CRG-20-029

ジェリー音量ダイヤルJelly Volume Dial

.cg-lq-dial {
  position: relative;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: conic-gradient(#2FA3C4 216deg, #EDEEF2 0deg);
  cursor: grab;
  touch-action: none;
}
.cg-lq-dial .ring {
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  background: #fff;
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, .08);
}
.cg-lq-dial .handle {
  position: absolute;
  top: 6px;
  left: 50%;
  width: 14px;
  height: 14px;
  margin-left: -7px;
  border-radius: 50%;
  background: linear-gradient(160deg, #55C0E0, #2273A8);
  box-shadow: 0 3px 8px rgba(47, 163, 196, .4);
  transform-origin: 7px 42px;
}
.cg-lq-dial:focus-visible { outline: 3px solid #2273A8; outline-offset: 3px; }
<div class="cg-lq-dial" role="slider" aria-label="音量" aria-valuemin="0" aria-valuemax="100" aria-valuenow="60" tabindex="0"><i class="ring"></i><i class="handle"></i></div>
(function () {
  var dial = document.querySelector('.cg-lq-dial');
  if (!dial) return;
  var handle = dial.querySelector('.handle');
  var angle = 0, angleV = 0, target = 216; /* 60% ≈ 216deg */
  var dragging = false;
  function setFromEvent(e) {
    var r = dial.getBoundingClientRect();
    var cx = r.left + r.width / 2, cy = r.top + r.height / 2;
    var deg = Math.atan2(e.clientY - cy, e.clientX - cx) * 180 / Math.PI + 90;
    if (deg < 0) deg += 360;
    target = deg;
    dial.style.background = 'conic-gradient(#2FA3C4 ' + deg + 'deg, #EDEEF2 0deg)';
    dial.setAttribute('aria-valuenow', Math.round(deg / 360 * 100));
  }
  dial.addEventListener('pointerdown', function (e) { dragging = true; setFromEvent(e); dial.setPointerCapture(e.pointerId); });
  dial.addEventListener('pointermove', function (e) { if (dragging) setFromEvent(e); });
  dial.addEventListener('pointerup', function () { dragging = false; });
  dial.addEventListener('keydown', function (e) {
    if (e.key === 'ArrowRight' || e.key === 'ArrowUp') target = Math.min(355, target + 10);
    if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') target = Math.max(0, target - 10);
    dial.style.background = 'conic-gradient(#2FA3C4 ' + target + 'deg, #EDEEF2 0deg)';
    dial.setAttribute('aria-valuenow', Math.round(target / 360 * 100));
  });
  angle = target;
  var last = performance.now();
  (function loop(now) {
    var dt = Math.min(.032, (now - last) / 1000);
    last = now;
    var f = (target - angle) * 260 - angleV * 16;
    angleV += f * dt; angle += angleV * dt;
    /* ドラッグ速度でハンドルが円周方向に伸びる(液体の遠心力) */
    var stretch = Math.min(1.6, 1 + Math.abs(angleV) / 900);
    handle.style.transform = 'rotate(' + angle + 'deg) scaleY(' + stretch + ')';
    requestAnimationFrame(loop);
  })(last);
})();
GROSS WT. 2718 B +JSORIGINAL