思い出のイルミネーション(？)

この記事は[ クソアプリ Advent Calendar 2021 ](https://qiita.com/advent-calendar/2021/kuso-app)の12日目の記事です。

## 作ったもの
Web上で選択した画像を配置して光らせるクソアプリを作りました。
サイズ調整が間に合わなかったため、スマホでは表示出来ません。(ごめんなさい)

[アプリはこちら](http://advent-2021-12-12-3939.s3-website-ap-northeast-1.amazonaws.com/)

## 作った背景(読み飛ばして良いポエム)

あれは10年ほど前のこと…

クリスマス時期にサーバー室での作業を終え、電気を消灯した時。
私の目に無数の光が飛び込んできたのでした。

それは、サーバーたちのLEDの灯火。
たしかに稼働していることを示す光。

その時私は思いました。

「うほっ、これは良いイルミネーション」

時は過ぎ、もうサーバー室で作業することはありませんが、
たまにあの時の光を思い出します。

良い機会ですので、思い出が風化する前に再現してみることにしました。

…そして2021/12/12、出来上がったのがこちらのクソアプリです。

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177998/8b0d25d5-3a63-b76e-8d93-676c6fafa768.gif)


## コンセプト
Web地図用のライブラリを使用してみたかったので、Leafletを使用します。

### 使用ライブラリ
* [Leaflet](https://leafletjs.com/)
* [React Leaflet](https://react-leaflet.js.org/)

### 作業環境
```
  - Windows 10
  - react@17.0.2
  - react-leaflet@3.2.2
  - leaflet@1.7.1
```

## 作成のポイント
### 地図以外の画像を背景にする

地図ではなく自前の画像を使用する場合は、位置情報は緯度経度ではないため
「CRS.Simple」を利用するようです。
ImageOverlayで背景画像(ラック図)を表示しました。

#### 参考
* [Not of this earth](https://leafletjs.com/examples/crs-simple/crs-simple.html)
* [Leaflet で地図以外の画像を扱う #1 L.ImageOverlay](https://lendl.sakura.ne.jp/journals/leaflet-image-overlay/)

```react:App.tsx
// ラック図の画像を読み込む
import BgImage from './assets/img/bg_server_rack_dark.png';

// 省略

const App = () => {

  //画像サイズからXY軸調整
  const map_image = {
    url:    BgImage,
    width:  600,
    height: 620
  };

  // 座標の指定
  const imageBounds = new LatLngBounds([0, 0],
    [map_image.height, map_image.width]);

  //省略

  return(
    //省略

    <MapContainer 
      className="map"
      center={[310, 300]}
      zoom={-4}
      zoomControl={false}
      scrollWheelZoom={false}
      crs={CRS.Simple}            // CRS.Simpleを設定
    >

      <ImageOverlay
        url={BgImage}
        bounds={imageBounds}
        opacity={1.0}
        zIndex={10}
      />

      <MyComponent />
                  
    </MapContainer>

    //省略
  )
}
```

### 各機器の画像を地図のマーカーとして表示する
Leafletの[divアイコン](https://leafletjs.com/reference.html#divicon)という機能で、
各機器のコンポーネントを呼び出します。

#### 参考
[Creating a dynamic “JSX” marker with react-leaflet](https://medium.com/@nikjohn/creating-a-dynamic-jsx-marker-with-react-leaflet-f75fff2ddb9)

機器毎にファイルを作成し、機器の画像と光らせたい部分の指定用に
divタグを付けておきます。

```react:srv2u.tsx
import React from 'react';
import SrvImg from '../assets/img/2u_SRV.png';
import '../assets/css/style.css';

const Srv2u = () => {

  return (
    <>
      <div className="base">
        <div className="light dsp"></div>
        <div className="light orb"></div>
        <img src={SrvImg} alt=""/>
      </div>
    </>
  );
}

export default Srv2u;
```


作成したコンポーネントは、L.divIconで設定しておきます。
配置先はクリックした場所にしています。

`<MapContainer>`の中に要素を入れておくと、イベントが拾えるようです。
[useMapEvents](https://react-leaflet.js.org/docs/api-map/#usemapevents)


```react:App.tsx
import ReactDOMServer from 'react-dom/server';
import L from "leaflet";
import { MapContainer, ImageOverlay, useMapEvents } from 'react-leaflet'
import "leaflet/dist/leaflet.css";

import './assets/css/style.css';
import Srv2u from './component/Srv2u';

// 省略

const App = () => {

// 省略

  const iconSrv2u = L.divIcon({
    html: ReactDOMServer.renderToString(<Srv2u />)
  });

  const MyComponent = () => {
    let target_icon = iconSrv2u;
    
    // 省略 機器のタイプをステートで分岐させています

    // マップのクリックイベントを検知し、クリック箇所の座標にマーカーを追加します
    // draggable:trueにするとマーカーを動かすことができます
    const map = useMapEvents({
      click: (e) => {
        L.marker(e.latlng, {icon:target_icon, draggable:true}).addTo(map);
      }
    })

  }

}
```

### 光らせる部分
cssの[キーフレーム](https://developer.mozilla.org/ja/docs/Web/CSS/@keyframes)で時間経過とともに色を変更します。

#### 参考
[コピペで簡単！CSSで作るグローエフェクト15選【光彩/ドロップシャドウ/テキスト装飾/HTML】](https://webdesignfacts.net/entry/glow-effect-css/?utm_source=pocket_mylist#gsc.tab=0)
[CSS3アニメーションに挑戦！色が移り変わる背景を実装しよう](https://www.webcreatorbox.com/tech/css3-animation-colour)

#### 通常時

```css:style.css
.base {
  width: 213px;
  height: 29px;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.dsp {
  margin: 0;
  padding: 0;
  position: absolute;
  top: 2px;
  left: 15px; 
  width: 15px;
  height: 17px;
  background: #85c4ee;
  opacity: .8;
  animation: dsp 5.0s infinite;  /* ここで下のdspを指定 */
}

@keyframes dsp {
  0%,100% {
    box-shadow: 0 0 0 0 #c8e2f1;
  }
  50% {
    box-shadow: 0 0 15px 2px #c8e2f1;
  }
}
```

#### クリスマスモード突入時

```css:style.css
.active .dsp {
  animation: active-dsp 5.0s infinite; /* 別のアニメーションを指定 */
}

@keyframes active-dsp {
  0%,100% {
    box-shadow: 0 0 4px 2px #8FFFCF;
    background-color: #0bfaca;
  }
  25%{
    box-shadow: 0 0 4px 2px #f8d7d4;
    background-color: #f872e6;
  }

  50% {
    box-shadow: 0 0 8px 2px #eec71b;
    background-color: #f1c40f;
  }
  75% {
    box-shadow: 0 0 4px 2px #8FFFCF;
    background-color: #0bfaca;
  }
}
```

## 改善点

* 一度配置すると削除出来ません。個別に削除出来るようにしたいです
* ラック外に配置できてしまうので、制御出来るようにしたいです
* コンポーネント毎に光る設定が固定のため、ランダムに光ると尚良いです
* 各機器の幅や光る部分は正確ではありません
* もう少し機器の種類を充実させたかったです

## 終わりに
思い出の中のサーバーたちが、Web上に再現出来…た気がします、多分。
だいたいこんな感じだったはず…

折角なので緑やオレンジ以外に光って欲しい願望も叶いました。
正常なのか異常なのか分かりづらいので、いつもの色が良いなと思いました。


**沢山詰め込んだ時のクリスマスモードの荒ぶりがすごい…！**

![owarini.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177998/b9048e5c-edeb-8e89-3503-f5a89976397d.gif)

## (オマケ)去年のクソアプリ
去年のクソアプリはペンギンしてました。
ヒゲペンギン推しの同士を増やすべく、リンクを貼っておきます。


https://qiita.com/makipen_39/items/e9ed7fa1343be8bd0b15

## 素材と資料の再掲
ラックやサーバーなどの素材は以下サイトからお借りしました。
ありがとうございます。

### 素材

* [サーバーのイラスト（PNG形式画像）](https://www.digipot.net/?p=1089#google_vignette)
* [YAMAHA アイコン・外観写真ダウンロード](https://network.yamaha.com/support/download/tool)

### Leaflet
* [Leaflet](https://leafletjs.com/)
* [Not of this earth](https://leafletjs.com/examples/crs-simple/crs-simple.html)
* [Leaflet で地図以外の画像を扱う #1 L.ImageOverlay](https://lendl.sakura.ne.jp/journals/leaflet-image-overlay/)
* [divアイコン](https://leafletjs.com/reference.html#divicon)

### React-Leaflet
* [React Leaflet](https://react-leaflet.js.org/)
* [Creating a dynamic “JSX” marker with react-leaflet](https://medium.com/@nikjohn/creating-a-dynamic-jsx-marker-with-react-leaflet-f75fff2ddb9)
* [useMapEvents](https://react-leaflet.js.org/docs/api-map/#usemapevents)

### CSS
* [キーフレーム](https://developer.mozilla.org/ja/docs/Web/CSS/@keyframes)
* [コピペで簡単！CSSで作るグローエフェクト15選【光彩/ドロップシャドウ/テキスト装飾/HTML】](https://webdesignfacts.net/entry/glow-effect-css/?utm_source=pocket_mylist#gsc.tab=0)
* [CSS3アニメーションに挑戦！色が移り変わる背景を実装しよう](https://www.webcreatorbox.com/tech/css3-animation-colour)