CSSとJSでロマンチックな流星群アニメーションを実現

CSSとJSでロマンチックな流星群アニメーションを実現

1. レンダリング

2. ソースコード

html

< 本文 > 
    < div クラス = "コンテナ" > 
        < div id = "マスク" > </ div > 
        < div id = "空" > </ div > 
        < div id = "月" > </ div > 
        < div id = "星" > </ div > 
        < div クラス = "クラウド クラウド-1" ></ div > 
        <div クラス = "クラウド クラウド-2" > </div > 
        < div クラス = "クラウド クラウド-3" > </ div > 
    </div> 
</本文>

CS

/* - - - - - - 再起動 - - - - - - */
 
 * {
     マージン: 0 ;
     パディング: 0;
 }
 
 html,
  体 {
      幅: 100%;
     最小幅: 1000px;
     高さ: 100%;
     最小の高さ: 400px;
     オーバーフロー:非表示;
 }

 /* -  -  -  -  -  - キャンバス -  -  -  -  -  - */ 
 。容器 {
      位置:相対的;
     高さ: 100%;
 }
 /* マスクレイヤー */
 
 #マスク {
      位置:絶対;
     幅: 100%;
     高さ: 100%;
     背景: rgba(0,0,0,.8);
     zインデックス: 900;
 }
 /*空の背景*/
 
 #空 {
      幅: 100%;
     高さ: 100%;
     背景: 線形グラデーション(rgba(0,150,255,1),rgba(0,150,255,.8),rgba(0,150,255,.5));
 }
 / *月* /
 
 #月 {
      位置:絶対;
     上: 50px;
     右: 200px;
     幅: 120px;
     高さ: 120px;
     背景: rgba(251,255,25,0.938);
     境界線の半径: 50%;
     ボックスシャドウ: 0 0 20px rgba(251, 255, 25, 0.5);
     zインデックス: 9999;
 }
 /* きらきら星 */
 
 .点滅{
      位置:絶対;
     背景: rgb(255,255,255);
     境界線の半径: 50%;
     ボックスシャドウ: 0 0 5px rgb(255,255,255);
     不透明度: 0;
     zインデックス: 10000;
 }
 /* 流れ星 */
 
 。星 {
      位置:絶対;
     不透明度: 0;
     zインデックス: 10000;
 }
 
 .star :: 後 {
      コンテンツ: "";
     表示:ブロック;
     境界:実線;
     境界線の幅: 2px 0 2px 80px ;
     /*流星の長さは徐々に短くなります*/ 
     境界線の色: 透明 透明 透明 rgba(255,255,255,1);
     境界線の半径: 2px 0 0 2px ;
     変換: 回転(-45度);
     変換元: 0 0 0 ;
     ボックスの影: 0 0 20px rgba(255,255,255,.3);
 }
 / *雲* /
 
 。雲 {
      位置:絶対;
     幅: 100%;
     高さ: 100px;
 }
 
 .cloud-1 {
      下: -100px;
     zインデックス: 1000;
     不透明度: 1;
     変換:スケール(1.5);
     -webkit-transform: スケール(1.5);
     -moz-transform: スケール(1.5);
     -ms-transform:スケール(1.5);
     -o-変換:スケール(1.5);
 }
 
 .cloud-2 {
      左: -100px;
     下: -50px;
     zインデックス: 999;
     不透明度: . 5 ;
     変換:回転(7度);
     -webkit-transform: 回転(7度)。
     -moz-transform: 回転(7度);
     -ms-transform:回転(7度);
     -o-transform:回転(7度);
 }
 
 .cloud-3 {
      左: 120px;
     下: -50px;
     zインデックス: 999;
     不透明度: . 1 ;
     変換: 回転(-10度);
     -webkit-transform: 回転(-10度);
     -moz-transform: 回転(-10度);
     -ms-transform:回転(-10度);
     -o-transform:回転(-10度);
 }
 
 。丸 {
      位置:絶対;
     境界線の半径: 50%;
     背景: #fff;
 }
 
 .circle-1 {
      幅: 100ピクセル;
     高さ: 100px;
     上: -50px;
     左: 10px;
 }
 
 .circle-2 {
      幅: 150ピクセル;
     高さ: 150px;
     上: -50px;
     左: 30px;
 }
 
 .circle-3 {
      幅: 300ピクセル;
     高さ: 300ピクセル;
     上: -100px;
     左: 80px;
 }
 
 .circle-4 {
      幅: 200ピクセル;
     高さ: 200px;
     上: -60px;
     左: 300px;
 }
 
 .circle-5 {
      幅: 80ピクセル;
     高さ: 80px;
     上: -30px;
     左: 450px;
 }
 
 .circle-6 {
      幅: 200ピクセル;
     高さ: 200px;
     上: -50px;
     左: 500px;
 }
 
 .circle-7 {
      幅: 100ピクセル;
     高さ: 100px;
     上: -10px;
     左: 650px;
 }
 
 .circle-8 {
      幅: 50px;
     高さ: 50px;
     上: 30px;
     左: 730px;
 }
 
 .circle-9 {
      幅: 100ピクセル;
     高さ: 100px;
     上: 30px;
     左: 750px;
 }
 
 .circle-10 {
      幅: 150ピクセル;
     高さ: 150px;
     上: 10px;
     左: 800px;
 }
 
 .circle-11 {
      幅: 150ピクセル;
     高さ: 150px;
     上: -30px;
     左: 850px;
 }
 
 .circle-12 {
      幅: 250ピクセル;
     高さ: 250px;
     上: -50px;
     左: 900px;
 }
 
 .circle-13 {
      幅: 200ピクセル;
     高さ: 200px;
     上: -40px;
     左: 1000px;
 }
 
 .circle-14 {
      幅: 300ピクセル;
     高さ: 300ピクセル;
     上: -70px;
     左: 1100px;
 }

