Happy Merry Christmas!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43894/799dbfe2-1cc9-8c05-f100-989714ee4ad5.png)

JavaScriptを使ってアニメーションを作りました。
CanvasとかSVGとかでも良かったのですが、あえてここはDIVだけで作ってみました。
激重です！！

**デモページ**
[Happy Merry Christmas!](https://hiron712.github.io/xmas2019/)

# ソース

[ソースをダウンロードする](https://github.com/hiron712/xmas2019)

## HTML

外部JSを読み込んでいるだけです。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Happy Merry Christmas!</title>
    <script src="script.js"></script>
</head>
<body> 
</body>
</html>
```

## JavScript

基本関数はスルーします。
- 「ready」はjQueryでいうところの「$(window).ready();」です。
- ループ用の「requestAnimationFrame」
- マウス座標取得「getpos」

関数「snow」に一連の動きを記述していきます。

```JavaScript
function snow(){
    this.init();
}
```

HTML読み込み完了後「new snow()」する

```JavaScript
ready(function(){
    new snow();
});
```

「prototype」で「snow」関数を拡張していきます。

### init
初期設定、リサイズイベント、マウスイベント、ループアニメションをしています。

```JavaScript
init:function(){
    var me = this;
    me.$body = document.getElementsByTagName('body')[0];

    me.q = 100;
    me.h = 600;

    me.mouseX = 0;
    me.mouseY = 0;

    me.px = me.mouseX;
    me.py = me.mouseY;

    window.addEventListener('resize',function(){
        me.onresize();
    });

    me.setBody();
    me.setFont();
    me.$xmas = me.setText();

    var loop = function(){
        for(var i = 0; i < me.snows.length; i++){

            me.snows[i].z += me.snows[i].az;

            me.px += (me.mouseX - me.px)/200;
            me.py += (me.mouseY - me.py)/200;

            var dat = me.threeToTwo(me.snows[i].x + me.px,me.snows[i].y + me.py,me.snows[i].z);

            if(me.snows[i].z > me.h){
                me.snows[i].z = me.h * -1;
                me.snows[i].reset();
            }

            if(me.snows[i].z < 0){
                var alpha = Math.abs(me.snows[i].z)/me.h;
                me.snows[i].style.opacity = 1 - alpha;
            }else{
                me.snows[i].style.opacity = 1;
            }
    
            me.snows[i].setPos(dat.x,dat.y,dat.scale);
    
        }

        for(var i = 0; i < me.stars.length; i++){
            me.stars[i].reset();
        }

        requestAnimationFrame(loop);
    }

    me.$body.addEventListener('mousemove',function(e){
        var pos = getpos(e);
        me.mouseX = (me.midx - pos.x)/10;
        me.mouseY = (me.midy - pos.y)/10;
    });
    
    setTimeout(function(){
        me.onresize();
        setTimeout(function(){
            me.setStar();
            me.setSnow();
            requestAnimationFrame(loop);
            me.$body.style.opacity = 1;
        },100);
    },100);
},
```
### setText

「Happy Merry Christmas!」のテキストを生成しています。

```JavaScript
setText:function(){
    var me = this;
    var $text = document.createElement('div');
    $text.style.color = '#ffff00';
    $text.style.fontSize = '60px';
    $text.style.fontFamily = "'Playball', cursive";
    $text.style.position = 'absolute';
    $text.style.left = '50%'; 
    $text.style.top = '50%'; 
    $text.style.transform = 'translate(-50%,-50%)';

    $text.style.background = '-webkit-linear-gradient(0deg, #40E0D0, #FF8C00, #FF0080)';
    $text.style.webkitBackgroundClip = 'text';
    $text.style.webkitTextFillColor = 'transparent';

    var newtext = document.createTextNode('Happy Merry Christmas!');
    $text.appendChild(newtext);
    me.$body.appendChild($text);

    return $text;
},
```
### setSnow

雪を「me.q」個生成して配列「me.snows」に入れています。
生成部分は「me.createSnow()」

```JavaScript
setSnow:function(){
    var me = this;
    me.snows = [];
    for(var i = 0; i < me.q; i++){
        var $snow = me.createSnow();
        me.snows.push($snow);
        me.$body.appendChild($snow);
    }
},
```
### setStar
星を「me.q」個生成して配列「me.stars」に入れています。
生成部分は「me.createStar()」

```JavaScript
setStar:function(){
    var me = this;
    me.stars = [];
    for(var i = 0; i < me.q; i++){
        var $star = me.createStar();
        me.stars.push($star);
        me.$body.appendChild($star);
    }
},
```

### createSnow

雪の生成と雪を動かす関数を設定しています。
「div.left」「div.top」「¥div.scale」はあまりにも重くて「transform」に変更してので使用しないですね・・・

```JavaScript
createSnow:function(){
    var me = this;
    var $div = document.createElement('div');

    $div.reset = function(){
        var size = Math.random() * 35 + 5;

        $div.style.width = size + 'px';
        $div.style.height = size + 'px';
        $div.style.borderRadius = '50%';
        $div.style.position = 'absolute';

        $div.x = Math.random() * 800 - 400;
        $div.y = Math.random() * 800 - 400;

        $div.az = Math.random() * 22 + 3;    
    };

    $div.z = Math.random() * 1200 - 600;
    $div.style.backgroundColor = '#ffffff';
    $div.style.filter = 'blur(4px)';

    $div.setPos = function(x,y,scale){
        this.style.transform = 'translate(' + x + 'px,' + y +'px) scale('+ scale +')';
    };

    $div.left = function(num){
        this.style.left = num + 'px';
    };
    $div.top = function(num){
        this.style.top = num + 'px';
    };

    $div.scale = function(num){
        this.style.transform = 'scale('+ num +')';
    };

    $div.reset();

    return $div;
},
```
### createStar
星の生成と星を動かす関数を設定しています。

```JavaScript
createStar:function(){
    var me = this;
    var $div = document.createElement('div');
    $div.style.width = '3px';
    $div.style.height = '3px';
    $div.style.position = 'absolute';
    $div.style.left = '0px';
    $div.style.top = '0px';
    $div.style.zIndex = -1;

    $div.reset = function(){
        $div.style.backgroundColor = 'rgb(' + Math.round(Math.random()*255) +',' + Math.round(Math.random()*255)+',' + Math.round(Math.random()*255) +')';
    };
    var x = Math.random() * window.innerWidth;
    var y = Math.random() * window.innerHeight;
    $div.style.borderRadius = '50%';
    $div.style.transform = 'translate('+ x +'px,'+ y +'px)';    

    $div.reset();

    return $div;
},
```

### threeToTwo

3次元座標を2次元座標に変換しています。

```JavaScript
threeToTwo:function(xpoz,ypoz,zpoz){
    var me = this;

    _x = (me.h * xpoz)/(me.h - zpoz) + me.midx;
    xx = (me.h * (xpoz+100))/(me.h - zpoz) + me.midx;
    _y = (me.h * ypoz)/(me.h - zpoz) + me.midy;

    sl = xx-_x;

    return {
        x : _x,
        y : _y,
        scale : sl/100,
    }
},
```
### setBody
背景の設定とフェードインの準備をしています。

```JavaScript
setBody:function(){
    var me = this;

    me.$body.style.backgroundColor = '#001122';
    me.$body.style.margin = 0;
    me.$body.style.padding = 0;
    me.$body.style.position = 'absolute';
    me.$body.style.top = 0;
    me.$body.style.bottom = 0;
    me.$body.style.left = 0;
    me.$body.style.right = 0;
    me.$body.style.overflow = 'hidden';
    me.$body.style.transition = 'all 1500ms 0s ease';
    me.$body.style.opacity = 0;
},
```

### setFont

GoogleのWebフォントの読み込み

```JavaScript
setFont:function(){
    var me = this;
    var $link = document.createElement('link');
    $link.setAttribute('href','https://fonts.googleapis.com/css?family=Playball&display=swap');
    $link.setAttribute('rel','stylesheet');
    me.$body.appendChild($link);
},
```

### onresize
リサイズされて時にブラウザの中心座標をもとめる

```JavaScript
onresize:function(){
    var me = this;
    me.midx = window.innerWidth/2;
    me.midy = window.innerHeight/2;
}
```

# 解説
簡単に言うと3次元座標で動かした雪を2次元座標に変換して配置しています。
Flash時代によく使っていた、3次元座標ー＞2次元座標をJavaScriptで使ってみたかったというだけの記事でした。
ネイティブで書いたので、スタイルの設定はめんどくさかったです。

# 最後に
ふあふあな雪に大の字に寝て星空を見ていると癒やされますよね。。。
そんな感じのアニメーションです。

みなさんはどんなクリスマスを過ごすのでしょうか？
また、今年一年はどんな年でしたか？
来年もみなさんにとって良い年でありますように。。。

Happy Merry Christmas! and Happy New Year!