Geolocation APIで現在地を取得している間にローディングアニメーションが起動されるようにしてみた。

## 実装のきっかけ
現在個人開発を行なっており、その際にGeolocation APIで現在地の緯度経度を取得する処理を実装していました。
現在地の取得まで最大で５秒ほどかかることがあり、従来だと以下のように何のアニメーションも表示されないので、ユーザーに不安感を抱かせてしまうと思いました。
そこで、「現在地の取得」をクリックした際にローディングアニメーションが実行されるようにすればユーザーの不安感を軽減できるのではないかと思い、実装することにしました。
![現在地の取得.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/0af332b5-ea4f-7967-7b78-fcb20295b5bf.gif)

## この記事の対象となる方
- Geolocation APIで現在地を取得する機能を実装している方
- その上でローディングアニメーションを実装したい方 

## 開発環境
- Ruby 2.6.6
- Rails 6.1.0

## コード

```app/views/layout/application.html.erb
  <body class="font-mono text-base sm:text-xl">
    <div class="loading_zone">
      <div class="spinner"></div>
    </div>
    <%= render  "layouts/header" %>
    <div>
      <p class="notice"><%= notice %></p>
      <p class="alert"><%= alert %></p>
      <%= yield %>
    </div>
    <%= render  "layouts/footer" %> 
  </body>
```

```app/assets/stylesheets/application.scss
  // ローディング画面のcss

.loading_zone {
  position: fixed;
  top: 0;
  left: 0;
  opacity: 0;
  color: #f5f5f5;
}
.spinner {
  margin: 300px auto;
  width: 200px;
  height: 200px;
  background-color: #fff;
  border-radius: 100%;
  animation: sk-scaleout 1.0s infinite ease-in-out;
  @media(max-width: 500px){
    margin: 200px auto;
  }
}
/* ローディングアニメーション */
@keyframes sk-scaleout {
  0% {
    transform: scale(0);
  } 100% {
    transform: scale(1.0);
    opacity: 0;
  }
}

.loading {
  width: 100vw;
  height: 100vh;
  transition: all 1s;
  background-color:gray;
  opacity: 0.9;
  z-index: 9999;
  position: fixed;
}
```

```app/javascript/packs/getcurrentlocation.js
  function geoFindMe() {
  const spinner = document.getElementsByClassName('loading_zone')[0];
  spinner.classList.add('loading')
  function success(position) {
    const latitude  = position.coords.latitude;
    const longitude = position.coords.longitude;
    document.getElementById('location').value = `${latitude},${longitude}`;
    spinner.classList.remove('loading'); 
  }

  function error() {
    alert('エラーが発生しました。')
  }

  if(!navigator.geolocation) {
     alert('Geolocation is not supported by your browser');
  } else {
    navigator.geolocation.getCurrentPosition(success, error);
  }
}

document.querySelector('#get_current_spot').addEventListener('click', geoFindMe);
```

##何をやっているのか？
では一個づつ解説していきます。


```app/views/layout/application.html.erb
  <body class="font-mono text-base sm:text-xl">
    <div class="loading_zone">
      <div class="spinner"></div>
    </div>
    <%= render  "layouts/header" %>
    <div>
      <p class="notice"><%= notice %></p>
      <p class="alert"><%= alert %></p>
      <%= yield %>
    </div>
    <%= render  "layouts/footer" %> 
  </body>
```

application.html.erbの中にローディングアニメーション用のdivを用意しています。

```app/assets/stylesheets/application.scss
  // ローディング画面のcss

.loading_zone {
  position: fixed;
  top: 0;
  left: 0;
  opacity: 0;
  color: #f5f5f5;
}
.spinner {
  margin: 300px auto;
  width: 200px;
  height: 200px;
  background-color: #fff;
  border-radius: 100%;
  animation: sk-scaleout 1.0s infinite ease-in-out;
  @media(max-width: 500px){
    margin: 200px auto;
  }
}
/* ローディングアニメーション */
@keyframes sk-scaleout {
  0% {
    transform: scale(0);
  } 100% {
    transform: scale(1.0);
    opacity: 0;
  }
}

.loading {
  width: 100vw;
  height: 100vh;
  transition: all 1s;
  background-color: gray;
  opacity: 0.9;
  z-index: 9999;
  position: fixed;
}
```

ここで見慣れない＠keyframesというコードが出てきたと思います。
＠keyframesはアニメーション開始から終了するまでどのようなアニメーションをするのか指定できるCSSの文法です。
0％を開始直後、100%を終了時としています。

＠keyframesについて詳しく知りたい場合はこちらを参照ください！

https://developer.mozilla.org/ja/docs/Web/CSS/@keyframes


```
  .spinner {
  margin: 300px auto;
  width: 200px;
  height: 200px;
  background-color: #fff;
  border-radius: 100%;
  animation: sk-scaleout 1.0s infinite ease-in-out;
  @media(max-width: 500px){
    margin: 200px auto;
  }
}
/* ローディングアニメーション */
@keyframes sk-scaleout {
  0% {
    transform: scale(0);
  } 100% {
    transform: scale(1.0);
    opacity: 0;
  }
}

```

つまり、上のコードは

1.アニメーション開始時
→何も表示されない
2.アニメーション開始〜終了直前まで
→円を表示する（最大サイズは直径200px）
3.アニメーション終了時
→opacityがゼロなので、消える。

以降1~3を繰り返す。
というアニメーションになっています。


最後にJavaScriptで、クリックしたときにアニメーションが表示されるように処理を書きます。

```app/javascript/packs/getcurrentlocation.js
  function geoFindMe() {
  const spinner = document.getElementsByClassName('loading_zone')[0];
  spinner.classList.add('loading')
  function success(position) {
    const latitude  = position.coords.latitude;
    const longitude = position.coords.longitude;
    document.getElementById('location').value = `${latitude},${longitude}`;
    spinner.classList.remove('loading'); 
  }

  function error() {
    alert('エラーが発生しました。')
    spinner.classList.remove('loading'); 
  }

  if(!navigator.geolocation) {
     alert('Geolocation is not supported by your browser');
  } else {
    navigator.geolocation.getCurrentPosition(success, error);
  }
}

document.querySelector('#get_current_spot').addEventListener('click', geoFindMe);
```

clickした後に走るgeoFindMeという関数の中に詳しい設定を書きます。
loading_zoneクラスを持つdivに対して、処理が走っている間はloadingクラスを付与、処理が終わった後はloadingクラスを削除するようにコードを書きました。

## 最後に
以上で、ローディングアイコンの実装が完了しました！
最後まで読んでいただき、誠にありがとうございます。

少しでも参考になったと思ったらLGTMを頂けますと幸いです！