3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

webサイト上で動く円グラフのアニメーション

Posted at

完成図

使い方

  1. Addボタンを押す
  2. 要素の出力したい角度を入力
  3. 要素を欲しい数だけ繰り返し追加する
  4. Generationボタンを押す
  5. Start Animationボタンを押す

注意点
Addボタンで追加した要素を後から変更した場合は必ずGenerationボタンを押すこと

code

こちらを基に応用させたものである

HTML

index.html
<div id="selects">
    <div>
      <button onclick="startAnimation()">Start Animation</button>
      <button onclick="all_reset()">Reset Animation</button>
    </div>

    <div id="list"></div>   
    
    <button id="addButton">Add</button> 
    <button id="generationButton">Generation</button>
</div>

<div id="circle" class="circle"></div>

CSS

index.css
  body {
    display: grid;
    place-items: center;
    height: calc(100vh - 16px);
    width: calc(100vw - 16px);
    grid-template-columns: repeat(5, 1fr);
    grid-template-rows: repeat(5, 1fr);
  }
  #selects {
    height: 100%;
    width: 100%;
    grid-column: 1/6;
    grid-row: 1/2;
    display: grid;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .circle {
    width: 200px;
    height: 200px;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    grid-column: 2/5;
    grid-row: 2/5;
    background-color: gray; /* 円の背景色 */
    border-radius: 50%; /* 円形にする */
    position: relative;
    overflow: hidden;
  }

JS

index.js
// ランダムな色を生成する関数
function getRandomColor() {
  // 0 から 255 の間のランダムな整数を生成して、RGB値を作成する
  var r = Math.floor(Math.random() * 256);
  var g = Math.floor(Math.random() * 256);
  var b = Math.floor(Math.random() * 256);

  // RGB値をCSSの色の形式に変換して返す
  return 'rgb(' + r + ',' + g + ',' + b + ')';
}

// addボタンがクリックされたときの処理
document.getElementById('addButton').addEventListener('click', function() {
  var list = document.getElementById('list');
  var newItem = document.createElement('div');
  var newIndex = list.children.length + 1;

  newItem.innerHTML = `
  <label for="title${newIndex}_percent">角度を入力</label>
  <input type="number" class="percent" id="title${newIndex}_percent" name="title${newIndex}_percent">
  `;

  list.appendChild(newItem);

  // 追加された要素の数値入力フィールドを取得
  var newNumberInput = document.getElementById('title' + newIndex + '_percent');

  // 追加された要素の数値入力フィールドが空でない場合、ボタンを有効にする
  if (!newNumberInput.value.trim()) {
    document.getElementById('addButton').disabled = true;
  } else {
    document.getElementById('addButton').disabled = false;
  }

  // 追加された要素の数値入力フィールドの値が変更されるたびにチェックする
  newNumberInput.addEventListener('input', function() {
    if (!this.value.trim()) {
      document.getElementById('addButton').disabled = true;
    } else {
      document.getElementById('addButton').disabled = false;
    }
  });
});

