移动端拖拽

最近上线了一个动画效果偏多的小页面,因为上线时间紧张,来不及作优化,可是其中也不乏有一些功能点能够小小的记录一下。
要达到的效果以下图,须要在必定时间内不断向上拖拽图中的圆(圆会不断放大到某个倍数),同时,页面长背景同步以某个和谐的速率下移,拖拽到页面上方正方形的底部,作碰撞检测。
图片描述css

开发的过程当中遇到一些难点:css3

  • 开发时间短的状况下选择什么方案实现

一开始评估的时候,其实没往拖拽这个重点想,尽往游戏框架用canvas画去靠拢,由于动效不少,也涉及到碰撞检测等等的。原本想快速的看看游戏框架,并在脑子里看是否match这个项目的需求,但最后仍是放弃了。怕遇到短期会使人抓狂的坑。最后仍是决定就用原生的touch事件写,这样出了bug也好解决一点。
固然,其实这种决策过程也能暴露不少的问题吧,说明也有很是多的进步空间。
总而言之,无论解决方案是什么,在时间紧张的状况下,至少兜底的方案得先出来。canvas

  • touchmove事件的一些莫名问题

向上提拉的过程,涉及到三个部分的同步动效。背景、圆圈、圆圈的放大。效果出来要和谐的话,就须要不断调试。
解决思路一开始想的很简单,无非是计算出来每一次move的时候手指在屏幕上滑动的距离。根据这个距离,除以必定的系数比例,嗯,一开始是晕着调的。
可是原生的touchmove卡顿严重。致使动效一直不理想。touchmove事件彷佛不是一直触发的。
查到其余人说缘由是由于浏览器

200ms超时致使内核不必定会一直处理touchmove事件,一旦超时会将后续全部的事件转交给UI处理,致使touchmove不会一直触发。系统浏览器也存在一样的问题,为了解决开发者须要,建议开发者在touchstart时调用event.preventDefault,这样就能够保证内核会一块儿触发touchmove事件了

可是调用了preventDefault依然会卡顿。网络上也有不少答案说是要加上passive属性,网络

Passive Event Listeners是Chrome提出的一个新的浏览器特性:Web开发者经过一个新的属性passive来告诉浏览器,当前页面内注册的事件监听器内部是否会调用preventDefault函数来阻止事件的默认行为,以便浏览器根据这个信息更好地作出决策来优化页面性能。当属性passive的值为true的时候,表明该监听器内部不会调用preventDefault函数来阻止默认滑动行为,Chrome浏览器称这类型的监听器为被动(passive)监听器。目前Chrome主要利用该特性来优化页面的滑动性能,因此Passive Event Listeners特性当前仅支持mousewheel/touch相关事件。
引用自: https://juejin.im/post/5b28d6...

clipboard.png

可是发现效果依然不满意,仍是十分卡顿。框架

叹了口气,最后的解决方案就是,仍是去找了一个轻便的手势库hammerjs,用到了其中的pan相关的事件。dom

function () {
        // 绑定手势
        var hammertime = new window.Hammer(document.querySelector('xx'))
        hammertime.get('pan').set({direction: window.Hammer.DIRECTION_ALL}) // 要设定方向才会开启垂直方向的移动
        hammertime.on('panstart', this.touchStart)
        // 上拉
        hammertime.on('panup', this.touchMove)
        hammertime.on('panend', this.touchEnd)
      },

hammerjs却是很方便, 主要用到其中的panup事件,panup的事件对象里,也暴露出了一些本来须要本身计算的参数,这样能够直接拿来使用, 主要使用到y轴的加速度,还有时间差,deltaTime, velocityY函数

clipboard.png

clipboard.png

三个动画须要和谐,大体调整的比例是4:11,若是圆的位置要保持在屏幕中间的话post

// 作边界限定,否则会一次增加过快
if (speed > 4) speed = 4
   yuanSpeed= speed * 4 //圆的速率
   background.bottom -= speed * 11 // 背景的速率
    // 总体增大到1.8倍
    yuan.scaleVal += speed / 110 // 圆总体增大
  • 假重力回落

为了效果更好,须要加上一个若是touchend了,就圆就往回落必定距离的效果。
一开始的思路是在圆的现有高度的基础上*0.98,乘以某个系数。可是会出现一个问题,当每次提拉的距离小于0.98的时候,会出现往上拽不动的问题。因此,最终解决方法是,将这一次滑动全部的move累加的值记录下来,touchend的时候再减掉这个累加值的一半:性能

this.totalMove+= yuanSpeed

toucheEnd: function () {
  // 当次距离的一半
  this.yuanHeight-= this.totalMove / 2
}
  • 如何动态计算圆到盒子底部的距离

因为这个方案涉及不少dom操做,不像canvas那样有xy坐标,方便作碰撞检测。dom操做在计算的过程当中原本就十分消耗性能。

好比遇到一个问题:

圆的背景图片到盒子顶部有必定的距离,圆自己会不断放大,这个空白的距离怎么计算而且适配全部的屏幕呢?
加入1334设计稿上,这个空白的距离是50px,那么如何动态计算呢?
解决的办法也有点笨,但管用吧。就是用圆放大后的高度/原来圆背景图的高度得出的比例 * 50

var yuanHeight= yuan.getBoundingClientRect().height // getBoundingClientRect能拿到scale后高度。直接用innerHeight不行
var yuanMargin = (yuanHeight / yuanImg.naturalHeight) * 50 // naturalHeight本来的高度

固然整个计算过程,额,有点费劲

// 主要是圆距离屏幕顶部的高度+超出屏幕部分的高度
// 在这个距离的基础上,减掉或者加上一些其余距离
// maxOffsetTop 是初始的屏幕顶部超出部分
// bottom是不断下移的距离
var top = Math.abs(this.maxOffsetTop - this.bottom) - self.boxBottom + yuanMargin + yuan.getBoundingClientRect().top
  • transition动画闪烁的问题

当碰撞检测成功或者失败的时候都有相关的动画,一开始是直接用css3写,可是会出现奇怪的第一次闪烁的状况。
索性仍是换了一种可能看上去不是十分优雅,可是好用的办法。

animateFn: function () {
    // 图片张数和动画时间 01~30 1s
    var time = 33,  // 动画时间 / 图片张数
      self = this,
      idx = this.animateIndex % 30 // animateIndex 初始值为0
      imgDom
    img= $('divImg img')
    setTimeout(function () {
    // 控制图片显隐
      $(img[idx]).removeClass('displaynone').siblings().addClass('displaynone')
      self.animateIndex++
      // 动画1s后结束
      if (self.animateIndex <= 30) {
        // 调用自身
        self.animateFn()
      } else {
        // 动画结束
      }
    }, time)
  },

固然,以上的解决方法都十分笨拙,确定有不少高性能的办法能够打磨得更好,可是呢,我想一想,无论如何,是一个思考过程,仍是须要记录一下。

相关文章
相关标签/搜索