모바일에서 Swiper를 사용하는 방법

小云云
풀어 주다: 2018-01-26 13:29:05
원래의
2539명이 탐색했습니다.

최근에는 모바일 개발 시 Ele.me의 vue 프론트엔드 컴포넌트 라이브러리를 사용하고 있습니다. 단순히 컴포넌트로만 사용하고 싶지 않기 때문에 구현 원리에 대해 더 깊이 이해하고 싶습니다. 이 글은 Swiper의 모바일 효과 관련 정보를 주로 소개하고 있으며, 관심 있는 친구들이 참고하면 도움이 될 것입니다.

코드는 여기에 있습니다: poke me

1. 설명

상위 컨테이너overflow:hidden;,하위 페이지transform:translateX(-100%);width:100%;

2. 핵심 분석

2.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:

슬라이딩하는 동안 사용자의 자연스러운 스크롤인지 실시간으로 판단할 수 있습니다. be done:

dragging = false;
dragState = {};
로그인 후 복사
로그인 후 복사

물론 userScrolling: false 인 경우 하위 페이지를 슬라이드하고 doOnTouchEnd 메서드

를 실행하여 탭 이벤트인지 확인합니다

// 时间小于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);
}
로그인 후 복사

작업 후:

에 저장된 상태 정보 지우기 슬라이딩 사이클

dragging = false;
dragState = {};
로그인 후 복사
로그인 후 복사

Summary


전체적으로 구현 원리는 비교적 간단합니다. 슬라이딩은 초기 위치를 기록하기 시작하고 이전 페이지와 다음 페이지에 표시되어야 하는 페이지를 계산합니다. 슬라이딩이 끝나면 이전 페이지와 다음 페이지의 이동 결과에 따라 해당 애니메이션이 실행됩니다.

한 가지 세부 사항은 슬라이딩 중 전환으로 인해 이전 페이지와 다음 페이지가 부자연스럽게 이동하는 것을 방지하기 위해

전환

효과가 비어 있도록 설정된다는 것입니다. 애니메이션 효과가 끝난 후에 추가하세요. 관련 추천:


WeChat 애플릿 탭과 스와이퍼의 결합 효과 구현

vue swiper 컴포넌트 개발 구현

스와이프 사용 방법에 대한 자세한 설명

위 내용은 모바일에서 Swiper를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!