ボーダラインアニメーションをいくつか調べて実装したのでまとめてみる[Next.js, SCSS]

ボーダーラインアニメーションをいくつか実装してみたいなと思い、色々調べて実装してみたので、今回は備忘録として実装したボーダラインアニメーションのやり方について紹介しようと思います。

# 開発環境

Next.jsでReactを使用して、今回のアニメーションはSCSSを使って実装しました

- Next.js
- SCSS(SCSS modules)

:::note
### CSS modulesとは
CSSのクラス名をグローバルスコープではなく、ローカルスコープで定義できるようにする技術です。

CSSでは別ファイルで同じで同じクラス名を使うことができません。

しかしCSS modulesを使うと自動的に名前がユニークになり、それを使って下のようにスタイルを適用できるようになります。
```scss:style.module.scss
.title{
    color: #000000
}
```

```typescript:index.tsx
import style from "./style.module.scss":

const Components = () =>{
    return(
        <div className={style.title}>Hello</div>
    )
}
```
:::


# メニューバー(ヘッダ-)がhoverされたら線が出るように
メニューバーで要素をhoverしたら、紫の線が伸びるアニメーションを実装しました。
![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3766230/0829de30-64cb-4f11-c359-270864fd50bd.gif)

- tsxファイル
```index.tsx
import style from "@/features/header/styles/style.module.scss"

export const Header = () =>{
    return (
        <div className={style.headerContainer}>
            <div className={style.menuContainer}>
                <div className={style.headerContent}>Home</div>
                <div className={style.headerContent}>About</div>
                <div className={style.headerContent}>Works</div>
            </div>
        </div>
    )
}
```

- scssファイル
```style.module.scss
.headerContainer {
  display: flex;
  position: relative;
}

.menuContainer{
  display: flex;
  position: absolute;
  right: 0;
}

.headerContent{
  font-size: 2rem;
  margin: 1rem 1rem 1rem 4rem;

  position: relative;
  &::after {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    width: 0;
    height: 3px;
    background-color: theme.$purple;
    transition: width 0.3s ease;
  }

  &:hover::after {
    width: 100%;
    left: 0;
  }
}
```

## やり方

ここでは擬似要素afterを使って下の紫の線を作っています。

:::note
### beforeとafterとは
タグで囲んでいる要素の前方に追加要素を加えることができる擬似要素のことです。

beforeは要素の前方に、afterは要素の後方に追加します。

例えば、下のようなhtmlがあったとします。
```html
<h1>こんにちわ</h1>
```

下のようなcssを追加したとします。
```css
h1::before {
  content: "☀️";
}
```

