CSS Houdini でダイナミックな波効果を実現

CSS Houdini でダイナミックな波効果を実現

CSS Houdini は、CSS 分野における最もエキサイティングなイノベーションとして知られています。 CSS 自体には長い間構文機能が不足しており、スケーラビリティはほぼゼロで、新機能のサポート効率が低く、互換性も低いです。 Houdini は CSS API を開発者に直接公開します。これまで完全にブラックボックスだったブラウザ解析フローが外部に公開され、開発者は独自の CSS プロパティをカスタマイズできるようになりました。

背景

ブラウザがページをレンダリングするときは、まずページの HTML と CSS を解析し、レンダリング ツリーを生成し、次にレイアウトとペイントを通じてページ コンテンツ全体を表示することが知られています。 Houdini が登場する前は、このプロセスで操作する余地がほとんどなく、特にレイアウトとペイントの段階は完全に閉じられていたため、CSS の柔軟性が大きく制限されていました。コミュニティで sass、less、stylus などの CSS プリプロセス テクノロジが登場したのは、主にこの理由によるものです。これらのテクノロジはすべて、プリコンパイルによって CSS の制限を打ち破り、CSS にさらに強力な構成機能と書き込み機能を与えることを望んでいます。そのため、徐々に CSS を手動で記述することはなくなり、より便利で柔軟な CSS 拡張言語が Web 開発の主役になってきました。この状況を見て、CSS Houdini はついにじっとしていられなくなりました。

CSS Houdini とは何ですか?

CSS Houdini は、ブラウザ解析プロセスの一連の API を公開しています。これらの API により、開発者はブラウザの CSS エンジンの動作に介入して、より多くの CSS ソリューションを提供できます。

CSS Houdini は主に以下の API を提供します。

CSS プロパティと値の API

CSS で変数を定義して使用できる、現在最も互換性の高い API です。

レイアウトAPI

開発者が独自のレイアウト モジュールを作成し、表示などのレイアウト プロパティをカスタマイズできるようにします。

ペイントAPI

開発者が独自のペイント モジュールを作成し、背景画像などの描画プロパティをカスタマイズできるようにします。

基礎: 3 つのステップで Painting API を使用する

1. ワークレット経由で HTML にスタイルを読み込むためのカスタム コード:

<div class="rect"></div>
<スクリプト>
  if (CSS の "paintWorklet") {
    CSS.paintWorklet.addModule("paintworklet.js");
  }
</スクリプト>

Worklets も Houdini が提供する API の 1 つであり、スタイルのカスタム JS コードの読み込みと実行を担当します。これは、メインコードの外部で実行される独立した作業プロセスである Web Worker に似ていますが、Worker よりも軽量で、CSS レンダリング タスクに最適です。

2. paintworklet.js を作成し、registerPaint メソッドを使用してペイント クラスの rect を登録し、ペイント属性の描画ロジックを定義します。

ペイントを登録する(
  「直角」、
  クラス {
    静的に入力プロパティを取得します(){
      ["--rect-color"] を返します。
    }
    ペイント(ctx, geom, プロパティ) {
      定数color = properties.get("--rect-color")[0];
      ctx.fillStyle = 色;
      ctx.fillRect(0, 0, geom.width, geom.height);
    }
  }
);

上記では、rect という名前のペイント属性クラスが定義されています。rect を使用すると、rect がインスタンス化され、ペイント メソッドが自動的にトリガーされてレンダリングが実行されます。 paint メソッドでは、ノードの CSS で定義された --rect-color 変数を取得し、要素の背景を指定された色で塗りつぶします。 ctx パラメータは Canvas Context オブジェクトなので、ペイント ロジックは Canvas 描画メソッドと同じです。

3. CSS で使用する場合は、paint メソッドを呼び出すだけです。

.rect {
  幅:100vw;
  高さ:100vh;
  背景画像: paint(rect);
  --rect-color: rgb(255, 64, 129);
}

これは、カスタム CSS 背景色プロパティの簡単な実装です。CSS Houdini を使用すると、キャンバスを操作するのと同じくらい柔軟に、必要なスタイル機能を実現できることがわかります。

上級: 動的リップルの実装

上記の手順に基づいて、CSS ペイント API を使用して動的な波の効果を実現する方法を示します。