JS

// 流星アニメーション setInterval(function() {
     const obj = addChild("#sky","div",2,"star");

    (i = 0 とします; i < obj.children.length; i++) {
         定数 top = -50 + Math.random() * 200 + "px",
            左 = 200 + Math.random() * 1200 + "px"、
            スケール = 0.3 + Math.random() * 0.5;
        定数タイマー = 1000 + Math.random()*1000;

        obj.children[i].style.top = top;
        obj.children[i].style.left = left;
        obj.children[i].style.transform = `scale(${scale})`;

        リクエストアニメーション({
            要素: obj.children[i],
             属性: ["上", "左", "不透明度"],
             値: [150, -150, .8],
             時間:タイマー,
             フラグ: false、
             関数: 関数() {
                リクエストアニメーション({
                    要素: obj.children[I],
                     attr:["上","左","不透明"],
                     値: [150, -150, 0],
                     時間: タイマー、
                     フラグ: false、
                     関数:() => {
                        obj.parent.removeChild(obj.children[i]);
                    }
                })
            }
        });
    }

}, 1000);

// きらめく星のアニメーション setInterval(function() {
     const obj = addChild("#stars","div",2,"点滅");

    (i = 0 とします; i < obj.children.length; i++) {
         定数 top = -50 + Math.random() * 500 + "px",
            左 = 200 + Math.random() * 1200 + "px"、
            ラウンド = 1 + Math.random()*2 + "px";
        定数タイマー = 1000 + Math.random() * 4000;

        obj.children[i].style.top = top;
        obj.children[i].style.left = left;
        obj.children[i].style.width = 丸い;
        obj.children[i].style.height = 丸い;

        リクエストアニメーション({
            要素: obj.children[i],
             属性: "不透明度"、
             値: .5,
             時間:タイマー,
             フラグ: false、
             関数: 関数() {
                リクエストアニメーション({
                    要素: obj.children[i],
                     属性: "不透明度"、
                     値: 0,
                     時間:タイマー,
                     フラグ: false、
                     関数: 関数() {
                        obj.parent.removeChild(obj.children[i]);
                    }
                });
            }
        });
    }

}, 1000);

//月の動きrequestAnimation({
    要素: "#moon",
     属性: "右",
     値: 1200、
     時間: 10000000、
});


