모바일에서 Swiper를 사용하는 방법
최근에는 모바일 개발 시 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('is-active'); if (index === defaultIndex) { // 给激活的子页面加上激活class child.classList.add('is-active'); } }); 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 === 'prev') { // 重置上一个页面的水平位移为 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 === 'next') { 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('tap'); } }
방향 결정
// 如果事件间隔小于300ms但是滑出屏幕,直接返回 if (dragDuration < 300 && dragState.currentLeft === undefined) return; // 如果事件间隔小于300ms 或者 滑动位移超过屏幕宽度 1/2, 根据位移判断方向 if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) { towards = offsetLeft < 0 ? 'next' : 'prev'; } // 如果非连续,当处于第一页,不会出现上一页,当处于最后一页,不会出现下一页 if (!continuous) { if ((index === 0 && towards === 'prev') || (index === pageCount - 1 && towards === 'next')) { 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 = 'block'; translate(prevPage, -pageWidth); } if (nextPage) { nextPage.style.display = 'block'; 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 === 'prev') { if (index > 0) { newIndex = index - 1; } if (continuous && index === 0) { newIndex = pageCount - 1; } } else if (towards === 'next') { 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('is-active'); newPage.classList.add('is-active'); index = newIndex } if (isDone) { end(); } if (prevPage) { prevPage.style.display = ''; } if (nextPage) { nextPage.style.display = ''; } } setTimeout(function() { // 向后滑动 if (towards === 'next') { isDone = true; before(currentPage); // 当前页执行动画,完成后执行callback translate(currentPage, -pageWidth, speed, callback); if (nextPage) { // 下一面移动视野中 translate(nextPage, 0, speed) } } else if (towards === 'prev') { 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 !== 'undefined') { 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
전체적으로 구현 원리는 비교적 간단합니다. 슬라이딩은 초기 위치를 기록하기 시작하고 이전 페이지와 다음 페이지에 표시되어야 하는 페이지를 계산합니다. 슬라이딩이 끝나면 이전 페이지와 다음 페이지의 이동 결과에 따라 해당 애니메이션이 실행됩니다.
한 가지 세부 사항은 슬라이딩 중 전환으로 인해 이전 페이지와 다음 페이지가 부자연스럽게 이동하는 것을 방지하기 위해
전환효과가 비어 있도록 설정된다는 것입니다. 애니메이션 효과가 끝난 후에 추가하세요. 관련 추천:
위 내용은 모바일에서 Swiper를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











appdata 폴더를 D 드라이브로 이동할 수 있나요? 컴퓨터 사용이 늘어나면서 점점 더 많은 사용자의 개인 데이터와 애플리케이션이 컴퓨터에 저장됩니다. Windows 운영 체제에는 사용자의 애플리케이션 데이터를 저장하는 데 사용되는 appdata 폴더라는 특정 폴더가 있습니다. 많은 사용자들은 데이터 관리 및 보안 고려 사항을 위해 이 폴더를 D 드라이브나 다른 디스크로 이동할 수 있는지 궁금해합니다. 이번 글에서는 이 문제에 대해 논의하고 몇 가지 해결책을 제시하겠습니다. 먼저, 나에게

7월 23일 뉴스에 따르면 블로거 Digital Chat Station은 Xiaomi 15 Pro의 배터리 용량이 6000mAh로 증가하고 90W 유선 플래시 충전을 지원한다는 소식을 전했습니다. 이는 Xiaomi 디지털 시리즈 중 가장 큰 배터리를 탑재한 Pro 모델이 될 것입니다. Digital Chat Station은 이전에 Xiaomi 15Pro의 배터리가 초고에너지 밀도를 가지며 실리콘 함량이 경쟁 제품보다 훨씬 높다고 밝혔습니다. 2023년 실리콘 기반 배터리가 대규모로 테스트된 후, 2세대 실리콘 양극 배터리가 업계의 미래 발전 방향으로 확인되어 올해 직접 경쟁의 정점을 맞이할 것입니다. 1. 실리콘의 이론적인 그램 용량은 4200mAh/g에 도달할 수 있으며 이는 흑연의 그램 용량의 10배 이상입니다(흑연의 이론적인 그램 용량은 372mAh/g입니다). 음극의 경우 리튬이온 삽입량이 최대에 도달했을 때의 용량이 이론 그램 용량으로 동일 중량 하에서

JSP 주석 분류 및 활용 분석 JSP 주석은 두 가지 유형으로 구분됩니다. 한 줄 주석: 로 끝나는 코드로 한 줄만 주석을 달 수 있습니다. 여러 줄 주석: /*로 시작하고 */로 끝나는 경우 여러 줄의 코드에 주석을 달 수 있습니다. 한 줄 주석 예 여러 줄 주석 예/**여러 줄 주석입니다*여러 줄의 코드에 주석을 달 수 있습니다*/JSP 주석 사용 JSP 주석을 사용하여 JSP 코드에 주석을 달면 읽기 쉬워집니다.

Microsoft는 최신 Windows 11 버전에서 PhoneLink의 이름을 MobileDevice로 변경했습니다. 이 변경을 통해 사용자는 프롬프트를 통해 모바일 장치에 대한 컴퓨터 액세스를 제어할 수 있습니다. 이 문서에서는 모바일 장치의 액세스를 허용하거나 거부하는 컴퓨터 설정을 관리하는 방법을 설명합니다. 이 기능을 사용하면 모바일 장치를 구성하고 컴퓨터에 연결하여 문자 메시지를 보내고 받고, 모바일 애플리케이션을 제어하고, 연락처를 보고, 전화를 걸고, 갤러리를 보는 등의 작업을 수행할 수 있습니다. 휴대폰을 PC에 연결하는 것이 좋은 생각입니까? 휴대폰을 Windows PC에 연결하는 것은 편리한 옵션이므로 기능과 미디어를 쉽게 전송할 수 있습니다. 모바일 장치를 사용할 수 없을 때 컴퓨터를 사용해야 하는 사람들에게 유용합니다.

WPS는 일반적으로 사용되는 사무용 소프트웨어 제품군이며 WPS 테이블 기능은 데이터 처리 및 계산에 널리 사용됩니다. WPS 테이블에는 두 날짜 사이의 시차를 계산하는 데 사용되는 매우 유용한 함수인 DATEDIF 함수가 있습니다. DATEDIF 함수는 영어 단어 DateDifference의 약어입니다. 구문은 다음과 같습니다. DATEDIF(start_date,end_date,unit) 여기서 start_date는 시작 날짜를 나타냅니다.

C 언어에서 종료 기능을 사용하려면 특정 코드 예제가 필요합니다. C 언어에서는 프로그램 초기에 프로그램 실행을 종료하거나 특정 조건에서 프로그램을 종료해야 하는 경우가 많습니다. C 언어에서는 이 기능을 구현하기 위해 exit() 함수를 제공합니다. 이 기사에서는 exit() 함수의 사용법을 소개하고 해당 코드 예제를 제공합니다. Exit() 함수는 C 언어의 표준 라이브러리 함수로 헤더 파일에 포함되어 있습니다. 그 기능은 프로그램 실행을 종료하는 것이며 정수를 취할 수 있습니다.

Python 함수 소개: abs 함수 사용법 및 예 1. abs 함수 사용법 소개 Python에서 abs 함수는 주어진 값의 절대값을 계산하는 데 사용되는 내장 함수입니다. 숫자 인수를 허용하고 해당 숫자의 절대값을 반환할 수 있습니다. abs 함수의 기본 구문은 다음과 같습니다: abs(x) 여기서 x는 정수 또는 부동 소수점 숫자일 수 있는 절대값을 계산하기 위한 숫자 매개변수입니다. 2. abs 함수의 예 아래에서는 몇 가지 구체적인 예를 통해 abs 함수의 사용법을 보여줍니다. 예 1: 계산

Python 함수 소개: isinstance 함수의 사용법 및 예 Python은 프로그래밍을 보다 편리하고 효율적으로 만들기 위해 많은 내장 함수를 제공하는 강력한 프로그래밍 언어입니다. 매우 유용한 내장 함수 중 하나는 isinstance() 함수입니다. 이 기사에서는 isinstance 함수의 사용법과 예를 소개하고 구체적인 코드 예를 제공합니다. isinstance() 함수는 객체가 지정된 클래스나 유형의 인스턴스인지 여부를 확인하는 데 사용됩니다. 이 함수의 구문은 다음과 같습니다
