因为最近公司的项目上有用到图片缩放及滚动的效果,而swiper在该项目上有一些问题,所以就决定自己写一个图片的缩放和滚动效果,以及回弹效果, 用的是面向对象的方式封装的, 因为是移动端的,所以用的
touch方法,HTML结构样式先放出来
<div id="box">
<div id="mask"></div>
<div id="content">
<img src="./11.jpg" alt="">
<div>
<table>
<tr>
<th>姓名</th>
<th>手机号</th>
<th>年龄</th>
<th>性别</th>
</tr>
<tr id="tr">
<td>六六六</td>
<td>18437928787</td>
<td>18</td>
<td>女</td>
</tr>
</table>
</div>
</div>
</div>
* {
padding: 0;
margin: 0;
}
body,html {
width: 100%;
height: 100%;
}
#box {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
#content {
position: absolute;
top: 0;
left: 0;
}
#mask {
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
height: 100%;
}
img {
width: 100%;
display: block;
}
主要的缩放和滑动区域是content盒子,所以content里面随便放什么都无所谓, 这里的mask是一个遮罩层,层级大于content, 所有事件都绑定在了mask上, 这样做是因为, 如果content里的元素又小又多, 那么当双指进行缩放时,两个手指出碰到的不是同一元素, 获取到的e.target的目标会不一样,这样就会出问题, 所以设置这样一个遮罩层
function Scroll(ele) {
this.box = document.querySelector(ele)
this.content = document.querySelector('#content')
this.mask = document.querySelector('#mask')
this.data = {
width: this.content.offsetWidth, //获取内容区域的初始宽度
height: this.content.offsetHeight, //获取内容区域的初始高度
currentWidth: this.content.offsetWidth, //获取内容区域当前宽度
currentHeight: this.content.offsetHeight, //获取内容区域当前高度
startXA: 0, //第一根手指的起始位置
startYA: 0,
endXA: 0, //第一根手指的结束位置
endYA: 0,
startXB: 0, //第二根手指的起始位置
startYB: 0,
endXB: 0, //第二根手指的结束位置
endYB: 0,
currentMultiple: 1, //当前的缩放倍数
multiple:1, //缩放倍数
positionY: 0, //在Y轴的位置
positionX: 0, //在X轴的位置
startTime: 0, //手指触碰的起始时间
endTime: 0, //手指离开的结束时间
centreX: 0, //缩放位置的X轴坐标
centreY: 0, //缩放位置的Y轴坐标
bool: false, //用来判断是否是双指操作
num: 0, //用来判断屏幕上的手指数
timer: null //定时器
}
this.mask.ontouchstart = (e) => {
this.touchBegin(e)
}
this.mask.ontouchmove = (e) => {
this.touching(e)
}
this.mask.ontouchend = (e) => {
this.touchFinish(e)
}
}
当触摸事件触发时,调用touchBegin事件
Scroll.prototype.touchBegin = function(e) {
this.data.num++ //当调用这个方法时,num自增
e.preventDefault() //阻止浏览器滑动
switch (e.targetTouches.length) {
case 1:
this.data.startXA = e.targetTouches[0].pageX
this.data.startYA = e.targetTouches[0].pageY
this.data.startTime = new Date().getTime()
break
case 2:
this.data.bool = true //如果是双指操作,将bool的值置为true
this.data.startXA = e.targetTouches[0].pageX
this.data.startYA = e.targetTouches[0].pageY
this.data.startXB = e.targetTouches[1].pageX
this.data.startYB = e.targetTouches[1].pageY
this.data.centreY = Math.abs((-this.data.positionY + this.data.startYA) - (-this.data.positionY + this.data.startYB)) / 2 + (-this.data.positionY + this.data.startYA)
this.data.centreX = Math.abs((-this.data.positionX + this.data.startXA) - (-this.data.positionX + this.data.startXB)) / 2 + (-this.data.positionX + this.data.startXA)
}
}
当手指滑动时, 触发touching事件
Scroll.prototype.touching = function(e) {
switch (e.targetTouches.length) {
case 1:
if(!this.data.bool) { //如果bool是false, 也就是说不是双手操作,或者刚刚结束双手操作,但只离开了一只手指
this.data.endXA = e.targetTouches[0].pageX
this.data.endYA = e.targetTouches[0].pageY
let x= this.data.endXA - this.data.startXA+this.data.positionX //content应在X轴上移动的距离
let y= this.data.endYA - this.data.startYA+this.data.positionY //content应在Y轴上移动的距离
if(this.data.currentWidth === this.data.width) { //判断当前的宽度与初始化时的宽度大小是否相等 , 如果相等,也就是说明没有进行缩放操作, 那么就只在Y轴进行移动
this.content.style.transform = "translate(0,"+y+"px) scale("+this.data.multiple+")"
} else { //否则,就在X,Y轴上进行移动
this.content.style.transform = "translate("+x+"px,"+y+"px) scale("+this.data.multiple+")"
}
}
break
case 2:
this.data.endXA = e.targetTouches[0].pageX
this.data.endYA = e.targetTouches[0].pageY
this.data.endXB = e.targetTouches[1].pageX
this.data.endYB = e.targetTouches[1].pageY
this.content.style.transformOrigin = this.data.centreX+"px "+this.data.centreY+"px" //设置缩放中心点坐标
let multipleOne = Math.sqrt(Math.pow(Math.abs(this.data.startXA - this.data.startXB),2) + Math.pow(Math.abs(this.data.startYA - this.data.startYB),2)) //获取两只手指的起始直线距离
let multipleTwo = Math.sqrt(Math.pow(Math.abs(this.data.endXA - this.data.endXB),2) + Math.pow(Math.abs(this.data.endYA - this.data.endYB),2)) //获取滑动时两只手指的直线距离
let multiple = multipleTwo / multipleOne * this.data.currentMultiple //获取放大倍数
if(multiple < 1 ) { //规定放大倍数,不能大于2且不能小于1
multiple = 1
}
if(multiple > 2 ) {
multiple = 2
}
this.data.multiple = multiple
this.content.style.transform = "translate("+this.data.positionX+"px,"+this.data.positionY+"px) scale("+this.data.multiple+")"
this.data.currentWidth = this.data.multiple * this.content.offsetWidth
this.data.currentHeight = this.data.multiple * this.content.offsetHeight
}
}
手指离开时,调用touchFinish事件
Scroll.prototype.touchFinish = function(e) {
let limitTop = this.data.centreY*this.data.multiple - this.data.centreY //获取上下左右的边界值(当图片进行缩放时,边界值也会发生改变,就是缩放中心到边界的距离*缩放倍数 - 缩放中心到边界的距离)
let limitDown = (this.content.offsetHeight - this.data.centreY)*this.data.multiple - (this.content.offsetHeight - this.data.centreY)
let limitLeft = this.data.centreX*this.data.multiple - this.data.centreX
let limitRight = (this.content.offsetWidth - this.data.centreX)*this.data.multiple - (this.content.offsetWidth - this.data.centreX)
this.data.num-- //num自减
this.data.currentMultiple = this.data.multiple
let down = this.box.offsetHeight - this.content.offsetHeight
let right = this.box.offsetWidth - this.content.offsetWidth
this.data.endTime = new Date().getTime()
this.data.positionY = this.content.style.transform.slice(this.content.style.transform.indexOf(',')+1,this.content.style.transform.indexOf(')')-2)-0
this.data.positionX = this.content.style.transform.slice(this.content.style.transform.indexOf('(')+1,this.content.style.transform.indexOf(',')-2)-0
if(this.data.endTime - this.data.startTime <300 && Math.abs(this.data.startYA-e.changedTouches[0].pageY)>50) { //如果手指滑动时间小于300毫秒,并且距离大于50,则触发滑动缓冲效果
let p = (this.data.endYA-this.data.startYA) * (1/(this.data.endTime - this.data.startTime)) * 100 + this.data.positionY //缓冲的距离
this.Drop(this.content,p,this.data.positionX) //调用移动动画
}
this.data.timer = setTimeout(()=>{ //设置定时器,当手指离开200毫秒后触发边界判断
if(this.data.positionY >= limitTop ){ //如果大于上边界,调用回弹动画,再判断左右边界是否超出
this.Drop(this.content, limitTop,this.data.positionX)
setTimeout(()=> {
if(this.data.positionX >= limitLeft ){
this.Drop(this.content,limitTop,limitLeft)
}
if(this.data.positionX <= right-limitRight) {
this.Drop(this.content,limitTop,right-limitRight)
}
},200)
}
if(this.data.positionY <= -limitDown+down) {
this.Drop(this.content,-limitDown+down,this.data.positionX)
setTimeout(()=>{
if(this.data.positionX >= limitLeft ){
this.Drop(this.content,-limitDown+down,limitLeft)
}
if(this.data.positionX <= right-limitRight) {
this.Drop(this.content,-limitDown+down,right-limitRight)
}
},200)
}
if(this.data.positionX >= limitLeft ){
this.Drop(this.content,this.data.positionY,limitLeft)
}
if(this.data.positionX <= -limitRight+right) {
this.Drop(this.content,this.data.positionY,right-limitRight)
}
},200)
if(!this.data.num) { //当两只手指都离开后, 将bool设置为false
this.data.bool = false
}
}
移动动画方法
Scroll.prototype.Drop = function(ele, positionY,positionX) { //移动动画方式, 传入参数是目标元素, Y轴移动距离, X轴移动距离
clearInterval(ele.timer);
ele.timer = setInterval(() => {
this.data.positionY = this.content.style.transform.slice(this.content.style.transform.indexOf(',')+1,this.content.style.transform.indexOf(')')-2)-0
this.data.positionX = this.content.style.transform.slice(this.content.style.transform.indexOf('(')+1,this.content.style.transform.indexOf(',')-2)-0
var stepY = (positionY - this.data.positionY)/10
var stepX = (positionX - this.data.positionX)/10
stepY = stepY > 0 ? Math.ceil(stepY) : Math.floor(stepY);
stepX = stepX > 0 ? Math.ceil(stepX) : Math.floor(stepX);
let longY = this.data.positionY + stepY
let longX = this.data.positionX + stepX
ele.style.transform = "translate("+longX+"px,"+longY+"px) scale("+this.data.multiple+")";
if (Math.abs(positionY - this.data.positionY) <= Math.abs(stepY) && Math.abs(positionX - this.data.positionX) <= Math.abs(stepX)) {
ele.style.transform = "translate("+positionX+"px,"+positionY+"px) scale("+this.data.multiple+")";
this.data.positionX = positionX
this.data.positionY = positionY
clearInterval(ele.timer);
}
}, 10)
}