document.getElementById('generationButton').addEventListener('click', function() {
  var circle = document.getElementById('circle');
  circle.innerHTML = ''; // 円を空にする

  var len = document.getElementById('list').children.length; 
  var rotates = 0;

  // 360度を超えるかどうかをチェック
  var check = 0;
  for (var k = 1; k <= len; k++) {
    var timeInput = document.getElementById('title' + k + '_percent');
    check += Number(timeInput.value);
    if (check > 360) {
      alert("360度を超えています. 値を再入力してください");
      return;
    }
  }

  // 各要素を生成
  for (var t = 1; t <= len; t++) {
    var timeInput = document.getElementById('title' + t + '_percent');
    var color = getRandomColor();
    var count = Math.ceil(Number(timeInput.value) / 60); // 60度ごとの要素の数

    for (var i = 0; i < count; i++) {
      var newIndex_c = circle.children.length + 1;
      var newTriangle = document.createElement('div');
      newTriangle.style.height = '0';
      newTriangle.style.width = '0';
      newTriangle.style.display = 'grid';
      newTriangle.style.gridColumn = '1/2';
      newTriangle.style.gridRow = '1/2';

      var angle = (i + 1) * 60; // 60度ごとの角度
      var myangle = Math.min(Number(timeInput.value) - i * 60, 60); // 残りの角度
      console.log("rotates: " + rotates + "  myangle: " + myangle);

      newTriangle.style.transform = 'translate(calc(200px / 2), calc(200px / 2 - 200px * 1.25 * cos(var(--angle) * 1deg))) rotate(' + rotates + 'deg)';
      newTriangle.style.transformOrigin = 'left bottom';
      newTriangle.style.borderRight = 'calc(200px * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent';
      newTriangle.style.borderTop = 'calc(200px * 1.25 * cos(calc(var(--angle) * 1deg))) solid ' + color;
      newTriangle.classList.add('triangle' + newIndex_c);
      newTriangle.id = 'triangle' + newIndex_c;
      newTriangle.setAttribute('data-myangle', myangle);

      circle.appendChild(newTriangle);

      rotates += myangle;
    }
  }
});
var reset_times = true; // アニメーションをリセットするかどうかのフラグ
var animatnow = false; // アニメーション中かどうかのフラグ

// アニメーション開始関数
function startAnimation() {
  smoothTransition(360, 10);
}

// アニメーション関数(滑らかに移動する関数) 
function smoothTransition() {
  animatnow = true;
  var currentAngle = 0; // 現在の角度
  var frames = 100; // アニメーションのフレーム数
  var duration = 1000; // アニメーションの期間(大まかな)
  var interval = duration / frames; // フレーム間の間隔(ミリ秒)
  var increment = (360 - currentAngle) / frames; // 各フレームでの角度の増加量
  var circle = document.getElementById('circle');
  var newIndex_c = circle.children.length + 1;

  var animation = setInterval(function() {
    // 現在の角度を更新
    currentAngle += increment;
    for(var i = 1; i < newIndex_c; i++){
      console.log('changeAngle:' + i)
      changeAngle(currentAngle, i); // changeAngle の実行結果を取得
    }

    // アニメーションが終了(リセット)したらクリアする
    if (currentAngle >= 360 || !reset_times) {
      if(!reset_times){
        reset_angle();
      }
      animatnow = false;
      reset_times = true;
      clearInterval(animation);
    }
  }, interval);
}

// 角度を変更する関数
function changeAngle(angle, i){
  var timeInput = document.getElementById('triangle' + i);
  console.log("changeAngle:" + i)
  var timeValue = timeInput.getAttribute('data-myangle');
  console.log("timeValue:" + timeValue + "  i:" + i)

  // 既に設定された角度の合計を計算
  var per = 0;
  if(i>1){
    for (var j = 1; j < i; j++) {
      var timeInput_j = document.getElementById('triangle' + j);
      var timeValue_j = timeInput_j.getAttribute('data-myangle');
      per += Number(timeValue_j);
    }
  } 
  angle = Number(angle);
  console.log("per:" + per + "  timeValue:" + timeValue + "  angle:" + angle + "  i:" + i)
  if(angle >= Number(timeValue) + per){
    document.getElementById('triangle' + i).style.setProperty('--angle', Number(timeValue) + 0.1);
  }else{
    if(0>angle - per){
      document.getElementById('triangle' + i).style.setProperty('--angle', 0);
    }else{
      document.getElementById('triangle' + i).style.setProperty('--angle', angle - per);
    }
  }

}

// アニメーション終了関数(角度をリセットする関数)
function reset_angle(){
  var circle = document.getElementById('circle');
  var newIndex_c = circle.children.length + 1;
  for(var i = 1; i < newIndex_c; i++){
    document.getElementById('triangle' + i).style.setProperty('--angle', 0);
  }
}

// 初期化
function all_reset(){
  if(animatnow){
    reset_times = false;
  }else{
    reset_angle();
  }
}
3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?