移动端人机交互方式同PC端发生了本质的变化,在PC端使用鼠标和键盘进行交互,而移动端使用的是手指的触摸和滑动。PC端开发时主要监听鼠标事件,例如mouseEnter(鼠标进入事件)、mouseMove(鼠标移动事件)、mouseLeave(鼠标离开事件)。而在移动端监听事件为触摸的开始事件,触摸移动事件,以及触摸的结束事件。在本设计中商品详情页通过选项卡来达到切换显示基本信息、商品细节信息以及评论信息。在基本信息通过幻灯效果显示商品的大图,通过手指的滑动来控制幻灯的显示。移动端手势操作效果图:
实现的方法是监听手势的开始事件,记录手指在屏幕上的起始位置。监听手势的移动事件,获取移动中手指的坐标,并将移动的手指坐标减去起始位置的坐标得到手指移动的距离。然后实时地去改变幻灯片的transform的值来达到幻灯移动的效果。最后需要监听手势的结束事件,在该事件的处理函数中得到手指最后的位置,同样使用最后的位置减去手指的起始位置得到最终手指移动的距离,判断手指最终的移动距离是否大于滑动的临界值,本设计在此处设置临界值为100px,如果大于临界值则切换幻灯片,否则不进行切换。
在切换中特殊情况是幻灯处于第一张并向左滑动,以及最后一张并向右滑动,这两种操作都是无效的因为第一张幻灯左侧以及第二张幻灯右侧都是没有图片的。如何友好的告诉用户已近滑动到了第一张或者最后一张是提升用户体验的关键。在本设计中借鉴了各大主流电商平台的主流做法,即在无效滑动情况下降低幻灯的滑动速度让用户知道已近无法移动了。这种做法既保持了操作上的一致性,因为没有直接不响应用户的操作,又清楚明白的告诉了用户已经滑动到了尽头。
在PC端的开发过程中,使用绝对定位改变top与left的值来实现元素的移动。在第一次尝试使用绝对定位来实现,当手指滑动时改变元素的top值与left值,此时出现了元素左右抖动的情况。因为移动端都很好的支持了CSS3与HTML5,所以在移动端开发过程中可以放心地使用CSS3中transform样式,其样式值有translate3d(三个方向的移动)、transformX(横向的移动)、transformY(纵向的移动)、scale(2d缩放转换)、rotate(定义2d的旋转)等等。这里使用translate3d来改变元素位置。transform与绝对定位在表现上的不同是由于浏览器在样式改变时会触发重构与重绘,在改变绝对定位的left与top值是首先触发了重构接着触发重绘,而通过transform只触发了浏览器的重绘。从而使用transform提高了浏览器的处理效率,拥有更好的性能。在实际表现中transform也能够非常完美解决问题。
HTML结构为:
div.piclist-outer div.piclist-inner each singleBig in gmainImgs img(src='#{singleBig}' alt='slide img') div.piclist-signal-wrapper ul.piclist-signal each singleBig in gmainImgs li
因为使用的是Jade,所以为上边的格式。最外层的div是整个滑动区域,类名为.piclist-inner的div包裹着若干个图片,这些图片水平并列。类名为.piclist-signal-wrapper包裹的是当前显示是第几个图片的标志。
下面再来看具体js代码:
// @begin finger js var slidewrap=document.getElementsByClassName("piclist-outer")[0] var slideInner= document . getElementsByClassName("piclist-inner")[0] var page=0 var distanceX=0 var transX=0 var listUl=document.getElementsByClassName("piclist-signal")[0].getElementsByTagName("li") var listUlLen=listUl.length //触摸的开始事件的处理函数 function handlestart (e) { if(e.touches.length!==1){ return } //获取触摸时的横纵坐标 startX=e.touches[0].pageX startY=e.touches[0].pageY slidewrap.addEventListener('touchmove',handlemove,false) // console.log("sx is: "+ startX+"sy is : " + startY) } //触摸的移动事件处理函数 function handlemove (e) { transX = - page * screenWidth // console.log("page:"+(-page * screenWidth)) slideInner.style.transform="translate3d("+transX+"px,0,0)" var touches=e.touches if (touches&&touches.length) { distanceX=startX-touches[0].pageX // console.log("distanceX :"+distanceX) // console.log("handlemove"+transX) //如果当前是第一张幻灯或者最后一张幻灯,则让滑动速度降为原来的三分之一,已到达提示用户已不可滑动。 if ((page == 0 && distanceX < 0) || (page == (listUlLen - 1) && distanceX > 0)) { distanceX=distanceX / 3 } //通过改变transform属性值来达到移动的效果 var transX=-distanceX-page * screenWidth slideInner.style.transform="translate3d("+transX+"px,0,0)" } e.preventDefault() } //触摸的结束事件处理函数 function handleend(argument) { transX=- page * screenWidth - distanceX var move_time =1 var move_dis=8 // console.log("move end") //如果是第一张幻灯并向左滑动或者最后一张幻灯向右滑动的情况下,在滑动结束时显示原先的幻灯不发生改变 if ((page == 0 && distanceX < 0) || (page == (listUlLen - 1) && distanceX > 0)) { transX=- page * screenWidth slideInner.style.transform="translate3d("+transX+"px,0,0)" return } //如果滑动的距离大于100px,则向右切换幻灯片 if (distanceX>=100) { listUl[page].style.background="#e0e0e0" page++ listUl[page].style.background="#c40000" //通过定时器实现滑动的动画效果 var timer=setInterval(function () { slideInner.style.transform="translate3d("+transX+"px,0,0)" transX-=move_dis // console.log("transX:"+transX) // console.log("page:"+-page*(screenWidth + 1)) if (transX <= -page * (screenWidth + 1)) { clearInterval(timer) } },move_time) } //如果滑动距离小于100px,则向左切换幻灯片 else if (distanceX<=-100) { listUl[page].style.background="#e0e0e0" page-- listUl[page].style.background="#c40000" var timer=setInterval(function () { slideInner.style.transform="translate3d("+transX+"px,0,0)" transX+=move_dis // console.log("transX:"+transX) // console.log("page:"+-page*(screenWidth + 1)) if (transX >= -page * (screenWidth + 1)) { clearInterval(timer) } },move_time) } //如果滑动的距离没有达到临界的距离则不切换 else{ transX=- page * screenWidth slideInner.style.transform="translate3d("+transX+"px,0,0)" } }
查看完整项目可以去我的GitHub,欢迎大家的下载、提问和关注哈。