<!-- index.html -->
<div id="波"></div>

<スタイル>
  #波 {
    幅: 20%;
    高さ:70vh;
    マージン: 10vh 自動;
    背景色: #ff3e81;
    背景画像: ペイント(波)
  }
</スタイル>

<スクリプト>
  if (CSS の "paintWorklet") {
    CSS.paintWorklet.addModule("paintworklet.js");

    定数 wave = document.querySelector("#wave");
    tick = 0 とします。  
    requestAnimationFrame(関数raf(now) {
      ティック += 1;
      wave.style.cssText = `--animation-tick: ${tick};`;
      raf のアニメーションフレームをリクエストします。
    });
  }
</スクリプト>

// ペイントワークレット.js
registerPaint('wave', クラス {
  静的に入力プロパティを取得します(){
    ['--animation-tick'] を返します。
  }
  ペイント(ctx, geom, プロパティ) {
    tick = Number(properties.get('--animation-tick')) とします。
    定数{
      幅、
      身長
    } = ジオメト;
    定数 initY = 高さ * 0.4;
    ティック = ティック * 2;

    ctx.beginPath();
    ctx.moveTo(0, initY + Math.sin(tick / 20) * 10);
    (i = 1; i <= width; i++ とします) {
      ctx.lineTo(i, initY + Math.sin((i + tick) / 20) * 10);
    }
    ctx.lineTo(幅, 高さ);
    ctx.lineTo(0, 高さ);
    ctx.lineTo(0, initY + Math.sin(tick / 20) * 10);
    ctx.closePath();

    ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
    ctx.fill();
  }
})

paintworklet では、 sin 関数を使用して波線を描画します。 AnimationWorklets はまだ実験段階であり、公開範囲が限られているため、アニメーションを駆動して波線を動かすには、ワークレットの外部で requestAnimationFrame API を使用します。完了すると、以下の効果が表示されます。


しかし、実際には、この効果は少し厳密です。正弦関数は規則的すぎます。実際には、波は不規則に変動するはずです。この不規則性は、主に次の 2 つの側面に反映されます。

1) 波紋の高さ(Y)は位置(X)に応じて不規則に変化する


グラフを xy に従って直交分解すると、期待する不規則性は、固定された瞬間に x 軸が変化するときの波紋の高さ y の不規則な変化として考えることができます。

2) 固定点(Xは固定)では、波紋の高さ(Y)は時間の経過とともに不規則に変化する

動的プロセスでは、時間の側面を考慮する必要があります。私たちが期待する不規則性は、時間の影響にも反映される必要があります。たとえば、同じ位置の波の高さは、風が吹く 1 秒前と 1 秒後には必ず不規則に変化します。

不規則性というと、Math.random メソッドの使用を思いつく人もいるかもしれません。ただし、ここでの不規則性は乱数による実装には適していません。2 回取得した乱数は不連続ですが、前後の 2 点の波は連続しているからです。これは理解するのが難しくありません。ギザギザの波を見たことがありますか?あるいは、ある瞬間には高さ 10 メートルだった波が、次の瞬間には 2 メートルまで下がるのを見たことがありますか?

この連続的な不規則な特徴を実現するために、sin 関数を放棄し、パッケージ simplex-noise を導入します。波の高さに影響を与える次元は位置 X と時間 T の 2 つあるため、ここでは noise2D メソッドが必要になります。このメソッドは、3 次元空間に連続した不規則な表面を事前に構築します。

// ペイントワークレット.js
'simplex-noise' から SimplexNoise をインポートします。
定数sim = new SimplexNoise(() => 1);

