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つの手順を経て流れ星が描かれます。 そして雲は: 雲のコードは比較的長いので、ここでは掲載しません。やり方は、円を一つずつ重ねて雲の形を完成させるだけです。 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 を正しく使用する方法
MySQLでホワイトリストアクセスを設定する手順1. ログイン mysql -uroot -pmys...
テンプレート 1: ログイン.vue <テンプレート> <p class=&quo...
目次VARCHAR 型と CHAR 型結論: VARCHAR 型と CHAR 型VARCHAR と ...
Vueのメソッドとプロパティ1. 方法使用法 1メソッド: {メソッド名: function(){}...
httpリターンコードリスト(以下は概要です)詳細な中国語の説明についてはここをクリックしてくださ...
Linux 構成/ビルド システムがどのように機能するかを深く理解します。 Linux カーネル構成...
目次1. オプションの連鎖演算子 [? .】 2. 論理的な空の代入 (?? =) 3. 論理和代入...
プロジェクトの開発中に、データベースのデータがどんどん大きくなり、その結果、1 つのテーブルにデータ...
最近はビッグデータで遊んでいます。友人が私のところに来て、オンラインの Tomcat が不可解に終了...
この本「グラフィックデザイナーとして成長する」は多くの人が読んでおり、私もオリジナルの PDF 版を...
適用シナリオ:新しい要件の 1 つはアンケート調査を行うことですが、必然的に多くの質問が含まれ、1 ...
目次スロットとは何かデフォルトスロットの理解コードスニペット要約するスロットとは何かスロットは、親コ...
1.デバイス幅定義: 出力デバイスの画面表示幅を定義します。 Web ページが Safari で開か...
別のライブラリから別のライブラリにデータをインポートする必要がある場合があり、このデータは CSV ...
a タグが新しいページを開くかどうか: (1)百度百科事典:ヘッダーが異なる場合は新しいページが開き...