// 雲を追加 const clouds = addChild(".cloud","div",14,"circle",true);
(i = 0 とします; i < clouds.children.length; i++) {
     (j = 0; j < clouds.children[i].length; とします) {
        clouds.children[i][j].classList.add(`circle-${++j}`);
    }
}
//クラウドアニメーションlet flag = 1;
間隔を設定する()
    関数() {
         定数clouds = document.querySelectorAll(".cloud");
        左 = Math.random()*5;
        下部 = Math.random()*5;

        タイマーを 0 にします。
        (i = 0 とします; i < clouds.length; i++) {
            リクエストアニメーション({
                ele:雲 [i],
                 属性: ["左", "下"],
                 値:フラグ%2? [-左,-下]: [左,下],
                 時間:タイマー += 500,
                 フラグ: false、
                 関数: 関数() {
                    リクエストアニメーション({
                        ele:雲 [i],
                         属性: ["左", "下"],
                         値:フラグ%2? [左、下]: [-左、-下],
                         時間:タイマー,
                         フラグ: false
                    })
                }
            });
        }

        フラグ++;
    }, 2000)

包装方法

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - アニメーション -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - - 
//モーションアニメーション、Tween.js を呼び出す 
//ele: dom | class | id | tag node | class name | id name | tag name、1 つのノードの選択のみをサポートし、クラス名とタグ名はページの最初のノードのみを選択できます //attr: attribute 属性名 //value: target value ターゲット値 //time:duration 期間 //tween: タイミング関数関数式 //flag: ブール値は値で移動するか位置で移動するかを決定します。デフォルトでは位置で移動します //fn: callback コールバック関数 //add 戻り値: 内部パラメーター オブジェクトを返します。戻りオブジェクトのプロパティ stop を true に設定することで、アニメーション関数を中断できます requestAnimation(obj) {
     // -------------------------------------パラメータ設定------------------------------------------- 
    //デフォルトプロパティconstパラメータ={
         要素: null、
         属性: null、
         値: null、
         時間: 1000,
         トゥイーン: "リニア"、
         フラグ: true、
         停止:偽、
         引数: ""
    }

    // 入力プロパティをマージする Object .assign (parameter, obj); // 重複するプロパティを上書きする // ------------------------------------- アニメーション設定 --------- ------------------------------------ 
    // 再利用しやすいようにモーション方程式の初期パラメータを作成します。let start = 0 ; // 初期タイムスタンプの保存に使用します。let target = (typeof parameter.ele === "string"? document .querySelector(parameter.ele):parameter.ele), // ターゲット ノード attr = parameter.attr, // ターゲット属性 beginAttr = parseFloat(getComputedStyle(target)[attr]), // Attr 開始値 value = parameter.value, // モーション ターゲット値 count = value - beginAttr, // 実際のモーション値 time = parameter.time, // モーション期間,
        tween = パラメータ.tween、//モーション関数フラグ = パラメータ.flag、
        callback = param.fn, // コールバック関数 curVal = 0; // モーションの現在の値 // 渡された関数が配列であるかどうかを判断、マルチセグメントモーション (function () {
         if (attr インスタンスの配列) {
            開始属性 = [];
            カウント = [];
            for (私の属性を取得) {
                 const val = parseFloat(getComputedStyle(target)[i]);
                beginAttr.push(val);
                count.push(値 - val);
            }
        }
        if (配列の値インスタンス) {
             for (let i in value) {
                count[i] = 値[i] - beginAttr[i];
            }
        }
    })();

    //モーション関数アニメーション(タイムスタンプ){
         if (parameter.stop) return; //break //初期タイムスタンプを保存 if (!start) start = timestamp;
        // 経過時間 let t = timestamp - start;
        // 複数セグメントの動きを判断する if (beginAttr instanceof Array) {
             // const len ​​= beginAttr.length //配列の長さを保存し、再利用します //マルチセグメントモーションタイプ1 - 複数の属性、同じターゲット、同時/異なる時間 if (typeof count === "number") { //同じターゲット//同時 if (typeof time === "number") {
                     if (t> time) t = time; //目標値を超えているか判定 // attrをループし、それぞれに値を割り当てる (let i in beginAttr) {
                         if (flag) curVal = Tween [tween] (t, beginAttr [i], count, time); //Tweenを呼び出して現在の属性値を返します。計算方法は書き込み位置へ移動することです else curVal = Tween [tween] (t, beginAttr [i], count + beginAttr [i], time); //Tweenを呼び出して現在の属性値を返します。計算方法は書き込み距離を移動することです if (attr [i] === "opacity") target.style [attr [i]] = curVal; //属性に値を割り当てます else target.style [attr [i]] = curVal + "px" ; //属性に値を割り当てます if (t < time) requestAnimationFrame (animate); //移動が完了したかどうかを判断します other callbacks && callback(); //コールバック関数を呼び出します }
                    戻る;
                }

                //異なる時間 if (time instanceof Array) {
                     //ループ時間、属性、それぞれに割り当てられます(beginAttrで説明します){
                         //エラー判定 if (!time[i] && time[i] !== 0) {
                             新しいエラーをスローします(
                                 「入力時間の長さがプロパティの長さと等しくありません」);
                        }

                        //アニメーションが完了したかどうかを判断します。完了している場合は、このループをスキップします。if (parseFloat (getComputedStyle (target) [attr [i]]) === (typeof value === "number"? value: value [i]))
                             続く;
                        // t = timestamp - start; // ループごとに t を初期化します 
                        if (t > time [i]) t = time [i]; // ターゲット値を超えているか判定 if (flag || attr [i] === "opacity") curVal = Tween [tween] (t, beginAttr [i], count, i); // Tween を呼び出して現在の属性値を返します。このときの計算方法は書き込み位置への移動です else curVal = Tween [tween] (t, beginAttr [i], count + beginAttr [i], i); // Tween を呼び出して現在の属性値を返します。このときの計算方法は書き込み距離の移動です if (attr [i] === "opacity") target.style [attr [i]] = curVal; // 属性に値を代入します else target.style [attr [i]] = curVal + "px" ; // 属性に値を代入します }

                    if (t < Math.max(...time)) requestAnimationFrame(animate); //関数が他のコールバックを終了したかどうかを判断します && callback(); //最長のアニメーションが実行された場合は、コールバック関数を調査します return;
                }
            }

            //マルチセグメントモーションタイプ2 - 複数の属性、異なるターゲット、同時/異なる時間 if (count instanceof Array) {
                 //同じ時間if (typeof time === "number") {

                    if (t> time) t = time; //目標値を超えているか判定 for (let i in beginAttr) { //ループ attr、count、値を別々に代入 //エラー判定 if (! count [i] && count [i] !== 0) {
                             新しいエラーをスローします(
                                 「入力値の長さがプロパティの長さと等しくありません」);
                        }

                        if (flag || attr[i] === "opacity") curVal = Tween[tween](t, beginAttr[i], count[i], time); //Tweenを呼び出して現在の属性値を返します。計算方法は、書き込み位置に移動することです。 else curVal = Tween[tween](t, beginAttr[i], count[i] + beginAttr[i], time); //Tweenを呼び出して現在の属性値を返します。計算方法は、書き込み距離を移動することです。 if (attr[i] === "opacity") target.style[attr[i]] = curVal; //属性に値を割り当てます。 else target.style[attr[i]] = curVal + "px" ; //属性に値を割り当てます。 }

                    if (t < time) requestAnimationFrame (animate); //関数が他のコールバックを終了したかどうかを判断します && callback(); //最長のアニメーションが実行された場合は、コールバック関数を調べます return;
                }

                //異なる時間 if (time instanceof Array) {
                     (let i in beginAttr) {
                         //エラー判定 if (!time[i] && time[i] !== 0) {
                             新しいエラーをスローします(
                                 「入力時間の長さがプロパティの長さと等しくありません」);
                        }

                        //アニメーションが完了したかどうかを判断します。完了している場合は、このループをスキップします。if (parseFloat (getComputedStyle (target) [attr [i]]) === (typeof value === "number"? value: value [i]))
                             続く;

                        if (t> time[i]) t = time[i]; //目標値を超えたかどうかを判定 //エラー判定 if (! count[i] && count[i] !== 0) {
                             新しいエラーをスローします(
                                 「入力値の長さがプロパティの長さと等しくありません」);
                        }

                        if (flag || attr[i] === "opacity") curVal = Tween[tween](t, beginAttr[i], count[i], time[i]); //Tweenを呼び出して現在の属性値を返します。計算方法は、書き込まれた位置に移動することです。 else curVal = Tween[tween](t, beginAttr[i], count[i] + beginAttr[i], time[i]); //Tweenを呼び出して現在の属性値を返します。計算方法は、書き込まれた距離を移動することです。 if (attr[i] === "opacity") target.style[attr[i]] = curVal;
                        それ以外の場合、target.style[attr[i]] = curVal + "px";
                    }

                    t < Math.max(... 時間) の場合、 requestAnimationFrame(animate);
                    そうでない場合はコールバック && コールバック();
                    戻る;
                }
            }

        }

        //シングルモーションモード if (t> time) t = time;
        if (flag || attr === "opacity") curVal = Tween [tween] (t, beginAttr, count, time); //Tween を呼び出して現在の属性値を返します。計算方法は、書き込み位置に移動することです。 else curVal = Tween [tween] (t, beginAttr [i], count + beginAttr, time); //Tween を呼び出して現在の属性値を返します。計算方法は、書き込み距離を移動することです。 if (attr === "opacity") target.style [attr] = curVal;
        それ以外の場合、target.style[attr] = curVal + "px";

        t < 時間の場合、 requestAnimationFrame(animate);
        そうでない場合はコールバック && コールバック();

    }

    アニメーションフレームをリクエストします。
    戻りパラメータ; // 中断やその他の目的のためにオブジェクトを返す}
