ミニプログラムは左スライドのドロワーメニューをネイティブに実装します

ミニプログラムは左スライドのドロワーメニューをネイティブに実装します

モバイル デバイスでは、サイドスライド メニューは非常によく使用されるコンポーネントです (通常はドロワーと呼ばれます)。最近の携帯電話の画面は大きすぎるため、画面の隅にあるメニューボタンをクリックするのは、画面の中央をスワイプするほど便利ではないのは明らかです。

他のプラットフォームと比較すると、ミニプログラムのコンポーネント ライブラリ サポートは明らかに十分ではなく、さまざまなフレームワークはまだ成熟していません。以前フレームワークを使用していた際に、さまざまな不可解なバグに困惑したため、ネイティブ環境に戻ることにしました。

最近、ネイティブフレームワークでスライドドロワーメニュー効果を実装する方法を勉強しました。とても面倒だと思っていましたが、数十行のコードで済み、類推によって多くの柔軟な効果を実現できることが分かりました。インターネット上には関連情報があまりないと感じたので、ここで共有したいと思います。記事に掲載されているコードブロックに加え、リンクをクリックして効果をプレビューしたり、ミニプログラム開発ツールでコードスニペットを表示したりすることもできます。ここでは、3 つの一般的なエフェクトが実装されています。まずはアニメーション画像を見てから、コードの実装を 1 つずつ説明していきます。

上部のメニュー

A2メニューは上層にあり、下層はマスクされています

Bメニューは下部にあります

WXS レスポンス イベント

ジェスチャ コントロール メニューの原理は非常にシンプルです。アプレットはtouchstart, touchmove, touchend touchend) など、タッチ ジェスチャによってトリガーされる一連のイベントを提供します。これらのイベントにカスタム イベント応答関数をバインドすることで、ジェスチャに応じてメニューを開いたり閉じたりすることができます。

パフォーマンス上の理由から、イベント処理関数は JS ファイルではなく WXS ファイルに配置するのが最適です。具体的な原理はアプレットの動作環境に関係しますので、興味のある方は記事の最後でご確認ください。 WXS はアプレット専用のスクリプト言語です (WXS と JS の関係は WXSS と CSS の関係に相当します)。構文は JS に似ていますが、次のような違いもあります。

  • JSから分離されているため、他のJavaScriptファイルで定義された関数を呼び出すことも、ミニプログラムによって提供されるAPIを呼び出すこともできません。
  • ミニプログラムの組み込みコンポーネントのイベントにのみ応答でき、カスタム コンポーネントのイベント コールバックはサポートされません。
  • 変数と関数はデフォルトではモジュール内でプライベートであり、 module.exportsを通じて外部に公開されます。
  • WXML にインポートするにはタグを使用します (相対パスを使用する必要があります)

wxs ファイルと wxml ファイルでの基本的な記述方法は次のとおりです。

// インデックス.wxs

関数 touchStart(e, ins) {}
関数 touchMove(e, ins) {}
関数 touchEnd(e, ins) {}

モジュール.エクスポート = {
  タッチスタート: タッチスタート、
  タッチムーブ: タッチムーブ、
  タッチエンド: タッチエンド
}
<wxs モジュール="引き出し" src="./index.wxs"></wxs>

<view bindtouchstart="{{drawer.touchstart}}"
      bindtouchmove="{{drawer.touchmove}}" 
      bindtouchend="{{drawer.touchend}}">
</ビュー>

プランA

ページ構造とスタイル

最も一般的なドロワーメニュースタイルのひとつです。スライドしてもメインコンテンツは移動せず、メニューは上層に表示されます。まず、基本的な HTML 構造と CSS スタイルを記述します (一部の美観スタイルシートは省略されています)。

<wxs モジュール="引き出し" src="./index.wxs"></wxs>

<表示>
  <view class="main" bindtouchstart="{{drawer.touchstart}}"
    bindtouchmove="{{drawer.touchmove}}" bindtouchend="{{drawer.touchend}}">
    <表示>
      右にスライドするとサイドメニューソリューションAが表示されます
    </ビュー>
  </ビュー>

  <view class="drawer" data-drawerwidth="150">
    <view class="drawer-item">引き出しA</view>
    <view wx:for="{{[1, 2, 3]}}" class="drawer-item">
      <text>メニュー項目 {{item}}</text>
    </ビュー>
  </ビュー>