registerPaint('wave', クラス {
  静的に入力プロパティを取得します(){
    ['--animation-tick'] を返します。
  }

  ペイント(ctx, geom, プロパティ) {
    const tick = Number(properties.get('--animation-tick'));

    this.drawWave(ctx, geom, 'rgba(255, 255, 255, 0.4)', 0.004, tick, 15, 0.4);
    this.drawWave(ctx, geom, 'rgba(255, 255, 255, 0.5)', 0.006, tick, 12, 0.4);
  }
  
  /**
   * 波紋を描く */
  drawWave(ctx, geom, fillColor, ratio, tick, amp, ih) {
    定数{
      幅、
      身長
    } = ジオメト;
    定数 initY = 高さ * ih;
    const speedT = ティック * 比率;

    ctx.beginPath();
    (x = 0、speedX = 0、x <= width、x++) の場合 {
      速度X += 比率 * 1;
      var y = initY + sim.noise2D(speedX, speedT) * amp;
      ctx[x === 0 ? 'moveTo' : 'lineTo'](x, y);
    }
    ctx.lineTo(幅, 高さ);
    ctx.lineTo(0, 高さ);
    ctx.lineTo(0, initY + sim.noise2D(0, speedT) * amp);
    ctx.closePath();

    ctx.fillStyle = 塗りつぶし色;
    ctx.fill();
  }
})

ピークやバイアスなどのパラメータを変更することで、別の波形を描くことができます。効果は以下のとおりです。完成!

要約する

上記は、動的な波の効果を実現するために私が紹介した CSS Houdini です。お役に立てれば幸いです。ご質問がある場合は、メッセージを残してください。すぐに返信いたします。また、123WORDPRESS.COM ウェブサイトをサポートしてくださっている皆様にも感謝申し上げます。
この記事が役に立ったと思われた方は、ぜひ転載していただき、出典を明記してください。ありがとうございます!

<<:  クロスブラウザの問題に対する 5 つの解決策 (要約)

>>:  Maven モードで Tomcat ソースコードを実行する方法

ブログ    

推薦する

組み込み Linux 開発環境で ping と nfs を構築するためのソリューション

1. 組み込みソフトウェアレベル 1) ブートローダ -> ブートローダ組み込みシステム全体の...

MySQL の遅いクエリの例

導入スロークエリログを有効にすると、MySQL は指定された時間を超えるクエリステートメントを記録で...

React+TS を使用したシンプルな Jira プロジェクトを実装するためのベスト プラクティス

トレーニングのための一連のプロジェクト反応+ts内容は少ないですが、フックのカプセル化、ts ジェネ...

MySQL 5.7.17 のインストールと使用方法のグラフィックチュートリアル

MySQL は、スウェーデンの会社 MySQL AB によって開発され、現在は Oracle が所有...

複雑なウェブサイトのナビゲーションを簡素化

<br />ナビゲーション設計は構造設計における主要なタスクの 1 つです。ソフトウェア...

HTML Webページ作成チュートリアル iframeタグを慎重に使用してください

iframe を使用すると、他の Web サイトのページを簡単に呼び出すことができますが、注意して使...

シェルスクリプトはNginxのaccess.logのPVを定期的にカウントし、APIに送信してデータベースに保存します。

1. PVとIPの統計一日のPV(ページビュー)をカウントする cat access.log | ...

LeetCode の SQL 実装 (183. 注文をしたことがない顧客)

[LeetCode] 183.注文しない顧客Web サイトに、Customers テーブルと Or...

MySQLインデックスが失敗するいくつかの状況の分析

1. 最左プレフィックス原則 - 複数の列にインデックスが付けられている場合は、最左プレフィックス原...

Linux のさまざまなロックメカニズムの使用方法と違いについて詳しく説明します

序文:この知識を理解する必要がある人は、すでにプロセス間通信とスレッド間通信の基本的な理解を持ってい...

Struts2 ジャンプ後に CSS と JS が無効になる問題の解決策のアイデアと実装手順

struts2 アクションの実行後にジャンプした jsp が表示されると、css が機能しません。問...

LinuxのCPU負荷とCPU使用率の詳細な説明

CPU 負荷と CPU 使用率これらは両方とも、ある程度、マシンの忙しさを反映できます。 CPU 使...

jsvc を使用して tomcat を起動する方法 (通常のユーザーとして実行)

jsvc の紹介実稼働環境では、Tomcat はデーモン モードで実行する必要があります。Tomc...

Linux jdk のインストールと環境変数の設定チュートリアル (jdk-8u144-linux-x64.tar.gz)

最初にsudo suコマンドを使用して root アカウントに切り替えることをお勧めします。そうしな...

jsのイベントループ機構の解析

序文ご存知のとおり、JavaScript は本質的にシングルスレッドですが、ブラウザは非同期リクエス...