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 ソースコードを実行する方法

推薦する

MySQL データをエクスポートおよびインポートするための HeidiSQL ツール

場合によっては、SQL へのデータのエクスポートとインポートを容易にするために、特定のツールを使用し...

MySQL データベースの 1045 エラーの解決方法

ローカル データベースがサーバー データベースに接続されているときに発生する 1045 の問題を解決...

友達やグループを見つけるためのJavaScriptのLayim

現在、layuiの関係者はlayim友達検索ページの構造とスタイルを提供していません。私は個人的に非...

docker compose サービスの起動順序を制御する方法

まとめDocker-compose は複数の Docker コンテナ サービスを簡単に組み合わせるこ...

VMwareを使用したPermeateレンジシステムのインストール手順の詳細説明

1. 背景私たちは時々社内研修を行っており、実験環境をよく利用しています。最初はdockerコンテナ...

フォームの送信イベントが応答しない

1. 問題の説明<br />JS を使用してフォームの送信メソッドを呼び出してフォームを...

ページングクリックコントロールを実装するネイティブJS

これは、ネイティブJSを使用してページングクリックコントロールを実装する必要がある面接の質問です。参...

React 合成イベントの説明

目次入力ボックスをクリックして開始します拡張機能入力ボックスをクリックすると複数のイベントが発生しま...

MySQLのどのフィールドがインデックスに適しているかについての簡単な説明

目次1 データベース インデックスを作成するための一般的なルールは次のとおりです。 2. 数千万件の...

Ubuntu 18.04 でソースコードから Odoo14 をインストールするチュートリアル

目次このシリーズの背景概要PostgreSQL データベースの準備ソースからインストール仮想環境の作...

ノードでシェルスクリプトを使用する方法

背景開発中、特定の状況でビジネス ロジックをバッチ処理するためのスクリプトが必要になる場合があります...

Vue はチャット ボックスで絵文字を送信する機能を実装します

vueチャットボックスで絵文字を送信し、vueインターフェースで絵文字を送信するための具体的なコード...

VUEはトークンログイン認証を実装

この記事では、トークンログイン認証を実装するためのVUEの具体的なコードを例として紹介します。具体的...

vue+rem カスタムカルーセル効果

vue+remを使用したカスタムカルーセルチャートの実装は参考までに。具体的な内容は以下のとおりです...

ウェブページのドロップダウンリストとdivレイヤーのカバーの問題を選択する

HTML の select 要素に関する質問は、さまざまな場所で提起されています。最近のプロジェクト...