</ビュー>

WXML の重要なポイント:

  • wxs モジュールを正しくインポートします (相対パスを使用する必要があります)
  • スライド ジェスチャを実行するとメニューは非表示になり、スライドは実際にはメイン インターフェイスで実行されるため、3 つのスライド イベント コールバックをメイン コンテンツのビューにバインドする必要があります。
  • .drawer要素が移動され、簡単にアクセスできるようにclass属性を設定する必要があります
  • ドロワー要素の data-drawerwidth 属性は、データセットを通じて wxs スクリプトに渡されます。この属性はメニューの幅を指定し、スタイルと一致している必要があります。

WXSS については特に言うことはありませんが、コメントに次のように書かれています:

。主要 {
  高さ:100vh;
  幅: 100%;
  位置: 絶対;
}

.引き出し{
  高さ:100vh;
  幅: 150ピクセル;
  位置: 絶対;
  transition: transform 0.4s easy; /* 変位を実現するために transform を使用し、よりスムーズにするために遷移アニメーションを追加します */
  left: -150px; /* 幅とオフセットはWXMLの値と一致しており、メニューは初期状態では非表示になっています*/
}

WXS イベントコールバック関数

wxs関数には2つの入力パラメータがある

  • eventはアプレットのイベントオブジェクトであり、これに基づいてイベントをトリガーするコンポーネントのインスタンスも存在しますevent.instance
  • ownerInstance 、イベントをトリガーしたコンポーネントの親コンポーネント(ページ)のインスタンスです。

wxs のコンポーネント インスタンスはカプセル化されたComponentDescriptorオブジェクトであり、コンポーネントのデータセットを操作したり、スタイルやクラスなどを設定したりすることができ、基本的にインタラクティブなアニメーションには十分です。詳しい使用方法については、ドキュメントを参照してください。

var wxsFunction = 関数(イベント、所有者インスタンス) {
    var instance = ownerInstance.selectComponent('.classSelector') // コンポーネントのインスタンスを返しますinstance.setStyle({
        "font-size": "14px" // rpx をサポート
    })
    インスタンス.getDataset()
    インスタンス.setClass(クラス名)

    false を返す // バブルアップしない。これは stopPropagation と preventDefault を同時に呼び出すのと同じである。
}

WXS スクリプト

条件判断が中心で、ロジックは特別なものではなく、状況と組み合わせると理解するのは難しくありません

  • 変数を宣言する際に let または const を使用しないでください。エラーが発生します。
  • 変換属性 X 変位を設定するコードは、見た目を美しくするために単純にカプセル化されています。
  • ジャッジポイントは吸着効果に似ており、メニューを一定の位置を超えて描画すると、残りの部分が自動的に開きます。
var スタートマーク = 0;
var status = 0; // メニューが開いているか閉じているかのステータス var JUDGEPOINT = 0.7;

関数touchStart(e, ins) {
  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  スタートマーク = pageX;
}

関数touchMove(e, ins) {
  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  var offset = pageX - 開始マーク;
  var drawerComp = ins.selectComponent('.drawer');
  var drawerWidth = drawerComp.getDataset().drawerwidth;

  if (オフセット > 0 && ステータス == 0) {
    setCompTransX(drawerComp, Math.min(drawerWidth, offset))
  } そうでない場合 (オフセット < 0 && ステータス == 1) {
    setCompTransX(drawerComp、Math.max(0、オフセット))
  }
}

関数touchEnd(e, ins) {
  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  var offset = pageX - 開始マーク;
  var drawerComp = ins.selectComponent('.drawer');
  var drawerWidth = drawerComp.getDataset().drawerwidth;

  if (オフセット > 0 && ステータス == 0) {
    if (オフセット < 引き出し幅 * JUDGEPOINT) {
      setCompTransX(引き出しComp, 0);
    } それ以外 {
      setCompTransX(引き出しComp、引き出し幅);
      ステータス = 1;
    }
  } そうでない場合 (オフセット < 0) {
    setCompTransX(引き出しComp, 0);
    ステータス = 0;
  }
}