このような場合には、webサイト上では下のように表示されます。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="JoPpvNy" data-pen-title="Untitled" data-user="maooz4426" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/maooz4426/pen/JoPpvNy">
  Untitled</a> by mao ozaki (<a href="https://codepen.io/maooz4426">@maooz4426</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

:::



```scss
&::after {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    width: 0;
    height: 3px;
    background-color: theme.$purple;
    transition: width 0.3s ease;
  }
```

`position:absolute`を指定して、親要素から右下の位置のに高さ3pxだけある紫色の物体を配置しています。

:::note
### positionの注意点
`position：absolute`は一つ上のタグに`position：relative`が当てられている必要があります。

absoluteは親要素からの絶対位置で要素の場所を指定します。
親要素は勝手に決められるわけでなく、relativeで指定する必要があるので注意してください。
:::

そこからtransitionで横に広がるアニメーションが0.3sで適度な速度で加減速するようにしています

そこからafter要素がhoverされたら左からwidthを100%になるようにしています。これによって紫の線が、scssに当てている要素の下に右から左に流れるようにアニメーションすることができます。

```scss
  &:hover::after {
    width: 100%;
    left: 0;
  }
```

# Imageコンポーネントの周りを小さな光が回るように

このXでのツイートのようにアイコンの周りをうっすら光るものが動くアニメーションを追加しました。

https://x.com/maotan426/status/1874486835537404046

```page.tsx
<div  className={style.icon}>
    <Image
        src={`/icon.png`}
        alt="icon"
        width={500}
        height={500}
    />
</div>
```

```style.module.scss
.icon{
  border-radius: 50%;
  box-shadow: 0 0 10px 5px theme.$darkpurple;
  position: relative;
  display: inline-block;
  width: 500px;
  height: 500px;
}

@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
.icon::after ,.icon::before{
  content: '';
  position: absolute;
  border-radius: 50%;
  background-image:
          conic-gradient(from var(--angle),     transparent 0%,
                  transparent 65%,
                  theme.$darkpurple 71%,
                  theme.$purple 72%,
                  theme.$darkpurple 73%,
                  transparent 79%,
                  transparent 100%);
  top: 50%;
  left: 50%;
  width: 103%;
  height: 103%;
  translate: -50% -50%;
  display: block;
  z-index: -1;

  animation: border-animation 1.5s linear infinite;
}

.icon::before{
  filter: blur(1.5rem);
}

@keyframes border-animation {
  from{
    --angle: 0deg;
  }
  to{
    --angle: 360deg;
  }
}
```


## どうやっているのか

点の表現はまず下の部分で主に行っています。

↓この部分
```scss
.icon::after ,.icon::before{
  content: '';
  position: absolute;
  border-radius: 50%;
  background-image:
          conic-gradient(from var(--angle),     transparent 0%,
                  transparent 65%,
                  theme.$darkpurple 71%,
                  theme.$purple 72%,
                  theme.$darkpurple 73%,
                  transparent 79%,
                  transparent 100%);
  top: 50%;
  left: 50%;
  width: 103%;
  height: 103%;
  translate: -50% -50%;
  display: block;
  z-index: -1;

  animation: border-animation 1.5s linear infinite;
}
```

擬似要素であるbefore、afterを使って、htmlに記述せず要素を追加します。



これらの要素はアイコンの周りに見える必要があります。
Imageタグが中央にあるのでpositionやtop、leftプロパティを使って中央に持ってきています。
```scss
position: absolute;
top: 50%;
left: 50%;
translate: -50% -50%;
```



border-radiusで完全に円形にします。
```scss
border-radius: 50%;
```

widthとheightで大きさを調節します。
100%だと完全に重なって見えないので少しだけ大きさを調節します。

```scss
width: 103%;
height: 103%;
```

background-imageで背景イメージを設定しています。
```scss
 background-image:
```
そのイメージとしてconic-gradientで作成したイメージを使っています。

conic-gradientは放射状に色を変えていく関数です
```scss
conic-gradient(開始角度、どの位置でどの色か)
```

今回の場合で言うと、まずアニメーション用にカスタムプロパティを設定しています。
```scss
@property --angle {
  syntax: "<angle>"; //角度を扱う
  initial-value: 0deg; //0度から
  inherits: false; //継承しないので指定した要素のみで扱う
}
```

conic-gradient上で先ほどのカスタムプロパティを開始位置として使用します。
そこからどの角度でどのような色をしているかを記述しています。
今回は360度の内71%の位置は濃いめの紫、72%は普通の紫、73%は濃いめの紫とコントラストをつけて光ってる感じをだすようにしています。
```scss
conic-gradient(from var(--angle),     transparent 0%,
        transparent 65%,
        theme.$darkpurple 71%,
        theme.$purple 72%,
        theme.$darkpurple 73%,
        transparent 79%,
        transparent 100%);
```

ここにアニメーションをつけて回転するようにします。
`@keyframes`でcssのアニメーション制御を行うことができます。これを使ってカスタムプロパティを0度から360度変動するようにしています。

```scss
@keyframes border-animation {
  from{
    --angle: 0deg;
  }
  to{
    --angle: 360deg;
  }
}
```

そこからanimationで何秒でそのアニメーションを行うか指定します。
```scss
animation: border-animation 1.5s linear infinite;
```

:::note
### animationの使い方

今回のanimationでは下のように指定しています。
```scss
animation: <名前> <間隔> <アニメーションの緩急> <アニメーション回数>
```
:::

また、blurをつけて少しネオンが光ってる感じも出しています。
```scss
.icon::before{
  filter: blur(1.5rem);
}
```

:::note
### remとは
remはhtmlタグ内で使われている文字サイズを基準とした相対的な文字サイズ指定の単位です。

親の文字サイズに合わせて変更されるので、pxや%よりもレスポンシブ対応させやすいです
:::



# 参考にした動画

https://www.youtube.com/watch?v=ezP4kbOvs_E