//Tween.js 
/ *
 * t: 経過時間 * b: 開始値 * c: 総移動値 * d: 継続時間 *
 *曲線の方程式*
 * http://www.cnblogs.com/bluedream2009/archive/2010/06/19/1760909.html
 * * /

トゥイーン = {
     linear: function (t, b, c, d) { // 等速は c * t / d + b を返します。
    },
    easyIn: function (t, b, c, d) { // 加速曲線 return c * (t /= d) * t + b;
    },
    easyOut: function (t, b, c, d) { // 減速曲線 return -c * (t /= d) * (t - 2) + b;
    },
    easyBoth: function (t, b, c, d) { // 加速と減速の曲線 if ((t / = d / 2) < 1) {
             c / 2 * t * t + b を返します。
        }
        -c/2*((-t)*(t-2)-1)+b を返します。
    },
    easyInStrong: function (t, b, c, d) { // 加速曲線 return c * (t /= d) * t * t * t + b;
    },
    easyOutStrong: function (t, b, c, d) { // easyOut 曲線 return -c * ((t = t / d - 1) * t * t * t - 1) + b;
    },
    easyBothStrong: function (t, b, c, d) { //加速と減速のライン if ((t / = d / 2) < 1) {
             c / 2 * t * t * t * t + b を返します。
        }
        -c/2*((t-=2)*t*t*t-2)+b を返します。
    },
    elasticIn: function (t, b, c, d, a, p) { // 正弦減衰曲線(徐々に弾性が増す)
        t === 0 の場合
             b を返します。
        }
        ((t /= d) == 1) の場合 {
             b + c を返します。
        }
        もし(!p){
            0.3 ;
        }
        もし (!a || a < Math.abs(c)) {
            a = c;
            var s = p / 4;
        } それ以外 {
             var s = p / (2 * Math.PI) * Math.asin(c/a);
        }
        戻り値 - (A * math.pow(2, 10 * (T - = 1)) * math.sin((T * d - S) * (2 * math.PI) / P)) + B;
    },
    elasticOut: function (t, b, c, d, a, p) { // 正弦波強調曲線(弾性出力)
        t === 0 の場合
             b を返します。
        }
        ((t /= d) == 1) の場合 {
             b + c を返します。
        }
        もし(!p){
            0.3 から 0.4 へ
        }
        もし (!a || a < Math.abs(c)) {
            a = c;
            var s = p / 4;
        } それ以外 {
             var s = p / (2 * Math.PI) * Math.asin(c/a);
        }
        a * Math.pow(2, -10*t) * Math.sin((t*d - s) * (2*Math.PI) / p) + c + b を返します。
    },
    elasticBoth: 関数(t, b, c, d, a, p) {
         t === 0 の場合
             b を返します。
        }
        ((t/=d/2) == 2) の場合 {
             b + c を返します。
        }
        もし(!p){
            p = d * (0.3 * 1.5);
        }
        もし (!a || a < Math.abs(c)) {
            a = c;
            var s = p / 4;
        } それ以外 {
             var s = p / (2 * Math.PI) * Math.asin(c/a);
        }
        (T < 1) の場合 {
             -0.5 * (A * math.pow(2, 10 * (T - = 1)) * を返します。
                 数学.sin((T * d - S) * (2 * math.PI) / p)) + b;
        }
        * Math.pow(2, -10 * (t - = 1)) * を返します。
             Math.sin((t*d - s)*(2*Math.PI)/p)*0.5+c+b;
    },
    backIn: function (t, b, c, d, s) { // 後方加速(後方段階的進入)
        (型s == 'undefined')の場合{
            s = 1.70158 ;
        }
        c*(t/=d)*t*((s+1)*t-s)+b を返します。
    },
    バックアウト: 関数 (t, b, c, d, s) {
         (型s == 'undefined')の場合{
            s = 3.70158; // 引き込み距離}
        c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b を返します。
    },
    戻る両方: 関数(t, b, c, d, s) {
         (型s == 'undefined')の場合{
            s = 1.70158 ;
        }
        ((t/=d/2) < 1) の場合 {
             c/2*(t*t*(((s*=(1.525))+1)*t-s))+b を返します。
        }
        c/2*((t-=2)*t*(((s*=(1.525))+1)*t+s)+2)+b を返します。
    },
    bounceIn: function (t, b, c, d) { // ボールは徐々に消えていきます)
        c - Tween['bounceOut'](d - t, 0, c, d) + b を返します。
    },
    バウンスアウト: 関数(t, b, c, d) {
         ((t/=d) < (1/2.75)) の場合
             c * (7.5625 * t * t) + b を返します。
        } そうでなければ (t < (2/2.75)) {
             c*(7.5625*(t - =(1.5/2.75))*t+0.75)+b を返します。
        } そうでなければ (t < (2.5 / 2.75)) {
             c*(7.5625*(t - =(2.25/2.75))*t+0.9375)+b を返します。
        }
        c*(7.5625*(t - =(2.625/2.75))*t+0.984375)+b を返します。
    },
    バウンスBoth: 関数(T, B, C, D) {
         (T < d / 2) の場合
             tween['bounceIn'](T*2, 0, C, D)*0.5 + B を返します。
        }
        Tween['bounceOut'](t*2-d,0,c,d)*0.5+c*0.5+b を返します。
    }
}


