milvinae

web & product design.

anime.jsで変化し続けるグラデーションを作る2

,

See the Pen change keycolor in marble gradient with animejs by keiyashi (@keiyashi) on CodePen.

先の記事では自動でランダムに変化するグラデーションを作ることが出来ました。
次はインタラクティブに色相を変化させる処理を追加してみましょう。

方針は次の通りです。
・ラジオボタンを作って色を割り振る
・ラジオボタンを押すと、割り振った色相角度を中心に一定の範囲内で色を変化させる
・ランダムグラデーションに戻るボタンも作る

まずはHTMLから
<div class="grad-board">
  <div id="marble-ball-1" class="marble-ball"></div>
  <div id="marble-ball-2" class="marble-ball"></div>
  <div id="marble-ball-3" class="marble-ball"></div>
  <div id="marble-ball-4" class="marble-ball"></div>
  <div id="marble-ball-5" class="marble-ball"></div>
  
  <div class="radios">
    <div class="radio-case">
      <input id="red" name="color-btn" type="radio" class="button">
      <label for="red" class="color-name">red</label>
    </div>
    <div class="radio-case">
      <input id="yellow" name="color-btn" type="radio" class="button">
      <label for="yellow" class="color-name">yellow</label>
    </div>
    <div class="radio-case">
      <input id="green" name="color-btn" type="radio" class="button">
      <label for="green" class="color-name">green</label>
    </div>
    <div class="radio-case">
      <input id="blue" name="color-btn" type="radio" class="button">
      <label for="blue" class="color-name">blue</label>
    </div>
    <div class="radio-case">
      <input id="purple" name="color-btn" type="radio" class="button">
      <label for="purple" class="color-name">purple</label>
    </div>
    <div class="radio-case">
      <input id="gradient" name="color-btn" type="radio" class="button">
      <label for="gradient" class="color-name">gradient</label>
    </div>
  </div>
</div>

<div class="radios">以下が新しく追加した部分になります。
<input>と<label>を<div>でくくったものを1つの色セットとして、必要分準備します。
今回は赤・黄色・緑・青・紫、そしてランダムカラーの7セットを準備しています。

inputをラジオボタンとして使うには、属性にtype="radio"・name="任意の名前”を指定すればOKです。
nameはラジオボタンのグループ名だと思ってください。
labelのfor属性にセットになってるラジオボタンのidを指定すると、ボタンと連携できます。
ラベルをクリックしてもラジオボタンの選択状態を変えることが出来るようになります。

.grad-board{
  width :100dvw;
  height:100dvh;  
  display: block;
  position: relative;
  background-color:white;
  padding:0;
  margin:0;
  overflow:hidden;
  
  @media screen and (orientation:portrait)  { --dot-size : 100dvh;}
  @media screen and (orientation:landscape) { --dot-size : 100dvw;}
  
  .marble-ball{
    position: absolute;
    z-index: 0;
    width: var( --dot-size);
    height: var(--dot-size);
    border-radius: calc(var(--dot-size) / 4);
    transform-origin: center;
    background: radial-gradient(rgb(0,128,128) 0px, transparent 70%);
  }

  #marble-ball-1{
    left: calc(50dvw - var(--dot-size)/2);
    top:  calc(50dvh - var(--dot-size)/2);
  }
  #marble-ball-2{
    left: calc(25dvw - var(--dot-size)/2);
    top:  calc(25dvh - var(--dot-size)/2);
  }
  #marble-ball-3{
    left: calc(25dvw - var(--dot-size)/2);
    top:  calc(75dvh - var(--dot-size)/2);
  }
  #marble-ball-4{
    left: calc(75dvw - var(--dot-size)/2);
    top:  calc(25dvh - var(--dot-size)/2);
  }
  #marble-ball-5{
    left: calc(75dvw - var(--dot-size)/2);
    top:  calc(75dvh - var(--dot-size)/2);
  }
  
  .radios{
    z-index:10;
    display:flex;
    gap:1;
    position:absolute;
    flex-direction:column;
    margin-top:2em;
    
    .radio-case{      
      .button{
        visibility:hidden;
      }
      .button:checked+label {
          color: white;
      }
      
      .color-name{
        font-size:2em;
      }
      .color-name:hover {
        color: white;
      }
    }
  }
}

次はCSS(scss)です。
こちらは特にいうことないです。
// inspired by "Layered animations with anime.js"
// https://codepen.io/juliangarnier/pen/LMrddV
// and it needs to change anime.js about dealing colors.
// please check my github. 
// https://github.com/keiyashi/animejs_for_marbleGradient

var currentHue = 0;
var angleSetting = 20;
var angleDefault = 180;
var hueAngle = 180;
var colorMap ;

document.addEventListener("DOMContentLoaded", (event) => {
  layeredAnimation();

  colorMap ={        // hue angle
    'red':      {hue: 0   , angle: angleSetting } ,  
    'yellow':   {hue: 60  , angle: angleSetting } ,
    'green':    {hue: 120 , angle: angleSetting } , 
    'blue':     {hue: 200 , angle: angleSetting } ,
    'purple':   {hue: 300 , angle: angleSetting } ,
    'gradient': {hue: 180 , angle: angleDefault }
  };
  
  radioSwitchInit();
});

function layeredAnimation(){
    var layeredAnimationEl = document.querySelector('.grad-board');
    var colorPints = layeredAnimationEl.querySelectorAll('.marble-ball');

    for (var i = 0; i < colorPints.length; i++) {
        animateShape(colorPints[i]);
    }
}

function animateShape(el) {
    var easings = ['easeInOutQuad', 'easeInOutCirc', 'easeInOutSine'];
    var animation = anime.timeline({
        targets: el,
        duration: function() { return anime.random(3000, 4000); },
        easing: function() { return easings[anime.random(0, easings.length - 1)]; },
        complete: function(anim) { animateShape(anim.animatables[0].target); },
    })
    .add({
        translateX: anime.random(-500, 500),
        translateY: anime.random(-400, 400),
        scale: anime.random(100, 200) / 100,
        background: makeColorContext(),
    }, 0);
}

function makeColorContext(){
    var angle = hueAngle;
    var angleBand = anime.random(-angle, angle);
    var hue   = currentHue + angleBand;
    if(hue >= 360){
        hue = 720 - hue;
    }else if(hue < 0){
        hue = 360 + hue;
    }
  
   var colorText = 'hsl(' + Math.round(hue) + ', ' + 80 + '%, ' + 50 + '%)';
    var outputContext = 'radial-gradient(' + anime.hsl2Rgb(colorText) + ' 0px, transparent 50%)';
    return outputContext;
}

function radioSwitchInit(){
    var radios = document.querySelectorAll('.button');
    radios.forEach(curRadio => {
        curRadio.addEventListener("change", (e) =>{
            currentHue = colorMap[curRadio.id].hue;
            hueAngle   = colorMap[curRadio.id].angle;
        })
     })
}

今回、ラジオボタンのIDをキーにしたカラーマップを準備しています。
バリューには色相角度と、取り得る角度の範囲を持たせています。

変数として選択中の色相角度と変化可能角度があり、任意のラジオボタンが押下されるとカラーマップから該当するidの色相角度と範囲を取得します。

上記変数はアニメーション中に参照しているので、ラジオボタンを押すことでグラデーションカラーが変わります。

無事、インタラクティブな色の変化を起こすことが出来ました。