1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【React】おいでおいでするGitHubリンクボタンを作成する

Last updated at Posted at 2021-09-13

概要

ホバーすると手招きするGitHubリンクボタンを作成します。

output(video-cutter-js.com) (1).gif

以下のサイトを見ていたときに、いいなと思ってReactに転用してみました。

サイトで紹介しているパッケージも便利なのでおすすめです。

環境

  • React
  • TypeScript

実装

SVG

サイトのHTMLのSVG要素を見ると、手と体でpathが分かれているのがわかります。
これの手の部分をCSSのアニメーションで動かせば良さそうです。

.html
<svg
	width="80"
	height="80"
	viewBox="0 0 250 250"
	style="fill: #fff; color: #151513; position: absolute; top: 0; border: 0; right: 0"
	aria-hidden="true"
>
	<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
	<path
		d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
		fill="currentColor"
		style="transform-origin: 130px 106px"
		class="octo-arm"
	></path>
	<path
		d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
		fill="currentColor"
		class="octo-body"
	></path>
</svg>

アニメーションのタイミング

手が動くタイミングは、マウスカーソルをホバーしたときです。
要素ごとに分解すると下図のようになっていて、白い三角形領域(△)をホバーしたときにアニメーションを実行すれば良さそうです。

スクリーンショット 2021-09-13 173250.png

コード

.tsx
import React, { useEffect, VFC } from 'react';
import { css, keyframes } from '@emotion/css';

export const GitSVG: VFC = () => {
	const hoverHandler = () => {
		const octo_arm = document.getElementById('octo-arm')! as HTMLElement
		octo_arm.classList.add(styles.octoArm)
	}

	useEffect(() => {
		const octo_arm = document.getElementById('octo-arm')! as HTMLElement
		const removeStyles = () => {
			octo_arm.classList.remove(styles.octoArm)
		}
		octo_arm.addEventListener('animationend', removeStyles)
		return () => octo_arm.removeEventListener('animationend', removeStyles)
	}, [])

	return (
		<div>
			<svg
				width="80"
				height="80"
				viewBox="0 0 250 250"
				style={{
					fill: '#fff',
					color: '#151513',
					position: 'absolute',
					top: 0,
					border: 0,
					right: 0
				}}
				aria-hidden="true">
				<g className={styles.group} onMouseEnter={hoverHandler}>
					<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
					<path
						id="octo-arm"
						d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
						fill="currentColor"
						style={{ transformOrigin: '130px 106px' }}></path>
					<path
						d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
						fill="currentColor"></path>
				</g>
			</svg>
		</div>
	)
}

const animations = {
	arm: keyframes`
    0% {
      transform: rotate(0);
    }
    50% {
      transform: rotate(-30deg);
    }
    100% {
      transform: rotate(0deg);
    }
  `
}

const styles = {
	octoArm: css`
		animation: ${animations.arm} 0.3s ease-in 0s 3;
	`,
	group: css`
		cursor: pointer;
	`
}
  • アニメーションは、-30度回転して戻る×3とするようにしています。

  • まず、<g>タグで、三角形領域、タコの手、体をグループ化します。
    これは、三角形領域とタコをホバーしたときだけアニメーションが実行されるようにするためです。

.tsx
<g className={styles.group} onMouseEnter={hoverHandler}>
	<path>{/* 三角形領域 */}</path>
	<path>{/* タコの手 */}</path>
	<path>{/* タコの体 */}</path>
</g>
  • 次に、ホバー時(<g>タグにマウスが入ったとき)にタコの手にアニメーションのスタイルを追加します。
.tsx
	const hoverHandler = () => {
		const octo_arm = document.getElementById('octo-arm')! as HTMLElement
		octo_arm.classList.add(styles.octoArm)
	}
  • ここまでだと、一回目のホバー時にしかアニメーションが実行されないので、アニメーションの実行が終わったらスタイルを削除するようにします。
    そうすることで、次のホバーのタイミングで再度タコの手にアニメーションのスタイルが追加されて、アニメーションが実行されるようになります。
.tsx
    useEffect(() => {
        const octo_arm = document.getElementById('octo-arm')! as HTMLElement
        const removeStyles = () => octo_arm.classList.remove(styles.octoArm)
        octo_arm.addEventListener('animationend', removeStyles)

        return () => octo_arm.removeEventListener('animationend', removeStyles)
    }, [])

まとめ

animationendイベントがキモです。:writing_hand:

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?