// ------------------------------------------- DOM 操作 --- ------------------------------------------------ 
//ノードを追加//ele: 親ノード。入力変数、ID 値、クラス値、ラベル値をサポートします。変数を除き、他の値は文字列である必要があります //Node: 追加されたラベル名、値は文字列です //n: 追加されたノードの数 //className: ノードにバインドされたクラス名、値は文字列です、複数のクラス名はスペースで区切られます //Boolean: 対象の親ノードをすべて選択するかどうか。オプションパラメータ。入力しない場合は false と判断され、最初に選択されたノードのみが一致します。Function addChild (ele, node, n, className, boolean) {
     //ノードを取得します。let parent = null;

    if (typeof ele !== "string") parent = ele;
    そうでない場合 (ele[0] === "#") 親 = document.getElementById(ele.slice(1));
    そうでない場合 (ele[0] === ".") {
         ブール値 === false の場合、親 = document.getElementsByClassName(ele.slice(1))[0];
        そうでない場合、親 = document.getElementsByClassName(ele.slice(1));
    } それ以外 {
         ブール値 === false の場合、親 = docuemnt.getElementsByTagName(ele)[0];
        そうでない場合、親 = document.getElementsByTagNameNS(ele);
    }

    //親ノードと子ノードを格納するために使用するオブジェクトを宣言します。const obj = {
         「親」:親、
         "子供たち": []
    };

    //ノードを追加 if (boolean) {
         (i = 0 とします; i < parent.length; i++) {
             // コンテナフラグメントを作成 constfragment = document.createDocumentFragment();
            //戻り値の子ノードを保存します。obj.children[i] = [];

            (j = 0; j < n; j++) の場合 {
                 定数ターゲット = document.createElement(ノード);
                ターゲットクラス名 = クラス名;
                フラグメント.appendChild(ターゲット);
                //戻り値の配列に子ノードを追加します。obj.children[i][j] = target;
            }

            親[I].appendChild(フラグメント)
        }
    } それ以外 {
         //フラグメント コンテナーを作成します。constfragment = document.createDocumentFragment();

        (i = 0; i < n; i++ とします) {
             定数ターゲット = document.createElement(ノード);
            ターゲットクラス名 = クラス名;
            フラグメント.appendChild(ターゲット);
            //戻り値の子ノードを追加します。obj.children[i] = target;
        }
        //フラグメント コンテナーを親ノードに一度に追加します。parent.appendChild(fragment);
    }

    // 呼び出すアニメーション関数のパラメータを返します。 return obj;
}

