5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HTML・CSS・JSで円のアニメーション付きのカウントダウンタイマーを作ろう

Last updated at Posted at 2024-05-05

完成品

時間の中に入力する値は1~99であることを前提にしている

code

HTML

index.html
<body>
  <div id="selects">
    <div>
      <label for="radius">半径:</label>
      <input type="range" id="radius" name="radius" min="50" max="500" value="200" oninput="changeRadius(this.value);">
    </div>
    <!-- 追加:ボタン -->
    <div>
      <button onclick="startAnimation()">Start Animation</button>
      <button onclick="all_reset()">Reset Animation</button>
    </div>
    <div>
      <label for="radius">時間:</label>
      <input type="number" id="time" name="time" min="1" max="99" value="60" oninput="changeTimes(this.value);">
    </div>
  </div>
  <div class="circle2">
    <div class="triangle1" id="triangle1"></div>
    <div class="triangle2" id="triangle2"></div>
    <div class="triangle3" id="triangle3"></div>
    <div class="triangle4" id="triangle4"></div>
    <div class="triangle5" id="triangle5"></div>
    <div class="triangle6" id="triangle6"></div>
  </div>
  <!-- 追加:秒数表示 -->
  <div class="circle">

  </div>
  <div id="count_box">
    <p id="currentSeconds">0</p>
  </div>
</body>

CSS

細かな説明が気になる方は以下を参照ください

index.css
:root {
    --radius: 200px; /* デフォルトの半径を定義 */
  }
  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: 175px;
    height: 175px;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    grid-column: 2/5;
    grid-row: 2/5;
    background-color: black; /* 円の背景色 */
    border-radius: 50%; /* 円形にする */
    position: relative;
  }
  .circle2 {
    width: 200px;
    height: 200px;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    grid-column: 2/5;
    grid-row: 2/5;
    background-color: red; /* 円の背景色 */
    border-radius: 50%; /* 円形にする */
    position: relative;
    overflow: hidden;
  }
  .triangle1{
    height: auto;
    width: auto;
    display: grid;
    grid-column: 1/2;
    grid-row: 1/2;
  }
  .triangle2{
    height: auto;
    width: auto;
    display: grid;
    grid-column: 1/2;
    grid-row: 1/2;
  }
  .triangle3{
    height: auto;
    width: auto;
    display: grid;
    grid-column: 1/2;
    grid-row: 1/2;
  }
  .triangle4{
    height: auto;
    width: auto;
    display: grid;
    grid-column: 1/2;
    grid-row: 1/2;
  }
  .triangle5{
    height: auto;
    width: auto;
    display: grid;
    grid-column: 1/2;
    grid-row: 1/2;
  }
  .triangle6{
    height: auto;
    width: auto;
    display: grid;
    grid-column: 1/2;
    grid-row: 1/2;
  }

  .triangle1::before {
    content: "";
    width: 0;
    height: 0;
    grid-column: 1/2;
    grid-row: 1/2;
    transform: translate(calc(var(--radius) / 2), calc(var(--radius) / 2 - var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))));
    transform-origin: left bottom; /* 中心を縦横の中心に設定 */
    border-right: calc(var(--radius) * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent;
    border-top: calc(var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))) solid #f3c87d;
  }
  .triangle2::before {
    content: "";
    width: 0;
    height: 0;
    grid-column: 1/2;
    grid-row: 1/2;
    transform-origin: left bottom; /* 中心を縦横の中心に設定 */
    transform: translate(calc(var(--radius) / 2), calc(var(--radius) / 2 - var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg)))) rotate(60deg);
    border-right: calc(var(--radius) * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent;
    border-top: calc(var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))) solid #f3c87d;
  }
  .triangle3::before {
    content: "";
    width: 0;
    height: 0;
    grid-column: 1/2;
    grid-row: 1/2;
    transform-origin: left bottom; /* 中心を縦横の中心に設定 */
    transform: translate(calc(var(--radius) / 2), calc(var(--radius) / 2 - var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg)))) rotate(120deg);
    border-right: calc(var(--radius) * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent;
    border-top: calc(var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))) solid #f3c87d;
  }
  .triangle4::before {
    content: "";
    width: 0;
    height: 0;
    grid-column: 1/2;
    grid-row: 1/2;
    transform-origin: left bottom; /* 中心を縦横の中心に設定 */
    transform: translate(calc(var(--radius) / 2), calc(var(--radius) / 2 - var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg)))) rotate(180deg);
    border-right: calc(var(--radius) * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent;
    border-top: calc(var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))) solid #f3c87d;
  }
  .triangle5::before {
    content: "";
    width: 0;
    height: 0;
    grid-column: 1/2;
    grid-row: 1/2;
    transform-origin: left bottom; /* 中心を縦横の中心に設定 */
    transform: translate(calc(var(--radius) / 2), calc(var(--radius) / 2 - var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg)))) rotate(240deg);
    border-right: calc(var(--radius) * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent;
    border-top: calc(var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))) solid #f3c87d;
  }
  .triangle6::before {
    content: "";
    width: 0;
    height: 0;
    grid-column: 1/2;
    grid-row: 1/2;
    transform-origin: left bottom; /* 中心を縦横の中心に設定 */
    transform: translate(calc(var(--radius) / 2), calc(var(--radius) / 2 - var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg)))) rotate(300deg);
    border-right: calc(var(--radius) * 1.25 * sin(calc(var(--angle) * 1deg))) solid transparent;
    border-top: calc(var(--radius) * 1.25 * cos(calc(var(--angle) * 1deg))) solid #f3c87d;
  }
  .currentSeconds{
    grid-column: 1/2;
    grid-row: 1/2;
  }
  #count_box{
    display: grid;
    place-items: center;
    height: var(--radius);
    width: var(--radius);
    grid-column: 2/5;
    grid-row: 2/5;
    font-size: calc(var(--radius) / 2);
    font-weight: bold;
    z-index: 1;
  }
  p{
    color: white;
    margin: 0;
  }

