因为最近公司的项目上有用到图片缩放及滚动的效果,而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) }