3. 事例分析

html

ノードがたくさんあるので、できるだけリアルで面白いものにしたいので、ノードにランダムな位置も追加しました。そのため、ノードの出力は JS によって制御されます。HTML では、いくつかの親要素ボックスと、それに対応する ID 名とクラス名が記述されるだけで、構造は比較的シンプルです。

CS

CSS 部分の難しさは、流星のスタイルと雲を円で描き、雲を重ねて立体感を出すことです。

まず、流星のスタイルについてお話しましょう。

#空 .星 {
      位置:絶対;
     不透明度: 0;
     zインデックス: 10000;
 }
 
 .star :: 後 {
      コンテンツ: "";
     表示:ブロック;
     境界:実線;
     境界線の幅: 2px 0 2px 80px ;
     /*流星の長さは徐々に短くなります*/ 
     境界線の色: 透明 透明 透明 rgba(255,255,255,1);
     境界線の半径: 2px 0 0 2px ;
     変換: 回転(-45度);
     変換元: 0 0 0 ;
     ボックスの影: 0 0 20px rgba(255,255,255,.3);
 }

まず、共通スタイルを抽出し、配置属性を追加します。

次に、after 疑似クラスを使用して星の後に流れ星を追加し、border 属性を使用して描画します。

1) モデル図: border-width の順序は、4 辺とも上、右、下、左です。同様に、border-color の順序は、4 辺とも上、右、下、左です。このように、border-width と border-color を一つずつ一致させると、2px が流星の幅、80px が流星の長さ、0 ピクセルの流星が尾を形成していることがわかります。頭の幅が 2px、尾の幅が 0px、長さが 80px の流れ星モデル。