関数setCompTransX(comp, x) {
  comp.setStyle({
    変換: 'translateX(' + x + 'px)',
  })
}

モジュール.エクスポート = {
  タッチスタート: タッチスタート、
  タッチムーブ: タッチムーブ、
  タッチエンド: タッチエンド
}

マスクレイヤー

記事の最初または最後に記載されているリンクをクリックすると、ミニプログラム開発ツールで完全なコードが表示されます。

マスク レイヤーでは、メニューとメイン コンテナーの間にビューを追加するだけで済みます。

<view class="main"></view>
<view class="mask" data-maxopacity="0.6"></view>
<view class="drawer" data-drawerwidth="150"></view>

スタイルでは、pointer-events プロパティが非常に重要です。これを none に設定すると、クリック アクションによってビューが下のレイヤーにまで浸透してしまいます。マスクレイヤーはドロワーと違って画面外にあるため、透明度は 0 ですが、実際には .main を常に覆っています。この属性を追加しないと、.main 上のクリックはすべて .mask 上になり、スライドなどのボタンが無効になります。

。マスク {
  高さ:100vh;
  幅: 100%;
  位置: 固定;
  遷移: 不透明度 0.4 秒、緩和;
  不透明度: 0;
  ポインタイベント: なし;
  背景色: #548CA8;
}

wxs スクリプトも基本的には同じです。同様の方法でデータセット内の .mask インスタンスと透明度パラメータを取得し、変位属性を設定する際にマスク レイヤーの透明度属性を設定するだけです。

関数setDrawer(x) {
  setCompTransX(引き出しComp、x);
  マスクComp.setStyle({
    不透明度: x / 引き出し幅 * マスク不透明度、
  })
}

プランB

記事の最初または最後に記載されているリンクをクリックすると、ミニプログラム開発ツールで完全なコードが表示されます。

ソリューション B とソリューション A の主な違いは、スライドするとメイン インターフェイスが右に移動して下部のメニューが表示される点であり、他の部分の実装には違いはありません。ここでは主な相違点のみを掲載します。

.main 要素が移動されるため、幅構成データが要素のタグ内に配置され、取得できるコンポーネント インスタンスが 1 つ少なくなります。

<view class="drawer"></view>

<view class="main" 
      データドロワー幅="150" 
      bindtouchstart="{{drawer.touchstart}}"
      bindtouchmove="{{drawer.touchmove}}" 
      bindtouchend="{{drawer.touchend}}">
</ビュー>

遷移アニメーションのプロパティも .main に配置され、.drawer のオフセットは不要になりました。

。主要 {
  高さ:100vh;
  幅: 100%;
  位置: 絶対;
  遷移: 変換 0.4 秒のイーズ;
}

.引き出し{
  高さ:100vh;
  幅: 150ピクセル;
  位置: 絶対;
}

wxs スクリプトでは、取得されるさまざまなコンポーネントを除いて、変位設定も変更する必要はありません。

関数touchMove(e, ins) {
  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
  var offset = pageX - 開始マーク;
  var mainComp = ins.selectComponent('.main');
  var drawerWidth = mainComp.getDataset().drawerwidth;

  if (オフセット > 0 && ステータス == 0) {
    setCompTransX(mainComp、Math.min(drawerWidth、オフセット))
  } そうでない場合 (オフセット < 0 && ステータス == 1) {
    setCompTransX(mainComp、Math.max(0、オフセット))
  }
}

WXSを使用する理由

ミニプログラムは多くの点で Web 開発と非常に似ていますが、内部的にはいくつかの違いがあります。 Web ページでは、レンダリングとスクリプトの実行は同じスレッドで実行されます (そのため、スクリプトを実行するとページ全体がフリーズする可能性があります)。ミニプログラムは、ロジック レイヤー (JS スクリプト) とレンダリング レイヤー (WXML と WXSS) をそれぞれ異なるスレッドで実行し、スレッドはクライアント (ネイティブ) を介して通信します。

