モバイルでのスワイパーの使用方法

小云云
リリース: 2018-01-26 13:29:05
オリジナル
2538 人が閲覧しました

最近、モバイル開発でEle.meのvueフロントエンドコンポーネントライブラリを使用しています。単純にコンポーネントで使用したくないので、実装原理をより深く理解したいと考えています。この記事では主にモバイルエフェクトに関するSwiperの関連情報を詳しく紹介しますので、興味のある方は参考にしていただければ幸いです。

コードはこちらです: poke me

1. 説明

親コンテナoverflow:hidden;,子ページtransform:translateX(-100%);width:100%;

2. コア分析

2.1 ページの初期化

すべてのページがモバイル画面の 1 画面幅の左側に配置されているため、初期状態ではページ内にサブページが表示されません。最初のステップは、表示されるサブページを設定する必要があります。デフォルトでは、defaultIndex: 0


function reInitPages() {
  // 得出页面是否能够被滑动
  // 1. 子页面只有一个
  // 2. 用户手动设置不能滑动 noDragWhenSingle = true
  noDrag = children.length === 1 && noDragWhenSingle;

  var aPages = [];
  var intDefaultIndex = Math.floor(defaultIndex);
  var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length) 
    ? intDefaultIndex : 0;
  
  // 得到当前被激活的子页面索引
  index = defaultIndex;

  children.forEach(function(child, index) {
    aPages.push(child);
    // 所有页面移除激活class
    child.classList.remove(&#39;is-active&#39;);

    if (index === defaultIndex) {
      // 给激活的子页面加上激活class
      child.classList.add(&#39;is-active&#39;);
    }
  });

  pages = aPages;
}
ログイン後にコピー

2.2 コンテナのスライディング スタート (onTouchStart)

android 携帯電話の以前のバージョンでは、event.preventDefault( )が始まります 一定のパフォーマンス向上効果があり、スライドの引っかかりが少なくなります。

事前作業:

  • ユーザーがPrevent:trueを設定した場合、スライド時のデフォルトの動作を防止します

  • ユーザーがstopPropagation:trueを設定した場合、スライド時にイベントが上方向に伝播するのを防止します

  • アニメーションはまだ終了していません。スライドを防止します

  • ドラッグを設定します: true、スライド開始します

  • ユーザーのスクロールを false に設定します

ドラッグ開始:

グローバル オブジェクトを使用して、次の情報を記録します。


dragState = {
  startTime      // 开始时间
  startLeft      // 开始的X坐标
  startTop      // 开始的Y坐标(相对于整个页面viewport pageY)
  startTopAbsolute  // 绝对Y坐标(相对于文档顶部 clientY)
  pageWidth      // 一个页面宽度
  pageHeight     // 一个页面的高度
  prevPage      // 上一个页面
  dragPage      // 当前页面
  nextPage      // 下一个页面
};
ログイン後にコピー

2.3 コンテナスライド(onTouchMove)

グローバルdragStateを適用し、新しい情報を記録します


dragState = {
  currentLeft     // 开始的X坐标
  currentTop     // 开始的Y坐标(相对于整个页面viewport pageY)
  currentTopAbsolute // 绝对Y坐标(相对于文档顶部 clientY)
};
ログイン後にコピー

次に、スタートとスライドの情報を通じて何かを計算できます:

の水平変位スライド (offsetLeft = currentLeft - startLeft)

スライドの垂直変位 (offsetTop = currentTopAbsolute - startTopAbsolute)

これはユーザーの自然なスクロールですか? ここでの自然なスクロールとは、ユーザーがスワイパーをスライドさせたくないことを意味しますが、ページをスライドさせたいです


// 条件
// distanceX = Math.abs(offsetLeft);
// distanceY = Math.abs(offsetTop);
distanceX < 5 || ( distanceY >= 5 && distanceY >= 1.73 * distanceX )
ログイン後にコピー

左に移動するか右に移動するかを決定します(offsetLeft < 0の場合は左に移動し、それ以外の場合は右に移動します)

変位をリセットします


// 如果存在上一个页面并且是左移
if (dragState.prevPage && towards === &#39;prev&#39;) {
  // 重置上一个页面的水平位移为 offsetLeft - dragState.pageWidth
  // 由于 offsetLeft 一直在变化,并且 >0
  // 那么也就是说 offsetLeft - dragState.pageWidth 的值一直在变大,但是仍未负数
  // 这就是为什么当连续属性存在的时候左滑会看到上一个页面会跟着滑动的原因
  // 这里的 translate 方法其实很简单,在滑动的时候去除了动画效果`transition`,单纯改变位移
  // 而在滑动结束的时候,加上`transition`,使得滑动到最后释放的过渡更加自然
  translate(dragState.prevPage, offsetLeft - dragState.pageWidth);
}

// 当前页面跟着滑动
translate(dragState.dragPage, offsetLeft);

// 后一个页面同理
if (dragState.nextPage && towards === &#39;next&#39;) {
  translate(dragState.nextPage, offsetLeft + dragState.pageWidth);
}
ログイン後にコピー

2.4 スライド終了(onTouchEnd)

Pre -work:

スライド中に、ユーザーの自然なスクロールであるかどうかをリアルタイムで判断できます。ユーザーの自然なスクロールである場合、swiperのスライド情報はカウントされないため、いくつかのクリア操作が必要です。もちろん、 userScrolling: false の場合は、サブページをスライドして doOnTouchEnd メソッド


を実行して、タップ イベントかどうかを判断します

dragging = false;
dragState = {};
ログイン後にコピー

方向を決定する

// 时间小于300ms,click事件延迟300ms触发
// 水平位移和垂直位移栋小于5像素
if (dragDuration < 300) {
  var fireTap = Math.abs(offsetLeft) < 5 && Math.abs(offsetTop < 5);
  if (isNaN(offsetLeft) || isNaN(offsetTop)) {
    fireTap = true;
  }
  if (fireTap) {
    console.log(&#39;tap&#39;);
  }
}
ログイン後にコピー

アニメーションを実行する

// 如果事件间隔小于300ms但是滑出屏幕,直接返回
if (dragDuration < 300 && dragState.currentLeft === undefined) return;

// 如果事件间隔小于300ms 或者 滑动位移超过屏幕宽度 1/2, 根据位移判断方向
if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) {
  towards = offsetLeft < 0 ? &#39;next&#39; : &#39;prev&#39;;
}

// 如果非连续,当处于第一页,不会出现上一页,当处于最后一页,不会出现下一页
if (!continuous) {
  if ((index === 0 && towards === &#39;prev&#39;) 
    || (index === pageCount - 1 && towards === &#39;next&#39;)) {
    towards = null;
  }
}

// 子页面数量小于2时,不执行滑动动画
if (children.length < 2) {
  towards = null;
}
ログイン後にコピー

事後作業:


スライディングサイクル

// 当没有options的时候,为自然滑动,也就是定时器滑动
function doAnimate(towards, options) {
  if (children.length === 0) return;
  if (!options && children.length < 2) return;

  var prevPage, nextPage, currentPage, pageWidth, offsetLeft;
  var pageCount = pages.length;

  // 定时器滑动
  if (!options) {
    pageWidth = element.clientWidth;
    currentPage = pages[index];
    prevPage = pages[index - 1];
    nextPage = pages[index + 1];
    if (continuous && pages.length > 1) {
      if (!prevPage) {
        prevPage = pages[pages.length - 1];
      }

      if (!nextPage) {
        nextPage = pages[0];
      }
    }

    // 计算上一页与下一页之后
    // 重置位移
    // 参看doOnTouchMove
    // 其实这里的options 传与不传也就是获取上一页信息与下一页信息
    if (prevPage) {
      prevPage.style.display = &#39;block&#39;;
      translate(prevPage, -pageWidth);
    }

    if (nextPage) {
      nextPage.style.display = &#39;block&#39;;
      translate(nextPage, pageWidth);
    }
  } else {
    prevPage = options.prevPage;
    currentPage = options.currentPage;
    nextPage = options.nextPage;
    pageWidth = options.pageWidth;
    offsetLeft = options.offsetLeft;
  }

  var newIndex;
  var oldPage = children[index];

  // 得到滑动之后的新的索引
  if (towards === &#39;prev&#39;) {
    if (index > 0) {
      newIndex = index - 1;
    }
    if (continuous && index === 0) {
      newIndex = pageCount - 1;
    }
  } else if (towards === &#39;next&#39;) {
    if (index < pageCount - 1) {
      newIndex = index + 1;
    }
    if (continuous && index === pageCount - 1) {
      newIndex = 0;
    }
  }

  // 动画完成之后的回调
  var callback = function() {
    // 得到滑动之后的激活页面,添加激活class
    // 重新赋值索引
    if (newIndex !== undefined) {
      var newPage = children[newIndex];
      oldPage.classList.remove(&#39;is-active&#39;);
      newPage.classList.add(&#39;is-active&#39;);
      index = newIndex
    }

    if (isDone) {
      end();
    }

    if (prevPage) {
      prevPage.style.display = &#39;&#39;;
    }

    if (nextPage) {
      nextPage.style.display = &#39;&#39;;
    }
  }

  setTimeout(function() {
    // 向后滑动
    if (towards === &#39;next&#39;) {
      isDone = true;
      before(currentPage);
      // 当前页执行动画,完成后执行callback
      translate(currentPage, -pageWidth, speed, callback);
      if (nextPage) {
        // 下一面移动视野中
        translate(nextPage, 0, speed)
      }
    } else if (towards === &#39;prev&#39;) {
      isDone = true;
      before(currentPage);
      translate(currentPage, pageWidth, speed, callback);
      if (prevPage) {
        translate(prevPage, 0, speed);
      }
    } else {
     // 如果既不是左滑也不是右滑
     isDone = true;
     // 当前页面依旧处于视野中
     // 上一页和下一页滑出
     translate(currentPage, 0, speed, callback);
     if (typeof offsetLeft !== &#39;undefined&#39;) {
       if (prevPage && offsetLeft > 0) {
          translate(prevPage, pageWidth * -1, speed);
       }
       if (nextPage && offsetLeft < 0) {
          translate(nextPage, pageWidth, speed);
       }
     } else {
      if (prevPage) {
       translate(prevPage, pageWidth * -1, speed);
      }

      if (nextPage) {
       translate(nextPage, pageWidth, speed);
      }
     }
    }
  }, 10);
}
ログイン後にコピー

概要


全体的に、実装原理は比較的単純です。スライドは初期位置を記録し、前のページと次のページに表示されるページを計算します。前のページと次のページの移動。スライドが終了すると、移動結果に応じて対応するアニメーションが実行されます。
1つの詳細は、スライド中のトランジションによって前のページと次のページが不自然に移動するのを防ぐために、
トランジション

のエフェクトを空に設定し、スライドが終了した後に追加します。

関連する推奨事項:

WeChat アプレットタブとスワイパーの組み合わせ効果の実現


コンポーネント開発の vue スワイパー実装

スワイパーの使用方法の詳細な説明

以上がモバイルでのスワイパーの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!