2) もう少し現実的に: border-radius 経由?流星の頭に丸い角を追加して、よりリアルに見えるようにし、最後に roteta を使用して角度をつけて回転させ、落下しているように見せます。

3) 輝きを追加: ボックス シャドウを使用して流れ星に小さな光輪を追加し、キラキラ光っているように見せます。

上記の3つの手順を経て流れ星が描かれます。

そして雲は:

雲のコードは比較的長いので、ここでは掲載しません。やり方は、円を一つずつ重ねて雲の形を完成させるだけです。
雲のレイヤーが完成したら、それをコピーし、回転、不透明度、左配置などを使用して、複数の雲のレイヤーにフェードや重なり合う 3D 効果を作成します。

JS

JS部分はMeteorを例として使用します

setInterval(関数() {
     const obj = addChild("#sky","div",2,"star"); //流れ星を挿入 for(let i = 0; i < obj.children.length; i++) {
         //ランダムな位置 const top = -50 + Math.random() * 200 + "px",
            左 = 200 + Math.random() * 1200 + "px"、
            スケール = 0.3 + Math.random() * 0.5;
        定数タイマー = 1000 + Math.random()*1000;

        obj.children[i].style.top = top;
        obj.children[i].style.left = left;
        obj.children[i].style.transform = `scale(${scale})`;
        
        //アニメーションを追加 requestAnimation({
            要素: obj.children[i],
             属性: ["上", "左", "不透明度"],
             値: [150, -150, .8],
             時間:タイマー,
             フラグ: false、
             関数: 関数() {
                リクエストアニメーション({
                    要素: obj.children[I],
                     attr:["上","左","不透明"],
                     値: [150, -150, 0],
                     時間: タイマー、
                     フラグ: false、
                     関数:() => {
                        obj.parent.removeChild(obj.children[I]); //アニメーションの最後にノードを削除する}
                })
            }
        });
    }

}, 1000);

ここでは、私が独自にカプセル化した 2 つのメソッドが使用されています。1 つは requestAnimationFrame に基づく requestAnimation で、もう 1 つは appendChild に基づく addChild です。

ランダムな星の位置の効果を実現するために、setInterval タイマーを通じて流星が継続的に挿入および削除されます。

まず、毎回ページに 2 つの流星を追加しますが、タイマー間隔は流星のアニメーション時間よりも短くなります。これにより、ページ上の流星の数は固定値ではなく、確実に 2 より大きくなります。そうでなければ、一度に 2 つの流星では少し寂しくなります。

次に、ループを使用して(たとえば、機能するものであれば何でも for 式を使用することもできます。最も単純なものとしては)、ページに追加された新しい流星ごとにランダムな位置(上、左)、ランダムなサイズ(スケール)、ランダムなアニメーション実行時間(タイマー)を指定します。

