【wap】图片点击弹出、拖拽及边界回弹效果js

最近在公司的项目上碰到的一个小任务,要求实现移动端图片的点击弹出、拖拽及回弹效果。自己也是新人一枚,实现起来还费了一点时间,现在已成功实现,于是总结一下。

一、思路

思路很简单,但因为是移动端,很多方法事件已经和pc端不一样了,有必要记录一下。

首先是绑定点击事件,弹出图片浮动层。

其次图片层的处理,在pc端一般我们用mousedown、mousemove和mouseup事件。在移动端,则分别对应touchstart、touchmove和touchend。

最后就是对图片处理的算法处理。

二、实现

实现过程也很简单,就是边界回弹费了不少时间,结果无意中实现了,也是相当开心laugh。不过,开心归开心,原理还是要弄懂的。

/**
 * Created by Duanya on 2016/4/1.
 */

$(document).on("click", "img.popup", function(e){
    imgpopup(this.src);
})

function imgpopup(url, tips){
    // 创建容器
    var contain = $("<div></div>"); // 用的jquery,自己感觉原生方法也挺方便的
    contain.attr("id", "dyimgpop");
    contain.click(function () { // 本来打算直接用事件冒泡实现点击关闭的,结果微信
                                // 及 UC 浏览器上不能事件冒泡,不知道为什么。。。
        this.remove();
    })

    // 创建遮罩
    var mask = $("<div></div>");
    mask.attr("id", "imgmask");
    mask.css({
        "position": "fixed",
        "z-index": "9999",
        "top": "0",
        "left": "0",
        "width": "100%",
        "height": "100%",
        "background-color": "#666",
        "opacity": "0.9"
    });
    mask.on("click", function(){ // 绑定遮罩点击事件,理由如上
        contain.remove();
    })

    // 创建img元素
    var img = document.createElement("img"); // 直接创建元素对象,感觉不是很好就是了

    // 定义属性
    img.vtimer = null;
    img.xtimer = null;

    $(img).css({
        "position": "fixed",
        "z-index": "10000",
        "display": "block"
    });
    // 图片居中方法
    img.center = function () {
        var winH = document.documentElement.clientHeight;
        var winW = document.documentElement.clientWidth;
        var conW = this.offsetWidth;
        var conH = this.offsetHeight;
        this.style.top = (winH - conH) / 2 + "px";
        this.style.left = (winW - conW) / 2 + "px";
    }

    // 添加手势操作
    $(img).on("touchstart", function (e) {
        clearInterval(this.vtimer); // 清除惯性滑动

        e.preventDefault();

        this.isTap = true;  // 判断是不是点击事件,因为默认点击已被干掉,看上面,
                            // 理由是微信浏览器不这么做就不能响应touch事件。。。
                            
        this.isOneS = e.originalEvent.targetTouches.length > 1 ? false : true; 
        // 判断是不是单指操作。

        this.oldW = this.offsetWidth;
        this.oldH = this.offsetHeight;
        this.oldL = this.offsetLeft;
        this.oldT = this.offsetTop;

        this.x1 = e.originalEvent.targetTouches[0].clientX;
        this.y1 = e.originalEvent.targetTouches[0].clientY;
        if(!this.isOneS){ // 计算双指缩放必要数值
            this.x2 = e.originalEvent.targetTouches[1].clientX;
            this.y2 = e.originalEvent.targetTouches[1].clientY;
            this.ox = (this.x1+this.x2)/2;
            this.oy = (this.y1+this.y2)/2;
            this.s = Math.sqrt(Math.pow((this.x1-this.x2),2) + Math.pow((this.y1-this.y2),2));

            this.scaleX = (this.ox - this.oldL) / this.oldW;
            this.scaleY = (this.oy - this.oldT) / this.oldH;
        } else {
            // 单指拖动初始位置
            this.lastX = e.originalEvent.targetTouches[0].clientX;
            this.lastY = e.originalEvent.targetTouches[0].clientY;
        }

    }).on("touchmove", function (e) {
        this.isTap = false; // 一旦发生移动,单击判断就失效
        if(!this.isOneS){
            var nx1 = e.originalEvent.targetTouches[0].clientX;
            var ny1 = e.originalEvent.targetTouches[0].clientY;
            var nx2 = e.originalEvent.targetTouches[1].clientX;
            var ny2 = e.originalEvent.targetTouches[1].clientY;
            var ns = Math.sqrt(Math.pow((nx1-nx2),2) + Math.pow((ny1-ny2),2));

            var t = ns / this.s;
            
            // 设定最小缩放
            if(t < 1 && this.minHeight && this.offsetHeight <= this.minHeight)
                return false;
            if(t < 1 && this.minWidth && this.offsetWidth <= this.minWidth)
                return false;

            this.style.width = this.oldW * t + "px";
            this.style.height = this.oldH * t + "px";
            
            // 下面很长的一段纯粹是为了图片小于屏幕时的居中效果。。。
            if(t < 1){
                if(this.offsetWidth < $(document.body).get(0).clientWidth){
                    this.style.left = ($(document.body).get(0).clientWidth - this.offsetWidth) / 2 + "px";
                } else if(this.oldL - (this.offsetWidth - this.oldW) * this.scaleX > 0){
                    this.style.left = "0px";
                } else if(this.oldL - (this.offsetWidth - this.oldW) * this.scaleX + this.offsetWidth < $(document.body).get(0).clientWidth){
                    this.style.left = $(document.body).get(0).clientWidth - this.offsetWidth + "px";
                } else {
                    this.style.left = this.oldL - (this.offsetWidth - this.oldW) * this.scaleX + "px";
                }

                if(this.offsetHeight < $(document.body).get(0).clientHeight){
                    this.style.top = ($(document.body).get(0).clientHeight - this.offsetHeight) / 2 + "px";
                } else if(this.oldT - (this.offsetHeight - this.oldH) * this.scaleY > 0){
                    this.style.top = "0px";
                } else if(this.oldT - (this.offsetHeight - this.oldH) * this.scaleY + this.offsetHeight < $(document.body).get(0).clientHeight){
                    this.style.top = $(document.body).get(0).clientHeight - this.offsetHeight + "px";
                } else {
                    this.style.top = this.oldT - (this.offsetHeight - this.oldH) * this.scaleY + "px";
                }
            } else {
                this.style.left = this.oldL - (this.offsetWidth - this.oldW) * this.scaleX + "px";
                this.style.top = this.oldT - (this.offsetHeight - this.oldH) * this.scaleY + "px";
            }
        } else {
            clearTimeout(this.xtimer);
            this.speedX = e.originalEvent.targetTouches[0].clientX - this.lastX;
            this.speedY = e.originalEvent.targetTouches[0].clientY - this.lastY;
            this.lastX = e.originalEvent.targetTouches[0].clientX;
            this.lastY = e.originalEvent.targetTouches[0].clientY;
            var self = this;
            this.xtimer = setTimeout(function () { // 设定速度超时清零
                self.speedX = 0;
                self.speedY = 0;
            }, 300);
            
            var nx1 = e.originalEvent.targetTouches[0].clientX;
            var ny1 = e.originalEvent.targetTouches[0].clientY;

           // 防止拖拽超界
            if(nx1 > this.x1){
                if(this.oldL + nx1 - this.x1 <= 0)
                    this.style.left = this.oldL + nx1 - this.x1 + "px";
            } else if(nx1 < this.x1){
                this.oldL + nx1 - this.x1 + this.oldW >= $(document.body).get(0).clientWidth ? this.style.left = this.oldL + nx1 - this.x1 + "px" : false;
            }

            if(ny1 > this.y1){
                this.oldT + ny1 - this.y1 <=0 ? this.style.top = this.oldT + ny1 - this.y1 + "px" : false;
            } else if(ny1 < this.y1){
                this.oldT + ny1 - this.y1 + this.oldH >= $(document.body).get(0).clientHeight ? this.style.top = this.oldT + ny1 - this.y1 + "px" : false;
            }
        }
    }).on("touchend", function(){
        if(this.isTap){
            contain.remove();
        } else {
            this.move();
        }
    })
    
    // 惯性运动
    img.move = function () {
        var self = this;
        clearTimeout(self.xtimer);
        clearInterval(self.vtimer);
        this.vtimer=setInterval(function(){
            // 图片小于屏幕不允许惯性移动
            if(self.offsetHeight <= $(document.body).get(0).clientHeight)
                self.speedY = 0;
            if(self.offsetWidth <= $(document.body).get(0).clientWidth)
                self.speedX = 0;

            var t = self.offsetTop;
            var l = self.offsetLeft;

            self.speedY *= 0.96;
            self.speedX *= 0.96;

           // 下面的原理就是,一旦图片惯性移动超界,直接设置为边界值,因为速度是不断减小的,所以会产生回弹效果
            if(self.offsetHeight > $(document.body).get(0).clientHeight && t < $(document.body).get(0).clientHeight - self.offsetHeight){
                t = $(document.body).get(0).clientHeight - self.offsetHeight;
            }
            if(self.offsetHeight > $(document.body).get(0).clientHeight && t > 0){
                t = 0;
            }
            if(self.offsetWidth > $(document.body).get(0).clientWidth && l + self.speedX <= $(document.body).get(0).clientWidth - self.offsetWidth){
                if($(document.body).get(0).clientWidth - self.offsetWidth - l > 0){
                    l = $(document.body).get(0).clientWidth - self.offsetWidth;
                }
            }
            if(self.offsetWidth > $(document.body).get(0).clientWidth && l + self.speedX >= 0){
                if(l > 0){
                    l = 0;
                }
            }
            // 想要产生回弹效果,速度叠加必须要在后面
            t += self.speedY;
            l += self.speedX;

            self.style.left = l + "px";
            self.style.top = t + "px";

            if(Math.abs(self.speedX) < 1)
                self.speedX = 0;
            if(Math.abs(self.speedY) < 1)
                self.speedY = 0;
            if(self.speedX == 0 && self.speedY == 0){
                clearInterval(self.vtimer);
            }
        },10);
    }
    
    img.onload = function(){
        var winH = $(document.body).get(0).clientHeight;
        var winW = $(document.body).get(0).clientWidth;
        var imgW = this.naturalWidth;
        var imgH = this.naturalHeight;
        if(imgH > winH || imgH > winH){
            if(winH/winW > imgH/imgW){
                this.style.width = winW + "px";
                this.minWidth = winW;
            } else {
                this.style.height = winH + "px";
                this.minHeight = winH;
            }
        }
        contain.append(this);
        this.center();
    };
    img.onerror = function () {
        alert("图片加载错误...");
        contain.remove(); // 删除遮罩
    }

    contain.append(mask);
    $(document.body).append(contain);

    // 加载图片
    img.src = url;
}

代码已经展示了,效果还行,也有点不精致,等技术提升了再修改吧。

微信就是奇葩,运行效果卡的要死。。。