最近在公司的项目上碰到的一个小任务,要求实现移动端图片的点击弹出、拖拽及回弹效果。自己也是新人一枚,实现起来还费了一点时间,现在已成功实现,于是总结一下。
一、思路
思路很简单,但因为是移动端,很多方法事件已经和pc端不一样了,有必要记录一下。
首先是绑定点击事件,弹出图片浮动层。
其次图片层的处理,在pc端一般我们用mousedown、mousemove和mouseup事件。在移动端,则分别对应touchstart、touchmove和touchend。
最后就是对图片处理的算法处理。
二、实现
实现过程也很简单,就是边界回弹费了不少时间,结果无意中实现了,也是相当开心。不过,开心归开心,原理还是要弄懂的。
/**
* 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;
}
代码已经展示了,效果还行,也有点不精致,等技术提升了再修改吧。
微信就是奇葩,运行效果卡的要死。。。