JS

smoothTransition()滑らかなアニメーショにする際,正確な時間を表示するためにスタート時刻との差から計算した角度changeAngle(angle, times_animat)で使用している.

index.js
var times = 60; // 残り時間
changeTimes(times);
var reset_times = false; // アニメーションをリセットするかどうか

// 時間を変更する関数
function changeTimes(time){
  updateCurrentSeconds(time);
}

// アニメーション開始関数
function startAnimation() {
  if(!reset_times){
    reset_times = true;
    smoothTransition(); // 360度までの滑らかな遷移
  }
}

// 秒数を表示する関数
function updateCurrentSeconds(seconds) {
  document.getElementById('currentSeconds').textContent = Math.floor(seconds);
}

// 中心角を変更する関数
function changeAngle(angle, times_animat){
  if(angle < 65){
    document.getElementById('triangle1').style.setProperty('--angle', angle);
  }else if(angle < 125){
    document.getElementById('triangle2').style.setProperty('--angle', angle - 60);
  }else if(angle < 185){
    document.getElementById('triangle3').style.setProperty('--angle', angle - 120);
  }else if(angle < 245){
    document.getElementById('triangle4').style.setProperty('--angle', angle - 180);
  }else if(angle < 305){
    document.getElementById('triangle5').style.setProperty('--angle', angle - 240);
  }else if(angle < 360){
    if(angle < 360){
      document.getElementById('triangle6').style.setProperty('--angle', angle - 300);
    }else{
      document.getElementById('triangle6').style.setProperty('--angle', 65);
    }
  }else{
    reset_angle();
  }
  // 現在の秒数を更新
  if (times_animat - (angle / 360 * times_animat) > 0) {
      updateCurrentSeconds(times_animat - (angle / 360 * times_animat));
  } else {
      updateCurrentSeconds(0);
  }
}

// 中心角のリセット
function reset_angle(){
  document.getElementById('triangle1').style.setProperty('--angle', 0);
  document.getElementById('triangle2').style.setProperty('--angle', 0);
  document.getElementById('triangle3').style.setProperty('--angle', 0);
  document.getElementById('triangle4').style.setProperty('--angle', 0);
  document.getElementById('triangle5').style.setProperty('--angle', 0);
  document.getElementById('triangle6').style.setProperty('--angle', 0);
  return true;
}

// 初期化
function all_reset(){
  reset_times = false;
}

// 滑らかなアニメーション
function smoothTransition() {
  var currentAngle = 0; // 現在の角度
  var times_animat = document.getElementById('time').value; // カウント時間
  var duration = 1000 * times_animat; // アニメーションの期間(ミリ秒)
  
  var startTime = performance.now(); // アニメーションの開始時間

  var animation = setInterval(function() {
    var elapsed = performance.now() - startTime; // 経過時間

    // 現在の角度を更新
    currentAngle = (360) * (elapsed / duration);
    // 中心角を計算して更新 
    changeAngle(currentAngle, times_animat);

    // アニメーションが終了したら終了
    if (elapsed >= duration || !reset_times) {
      clearInterval(animation);
      updateCurrentSeconds(times_animat);
      reset_angle();
      reset_times = false;
    }
  }, 1000 / 60); // 60 FPSでアニメーション
}
5
5
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
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?