最後に、ループ内で、ページに追加された新しい流星をそれぞれアニメーション化し、コールバック関数を通じてアニメーションが完了したらノードを削除します。ここで注意すべきは、アニメーションを 2 つの段階 (出現と消失、主に不透明度の制御) に分ける必要があることです。なお、ここでの私の処理では、各流星は 300px 同じ距離を移動します。この距離も乱数で制御できると思いますが、面倒だったのでやめておきました。

4. 小さな問題

これまでに見つかった問題は 2 つあります。

1つはDOM操作自体の問題です。ページはノードを追加したり削除したりし続けるため、リフローと再描画はパフォーマンスをかなり消費します。

2 つ目は、requestAnimationFrame 自体の問題です。タイマーはノードを追加し続けますが、requestAnimationFrame の特徴は、現在のページを離れて他のページを閲覧するとアニメーションが一時停止されることです。これにより問題が発生します。ノードは継続的に追加されますが、アニメーションはすべて停止され、実行されません。そのため、次にこのページに戻ると、アニメーションが爆発し、画面がフリーズし、多くのオタマジャクシが集まって母親を探しに出かけます。

5. 結論

この小さなケースは難易度の点では単純ですが、非常にスケーラブルです。たとえば、愛情表現、愛情表現、ロマンチックであることなどは、純粋な CSS でも実現できます。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

<<:  Idea の起動速度を改善し、Tomcat ログの文字化けを解決する方法

>>:  XHTML ドキュメントで JavaScript と CSS を正しく使用する方法

推薦する

nginxコンテナ設定ファイルの独立した実装

コンテナを作成する [root@server1 ~]# docker run -it --name ...

JavaScript での HTML キャンバスとページ ストレージ テクノロジの使用に関する詳細な説明

目次1. JavaScriptはHTMLでキャンバスを使用する2. ページストレージ技術1. Jav...

Linux で Scala 環境を構築し、簡単な Scala プログラムを書く

Linux に Scala 環境をインストールするのは非常に簡単です。Ubuntu 環境であれば、さ...

HTMLのposition属性の使い方(4種類)の詳しい説明

位置の 4 つのプロパティ値は次のとおりです。 1.相対的な2.絶対3.修正4.静的これら 4 つの...

Ubuntu の MySQL のパラメータ ファイル my.cnf の詳細な分析

序文MySQL に関する私の理解に基づくと、パフォーマンスの最適化作業やマスター スレーブ レプリケ...

フロントエンド JavaScript ハウスキーパー package.json

目次1. 必須属性1. 名前2. バージョン2. 説明情報1. 説明2. キーワード3. 著者4. ...

JavaScript クロージャの説明

目次1. クロージャとは何ですか? 2. クロージャの役割2.1) メモリ2.2) プライベート変数...

MySQL で not in を使用して null 値を含める問題を解決する

知らせ! ! ! uid が (a,b,c,null) に含まれないユーザーから * を選択します。...

MySQL パフォーマンスの最適化: インデックスを効率的かつ正しく使用する方法

実践こそが真実をテストする唯一の方法です。この記事では、インデックスの全体的な使用法についてのみ説明...

Docker MySQLコンテナデータベースへの変更が有効にならない問題を解決する

公式の MySQL イメージを使用するには、構成ファイル、DB データ ファイル ディレクトリなどの...

HTML シンプルショッピング数量アプレット

この記事では、参考までにシンプルなHTMLショッピング数量アプレットを紹介します。具体的な内容は次の...

Vue フロントエンドで PDF を生成してダウンロードする方法

目次1. インストールと導入2. PDFファイルをパッケージ化してエクスポートする方法構成の詳細PD...

HTML テーブル タグ チュートリアル (34): 行スパン属性 ROWSPAN

複雑なテーブル構造では、一部のセルが水平方向に複数のセルにまたがるため、行間属性 ROWSPAN を...

HTML の大なり、小なり、スペース、引用符などでよく使用されるエスケープ コードのリスト。

表は以下のとおりです。 HTMLソースコード結果を表示説明する&lt; <未満記号また...

Node.js を使用して C# のデータ テーブル エンティティ クラス生成ツールを作成する方法

Microsoft は T4 テンプレートを提供していますが、使用するのが非常に難しいと思います。ス...