Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8647

FNS歌謡祭のHPに実装されているアニメーションを自分も作成したい!!! ハンズオン!!

$
0
0

はじめに

お疲れ様です。
DMM WEBCAMP Advent Calendar 2020の4日目を担当させていただきます。:fist:
メンターの@koseiinfratopです。:basketball_player_tone5:

 みなさん先日OAされたFNS歌謡祭はみましたか?:dancer_tone4:
バンタンや3代目JSBなど豪華アーティストが出演されていて本当におもしろい番組でしたね。なかでもユーミンと嵐のコラボは感動的でした。:guardsman_tone4:
OA当日僕はふと思いました。嵐はいつごろ出番なのだろうかと。:rolling_eyes:

そこでFNS歌謡祭の公式HPを訪れたことで今回のAdvent Calendarで何を書くがが決定しました。


一度公式HPを訪れていただけるとわかるのですが、アーティストの画像にカーソルを乗せてみてください。カーソルを乗せると薄黒いボックスとアーティスト名が出てきます。僕は疑問に思いました。これはどうやって実装しているのかと。。。
ということで今回は疑問に思ったことを解消するためにFNSのHPのようにカーソルを画像に載せるとアニメーションが発火するような機能を実装しようと思います。

↓今回実装したアニメーション
qiita_2.gif


  • 開発効率を上げたかったため今回はホットリロードが可能なReact.jsでコーディングしました。
  • しれっとiTunesAPIも使用しています。


1.html(js)を記述

music.js
 ...*iTunesAPI関係の処理は割愛

return(


<div className="searchresult">
  {artistData.map(artistdata => (
    <ArtistData 
      key={artistdata.CollectionId.toString()}
      id={artistdata.CollectionId}
      name={artistdata.ArtistName}
      album={artistdata.AlbumName}
      albumUrl={artistdata.AlbumUrl}
      genre={artistdata.AlbumGenre}
      release={artistdata.AlbumRelease}               
    />

    )
   )}
</div>
);
artistdata.js
import React from 'react';

const ArtistData = (props) => {

    return(

        <div className={props.id ? 'album': 'noalbum'}>
            <div className="flex">
                <img src={props.albumUrl} alt={props.album} className="albumImage"/>
                <div>
                    <p>ジャンル: <b>{props.genre}</b></p>
                </div>

            </div>
            <div className="mask">
                <div className="caption">{props.album}</div>
            </div>
            <div>
                <p>アーティスト: <b>{props.name}</b></p>
                <p>アルバム名: <b>{props.album}</b></p>
                <p>リリース日: <b>{props.release}</b></p>
            </div>



        </div>
    );
}

export default ArtistData

2.CSSの記述

*ポイントだけコメントアウトを用いて解説します。

music.css
.album {
    width:          300px;
    height:         230px;
  /* overflow・・・アルバムクラスのdivタグ範囲内に内容が収まらない場合(今回で言うとmaskクラスのdivタグがはみ出る)の処理
    overflow:       hidden; /* 表示させないようにしている */

    margin:         10px 8px 10px 16px;
    position:      relative; 
    border:        ridge 10px #87CEFA;
}
.mask {
    width:          100%;
    height:         100%;
    position:       absolute;
    top:            -100%; /* 枠の上に配置し非表示にする。 */
    opacity:            0;  /* マスクスクラス内を透明化(0)にすることで非表示にする。*/

    background-color:   rgba(0,0,0,0.4);
    transition:         all 0.6s ease; 
}  

.caption {
    font-size:      130%;
    text-align:    center;
    color:          #fff;
}

img.alabumImage {
    width: 80%;
    height: 80%;
    position: absolute;
}

.album:hover .mask {  アルバムクラス内をhover(カーソルを乗せる)時に発火する。
    opacity:        1;  /* マスクを完全に不透明表示にする */
    padding-top:        80px;   /* ホバーで下にずらす */
    top: 0; /* 先ほどのtop: -100% から top: 0;にすることにより下から降りてくるように見せることができる */
}

完成!!!


デモンストレーション


qiita_article.gif

詳細な動きform内にアーティスト(曲名も可)を入力し検索ボタンをクリックすることでitunesapiからアーティストが発売したアルバムを取得し、表示しています。

今回のmusic.js, music.cssの全コード(※artistdata.jsは上記のコードが全コードです。)

