>  기사  >  웹 프론트엔드  >  JS는 무제한 스와이프로 사진의 전체 화면 탐색을 실현합니다.

JS는 무제한 스와이프로 사진의 전체 화면 탐색을 실현합니다.

巴扎黑
巴扎黑원래의
2017-05-27 10:46:181479검색

 무한 로딩 전략

무한 스와이프이기 때문에 사진을 다 담을 수는 없습니다. 긁는 효과가 있어야 하므로 현재 이미지의 왼쪽과 오른쪽을 미리 로드해야 합니다. 따라서 세 장의 사진을 창으로 사용하고 회전 전략을 사용하여 무한 스와이프 목록을 구현할 수 있습니다.

<p class="lightbox">
  <p class="container">
    <p class="lightbox-item prev"></p>
    <p class="lightbox-item current"></p>
    <p class="lightbox-item next"></p>
  </p>
</p> 

 .lightbox 전체 화면 레이아웃인 .lightbox-item에는 이전 사진, 현재 사진, 다음 사진이 포함됩니다. 사진을 스와이프할 때마다 다음 사진을 이전 사진으로, 현재 사진을 이전 사진으로, 원래 이전 사진을 다음 사진으로 변경하고 다음 사진 리소스를 미리 로드합니다.

여기에 .container의 추가 레이어가 추가되어 모든 이미지를 래핑합니다. 이런 식으로 그림 전체를 슬라이드해야 할 때 애니메이션을 적용할 수 있습니다.

레이아웃 스타일

.lightbox를 전체 화면으로 설정하고 .prev를 현재 화면 왼쪽에 배치합니다. 화면 오른쪽 옆에 .

.lightbox, .container .lightbox-item{
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    background-color: #000;
}
.container{
    position: absolute;
}
.lightbox-item{
    /* 我们用背景图来显示图片 */
    position: absolute;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
}
.lightbox-item.prev{
    left: -100%;
    right: 100%;
}
.lightbox-item.next{
    left: 100%;
    right: -100%;
}

일부 브라우저(예: 특정 Samsung 내장 브라우저)에서는 페이지 콘텐츠가 실제로 페이지 너비의 3배인 것을 확인할 수 있습니다. 그래서 세 장의 사진이 모두 표시되도록 페이지가 넓어졌습니다. 오버플로를 설정하면 이 문제를 해결할 수 있습니다.

.lightbox{
    overflow: hidden;
}

터치 이벤트 바인딩

그림 스와이프 효과의 핵심은 다음에 있습니다. 사용자 터치 이벤트는 전체 화면 탐색이므로 창에 직접 바인딩될 수 있습니다. 하지만 창에 바인딩할 때는 충돌과 바인딩 문제에 주의해야 합니다. 등록한 기능을 .off하거나 네임스페이스를 추가할 수 있습니다. 예:

$(window)
    .on(&#39;mouseup.lightbox touchend.lightbox&#39;, onTouchEnd)
    .on(&#39;mousemove.lightbox touchmove.lightbox&#39;, onTouchMove)
    .on(&#39;mousedown.lightbox touchstart.lightbox&#39;, onTouchBegin)
$(window)
    .off(&#39;mouseup.lightbox touchend.lightbox&#39;)
    .off(&#39;mousemove.lightbox touchmove.lightbox&#39;)
    .off(&#39;mousedown.lightbox touchstart.lightbox&#39;)

6 주요 이벤트는 다음과 같습니다:

  • mousedown, mousemove, mouseup: 마우스 누르기, 이동 및 휴식

  •  touchbegin, touchmove, touchend: 터치 누르고 이동하고 나가세요.

그림 슬라이딩 애니메이션

사실 그림은 손가락처럼 애니메이션이 되지 않습니다 이동하면 터치무브할 때 위치를 업데이트하기만 하면 됩니다.  

// 起始位置,划动距离
var beginX, translateX;
function onTouchBegin(e){
    beginX = getCursorX(e);
}
function getCursorX(e) {
    // 如果是鼠标事件
    if ([&#39;mousemove&#39;, &#39;mousedown&#39;].indexOf(e.type) > -1) {
        return e.pageX;
    }
    // 如果是触摸事件
    return e.changedTouches[0].pageX;
}
function onTouchMove(e){
    translateX = getCursorX(e) - beginX;
    $(&#39;.container&#39;)
        .attr(&#39;transform:translate3d(&#39; + translateX + &#39;)&#39;);
        .attr(&#39;-webkit-transform:translate3d(&#39; + translateX + &#39;)&#39;);
}

  여기서 -webkit-transform은 안드로이드 UC 브라우저와의 호환성을 위한 것이고 그 외엔 모두 괜찮은 것 같습니다. 또한 Translate3d는 하드웨어 가속을 활성화하는 반면, TranslateX는 그렇지 않습니다. 따라서 일반 Android 브라우저에서 TranslateX의 성능은 매우 낮습니다.

호환성 문제가 발생하면 꼭 Tiansha의 UC에 대해 이야기하고 싶습니다. 그런데 생각해보니 적어도 IE6과 호환될 필요는 없고 너무 불평할 필요도 없더군요.

슬라이딩 대상 결정

위 코드에는 여전히 onTouchEnd가 없습니다. 즉, 사용자가 놓을 것입니다. 일정 거리를 스와이프하면 어떻게 되나요? 스와이프 거리가 충분히 크면 애니메이션을 계속하고 다음 프레임으로 슬라이드하고, 그렇지 않으면 원래 위치로 돌아갑니다. 동시에 스와이프 속도도 감지해야 합니다. 거리가 짧지만 속도가 매우 크면 사진 전환도 수행해야 합니다.

보통 사진을 슬라이드할 때 여기서 세세하게 고려한 적이 없나요?

onTouchBegin에 시작 시간을 기록하고, Calculable인 onTouchEnd에 기록합니다. 속도.

var beginTime, endTime;
function onTouchBegin(e){
    beginTime = Date.now();
}
function onTouchEnd(e){
    endTime = Date.now();
    animateTo(getTarget());
}

여기서 getTarget()은 스와이프할 사진을 계산하는 데 사용되는 반면 animateTo는 스와이프 애니메이션을 호출합니다.  

[code]php code:

function getTarget(){
    // 首先检测划动距离,返回 -1, 0, 1 表示上一张,当前,下一张
    var direction = getDirection(translateX, 0.3 * $(window).width());
    // 如果划动距离检测为0,继续检测速度
    if (direction === 0) {
        var deltaT = Math.max(endTime - beginTime, 1);
        var v = translateX / deltaT;
        direction = getDirection(v, 0.3);
    }
    return [&#39;.prev&#39;, &#39;.current&#39;, &#39;.next&#39;][direction + 1];
}
function getDirection(offset, max) {
    if (offset > max) return -1;
    if (offset < -max) return 1;
    return 0;
}

스트로크 종료 후 애니메이션

스와이프가 완료된 후 .container를 대상 이미지로 슬라이드해야 합니다. 현재 이미지가 대상 이미지로 갑자기 교체되는 것을 방지하기 위해 변환 애니메이션을 대상 위치로 설정한 후 조용히 교체합니다. 다음은 animateTo의 주요 로직입니다.

// 计算划动到的目标图片对应的translateX
var translateX = $(window).width() * (1 - idx);
$(&#39;.container&#39;).animate({
    &#39;transform&#39;: &#39;translate3d(&#39; + translateX + &#39;px, 0px, 0px)&#39;
    &#39;-webkit-transform&#39;: &#39;translate3d(&#39; + translateX + &#39;px, 0px, 0px)&#39;
}, {
    duration: 1000,
    complete: function() {
        // 动画结束后进行图片轮换
        var $wps = $(&#39;.container&#39;).find(&#39;.lightbox-item&#39;);
        var $prev = $wps.filter(&#39;.prev&#39;);
        var $curr = $wps.filter(&#39;.current&#39;);
        var $next = $wps.filter(&#39;.next&#39;);
        if (target === &#39;.prev&#39;) {
            idx--;
            $prev.attr(&#39;class&#39;, &#39;lightbox-item current&#39;);
            $curr.attr(&#39;class&#39;, &#39;lightbox-item next&#39;);
            $next.attr(&#39;class&#39;, &#39;lightbox-item prev&#39;);
            prefetch(&#39;.prev&#39;, idx - 1);
        } else if (target === &#39;.next&#39;) {
            idx++;
            $next.attr(&#39;class&#39;, &#39;lightbox-item current&#39;);
            $curr.attr(&#39;class&#39;, &#39;lightbox-item prev&#39;);
            $prev.attr(&#39;class&#39;, &#39;lightbox-item next&#39;);
            prefetch(&#39;.next&#39;, idx + 1);
        }
        $(.container).css(&#39;transform&#39;, &#39;none&#39;);
        $(.container).css(&#39;-webkit-transform&#39;, &#39;none&#39;);
    }
});

기억하시나요? 다음 사진을 슬라이드한 후 미리 가져와야 합니다. 이런 방식으로 사진을 계속해서 스크롤할 수 있습니다. 프리페치 작업은 서버에서 다음 이미지 주소를 프리페치한 다음 슬라이딩 창에서 가장 오래된 이미지를 바꾸는 것입니다. 구체적인 구현도 서버와 관련되어 있으므로 여기서는 자세히 설명하지 않겠습니다.

  注意!当动画结束时对.prev,.current,.next进行轮换并重置transform。 如果重置为translate3d(0,0,0)则动画仍会继续,页面就会跳一下。 如果重置为none则会非常平滑,同时别忘了-webkit-transform来兼容更多浏览器。

  TouchBegin 的兼容性

  在Android ICS下如果touchbegin和第一个touchmove中都未调用 preventDefault, 后续的touchmove和touchend就不会被触发。 解决办法当然是在onTouchBegin中进行preventDefault(), 然而这样click事件(点击关闭全屏啊!)就不会被触发了:

function onTouchBegin(e) {
    e.preventDefault();
}

  所以我们需要在onTouchMove中来判断这是否是一个Click,并手动触发它的行为。

function onTouchMove(e){
    if(isClick()) onClick(); 

    function isClick() {
        var deltaT = endTime - beginTime;
        var deltaX = Math.abs(translateX);
        // 时间很短,并且移动距离很小,那么应该是个点击!
        return deltaT < 700 && deltaX < 7;
    }
}

  图片渐进载入

  当网速很慢时,连续划动就可能使得旧的图片显示出来(因为预取请求仍未返回)。 常见的一个实践是:立即使用一个已经载入的图片来作为Placeholder, 当目标图片载入后用它替换掉当前的Placeholder。

function loadImage($img, src){
    // 先设置一个Placeholder
    $img.attr(&#39;src&#39;, 
        &#39;data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&#39;);
    // 载入图片到临时变量
    var tmp = new Image();
    tmp.onload = function(){
        // 资源载入后,将资源显示到目标的img
        $img.src = src;
    };
    tmp.src = src;
}

  设置背景图与设置src属性一样,均可以使用该策略。浏览器会复用那个资源。

  图片到底提示

  在第一张图片右划和最后一张图片左划时,应当给出提示。 可以做一张带有提示信息的Placeholder:

$lightbox.attr(&#39;style&#39;, &#39;top:0;left:0;right:0;bottom:0;&#39;);
$lightbox.append($(&#39;<p class="alert-nomore">&#39;).html(&#39;没有更多了..&#39;));

  然后让文字居中:

.lightbox-item .alert-nomore{
    position: absolute;
    text-align: center;
    bottom: 50%;
    left: 0;
    right: 0;
    color: #777;
    font-size: 20px;
}

위 내용은 JS는 무제한 스와이프로 사진의 전체 화면 탐색을 실현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.