そのため、JS スクリプトを使用してイベントに応答する場合、touchmove がトリガーされるたびに 2 つのプロセス間通信が生成され (下の図の左のように)、通信のオーバーヘッドが大きくなります。同時に、「setData のレンダリングによって他のスクリプトの実行もブロックされます」(ドキュメントにはそう書かれていますが、理由はわかりません)。ジェスチャによって膨大な数の touchmove イベントがトリガーされるため、上記の理由によりアニメーションが妨害されることになります。

WXS 機能はビュー レイヤーで実行されるため、上記の問題は発生しません (下の右図を参照)。

結論と参考文献

上記は、ネイティブ アプレットのドロワー メニューを実装するいくつかの方法です。お役に立てれば幸いです。記事の抜け漏れについて議論したり修正したりしていただければ幸いです。

リンクをクリックすると、ミニプログラム開発ツールで完全なコードが表示されます (ミニプログラム開発ツールを使用してコード スニペットを共有するには、開発ツールのバージョンに特定の要件があります)。彼が共有したコード スニペットは少し謎めいています。直接開けない場合は、ログイン後に「プロジェクト - インポート コード スニペット」にリンクまたはリンクの最後の ID を直接入力してみてください。

参考文献:

ミニプログラムフレームワーク/ビューレイヤー/イベントシステム/WXSレスポンスイベント

公式デモ

ミニプログラムホスト環境

ミニプログラムでの左スライドドロワーメニューのネイティブ実装に関するこの記事はこれで終わりです。ミニプログラムでの左スライドドロワーメニューに関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • WeChatアプレットの左スライドメニュー表示機能の実装

<<:  MySQL エラー番号 1129 の解決方法

>>:  ESXI の仮想マシンにワークステーションをインストールするときに発生するネットワーク障害の解決策

推薦する

画像ボタン送信とフォーム繰り返し送信の問題に関する議論

多くの場合、フォームを美しくするために、送信ボタンが画像に置き換えられます。ただし、細部に注意を払わ...

MySQL 分離レベルの詳細な説明と例

目次MySQL の 4 つの分離レベルデータ テーブルを作成します。分離レベルの設定物事の分離レベル...

Docker での Redis のマスタースレーブ構成チュートリアルの詳細説明

1. Redisイメージを取得するdocker pull redis 2. それぞれポート6379、...

JavaScript の組み込み Date オブジェクトの詳細な説明

目次日付オブジェクト日付オブジェクトの作成新しい日付()日付を取得する()取得日()月を取得する()...

Windows での MySQL 8.0.18 インストール チュートリアル (図解)

ダウンロードダウンロードアドレス: https://dev.mysql.com/downloads/...

MySQLとElasticsearch間のデータ非対称性問題の解決策

MySQLとElasticsearch間のデータ非対称性問題の解決策jdbc-input-plugi...

HTML/XHTML における img 画像タグの基本的な使用法の詳細な説明

画像タグは、Web ページに画像を表示するために使用されます。 HTML/XHTML 画像 <...

Linux のハードリンクとソフトリンクの原理と使用法の分析

Linux システムには、ファイル共有を解決するために使用できるリンク ファイルと呼ばれる種類のファ...

Docker クロスサーバー通信オーバーレイソリューション (パート 1) Consul 単一インスタンス

目次シナリオタスクアイデア分析するコンセプトと選択ちょっとしたテスト環境説明予防実践テスト引用シナリ...

Vue3は画像拡大鏡効果を実現します

この記事の例では、画像拡大鏡効果を実現するためのVue3の具体的なコードを参考までに共有しています。...

Ubuntu 20.04でLNMP環境を構築する方法

簡単な説明以前 Centos7 で構築し、その後個人開発環境として Ubuntu 20.04 を使っ...

Oracle10パーティションとMySQLパーティションの違いの詳細な説明

一般的に使用される Oracle10g パーティションは、範囲 (範囲パーティション)、リスト (リ...

HTMLのタグと要素の違いの詳細な説明

ウェブページに慣れていない友人の多くは私と同じように、HTML で要素、タグ、属性がどのように定義さ...

Apache Superset を使用して ClickHouse データを視覚化する 2 つの方法

Apache Superset は、データを表示および探索する方法を提供する強力な BI ツールで...

Nodejs エラー処理プロセス記録

この記事では、接続エラー ECONNREFUSED を例に、Node.js がエラーを処理するプロセ...