music.js
import React, {useState } from 'react';
import axios from 'axios';
import ArtistData from './artistdata.js';
import '../styles/music.css'
const Music = () => {
    const [artist, setArtist] = useState('');
    const [artistData, setArtistData] = useState([{
        CollectionId: '',
        ArtistName: '',
        AlbumName: '',
        AlbumUrl: '',
        AlbumGenre: '',
        AlbumRelease: '',
    }]);

    async function itunesGet(params){
        try{
            const prm = params.trim();
            const response = await axios.get(`https://itunes.apple.com/search?term=${prm}&entity=album`)
            const responsedata = response.data.results
            const responseAPI = responsedata.map(value => {
                return {
                    CollectionId: value.collectionId, 
                    ArtistName: value.artistName,
                    AlbumName: value.collectionName, 
                    AlbumUrl: value.artworkUrl100,
                    AlbumGenre: value.primaryGenreName,
                    AlbumRelease: value.releaseDate,

                };
            }
            );
            setArtistData(responseAPI);
            console.log(artistData);
        }catch(error) {
            const {
                status, statusText
            } = error.response;
            console.log(`Error! HTTP Status: ${status} ${statusText}`)

        }

    };

    return (
        <div>
            <form
                onSubmit = {e => {
                    e.preventDefault();
                    const artistnameElement = e.target.elements["artist"];
                    console.log(artistnameElement.value);

                    itunesGet(artistnameElement.value);

                    setArtist(artistnameElement.value);

                    artistnameElement.value = '';

                }}
            >
                <input type="text" id="artist"
                    placeholder="アーティスト名または曲名を入力してください"
                />
                <button type="submit">検索する</button>


            </form>
            <p className="result">検索結果: <b>{artist}</b></p>
            <div className="searchresult">
            {artistData.map(artistdata => (
                <ArtistData 
                    key={artistdata.CollectionId.toString()}
                    id={artistdata.CollectionId}
                    name={artistdata.ArtistName}
                    album={artistdata.AlbumName}
                    albumUrl={artistdata.AlbumUrl}
                    genre={artistdata.AlbumGenre}
                    release={artistdata.AlbumRelease}

                />

            )
            )}
            </div>

        </div>
    )
}

export default Music
music.css
* {
    margin: 0 auto;
    padding: 0;
    box-sizing: border-box;
}


form > :first-child  {
    outline: none;
    border: 1px solid #aaa;
    transition: all .3s;
    border-radius: 2px;
  }
form > :first-child {
    width: 400px;
    font-size: 18px;
    height: 24px;
    padding: 2px 8px;
  }
form > :nth-child(1):focus {
    box-shadow: 0 0 7px #1abc9c;
    border: 1px solid #1abc9c;
}

form > :last-child {
    margin-top: 4px;
    margin-left: 7px;
    font-size: 16px;
    height: 40px;
    padding: 2px 8px;
}
form button {
    border: 1px solid #ccc;
    background-color: #FFFFFF;

    border-radius: 2px;
    cursor: pointer;
    box-shadow: 0px 2px 2px 0px rgba(0,0,0,.1);
}
form button:hover {
    box-shadow: 0px 2px 2px 2px rgba(0, 0, 0, .1);

}
.result {
    text-align: center;
}
.searchresult {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
}
.noalbum{
    display: none;
}
.flex {
    padding: 3px 1px;
    display: flex;
    flex-direction: row;
}
.flex > :nth-child(2) {
    padding-top: 1.7em;
    padding-bottom: 0.5em;


}
.flex > :nth-child(2):nth-child(2){
    font-family: 'Courier New', Courier, monospace;
}

.album {
    width:          300px;
    height:         230px;
    overflow:       hidden; 
    margin:         10px 8px 10px 16px;
    position: relative;
    border: ridge 10px #87CEFA;
}
.mask {
    width:          100%;
    height:         100%;
    position:       absolute;
    top:            -100%;
    opacity:            0; /* マスクにする */

    background-color:   rgba(0,0,0,0.4);
    transition:     all 0.6s ease;
}  

.caption {
    font-size:      130%;
    text-align:         center;
    color:          #fff;
}

img.alabumImage {
    width: 80%;
    height: 80%;
    position: absolute;
}

.album:hover .mask {
    opacity:        1;  /* マスクを完全に不透明表示する */
    padding-top:        80px;   /* ホバーで下にずらす */
    top: 0;
}

まとめ

- 「このサイト、アプリはどのように実装されているのだろうか?」と疑問を持ち実際に自分で実装することで自分の技術力UPに繋がるということを改めて知ることができたそんな師走上旬でした。。。

裏話FNS公式HPのデベロッパーツールのSourceパネルと睨めっこしながら実装したのは内緒。。。



参考資料

2020 FNS歌謡祭 - フジテレビ
itunes api


Viewing all articles
Browse latest Browse all 8647

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>