移动开发之手势与双指缩放

ajwa4140 8年前
   <p>上周遇见一个关于双指缩放的问题,同时这个双指缩放也比较常见,于是决定对移动开发手势做一个学习和总结,并给出一个双指缩放的实例,希望对读者提供一些帮助。</p>    <p>先给一个例子,点此查看双指缩放实例</p>    <h2>前言</h2>    <p>当年乔布斯的iphone第一次支持多点触控时,确实惊艳了世人,而现在大部分手机都支持多点触控,这就有了手势这个概念,通过多点触控,形成不同的手势,开发者根据不同手势,提供不同功能,比如双指缩放,是很常见的。</p>    <p>ios的多点触摸</p>    <p>ios的Safari浏览器是第一个支持多点触摸的浏览器,并提供触摸API供开发者使用,到后来Android 3也开始支持多点触摸,各大浏览器也借鉴Safari提出了触摸API,除了个别硬件支持属性外,大致相同,这是值得庆幸的。</p>    <p>IE10的多点触摸</p>    <p>虽然现在市场上WP手机越来越少,但是为了更完整的学习理解手势知识,我们也需要进行学习。IE10支持多点触摸,但是其多点触摸与ios和android不同,ios和android浏览器为多点触摸提供一个包含touches数组的事件,包含所有多点触摸对象,而IE10为多点触摸的每一个触摸点创建一个单独的触摸事件。</p>    <h2>渐进增强与手势</h2>    <p>我们知道手势很方便,用户也很喜欢手势,但是我们也要明白手势并不总是能使用,有些设备或浏览器还是不支持的,所以我们最合适的是基于普通点击和触摸事件交互的页面,将手势作为增强的交互,这就是通常所说的渐进增强思想。</p>    <h2>触摸事件</h2>    <p>既然是要渐进增强,那就必须从基础点击和触摸事件说起,点击事件就不再多说,关于触摸事件,之前也有一篇文章介绍过 <a href="/misc/goto?guid=4959747441974602249" rel="nofollow,noindex">移动开发之轻触与单击事件</a> ,关于触摸四种基础事件和事件对象,可以查看该文章,这里就不重复了,这里要对触摸事件的处理进行说明。</p>    <p>浏览器兼容</p>    <p>通常我们在触摸和手势事件处理函数内会添加CSS3动画处理,这个时候需要进行浏览器兼容处理。</p>    <pre>  <code class="language-python">    var TRANSITION = 'transition';      var TRANSITION_END = 'transitionend';      var TRANSFORM = 'transform';      var TRANSFORM_PROPERTY = 'transform';      var TRANSITION_PROPERTY = 'transition';        if (typeof document.body.style.webkitTransform !== undefined) {          TRANSFORM = 'webkitTransform';          TRANSITION = 'webkitTransition';          TRANSITION_END = 'webkitTransitionEnd';          TRANSFORM_PROPERTY = '-webkit-transform';          TRANSITION_PROPERTY = '-webkit-transition';      }</code></pre>    <h2>双指缩放</h2>    <p>在移动端手势事件中,双指缩放的需求还是很常见的,本节详细阐述,现在大多数移动设备都支持原生的双指缩放,但是这样可能影响页面布局,而且为了更多可控性,很多时候还是会选择自行实现缩放手势功能。</p>    <p>缩放中心</p>    <p>在原生缩放动画,缩放中心通常是图像中心,可以拿起移动设备尝试一番,而在现实需求中,我们通常希望缩放以手势屏幕的两个接触点中心为中心,即缩放中心为接触点中心。</p>    <p>变换原点</p>    <p>在CSS3动画中,如, transform动画旋转,缩放等动画 ,有一个 transform-origin 属性:</p>    <p>The transform-origin property lets you modify the origin for transformations of an element.</p>    <p>transform-origin 属性使得我们可以修改一个元素变换动画的原点。</p>    <p>当我们使用CSS3变换动画进行缩放时,由于变换动画的变换原点和缩放中心(即接触点中心)相互独立,为了不影响缩放中心相对于元素的位置,元素变换缩放时,也应该对缩放中心坐标进行缩放移动,即除了变换缩放元素,还应该对该元素进行额外坐标偏移。</p>    <p>位移偏量</p>    <p>我们希望缩放以接触点中心为中心,元素向四周缩放,假设变换原点为元素左上角,即 transform-origin: 0 0; ,此时元素变换缩放位移均为正值,仅向右下方向缩放,所以缩放中心(其实也就是该元素)应对应向相反方向(左上方向)偏移,值为 缩放坐标值 * (1 - 缩放比例) 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/799a2e03d17ed9e43a4997a0c9340bb9.png"></p>    <p>如上图,原始元素A盒,以接触点M,N两点的中心点L(x, y)为缩放中心点,向四周缩放,其位置应该如图中C盒,由于L(x, y)点的位置是不可控的(用户行为),我们设置元素变换原点 transform-origin: 0 0; ,此时其缩放后位置如B盒,然而我们依然希望达到缩放后形成C盒的位移效果,则我们需要将元素向左上方向位移,假设缩放比例为k, 则元素位移值计算过程如下:</p>    <ul>     <li>1.计算元素A的位移,其实等效于缩放点的位移,也就是计算坐标系缩放后进行的位移;</li>     <li>2. L(x, y) 点,在缩放前坐标系中坐标为 (x, y) ,则缩放k倍后,其在缩放后坐标系中坐标为 (kx, ky) ;</li>     <li>3.计算两点位移距离:      <ul>       <li>水平位移: x – kx;</li>       <li>垂直位移:y – ky;</li>      </ul> </li>     <li>4.得到元素在水平和垂直方向需要偏移的距离。</li>    </ul>    <h2>缩放的实现</h2>    <p>缩放与滚动</p>    <p>我们自行实现的缩放事件需要使用 touchmove 事件,而 touchmove 事件会触发设备的滚动事件,所以要么阻止滚动,要么不让屏幕出现滚动条。</p>    <ul>     <li> <p>阻止滚动</p> <pre>  <code class="language-python">    document.addEventListener('touchmove', function(event){          event.preventDefault();      })</code></pre> </li>     <li> <p>阻止滚动条</p> <pre>  <code class="language-python">    html, body {          height: 100%;      }</code></pre> </li>    </ul>    <p>计算缩放中心</p>    <p>缩放动画以接触点为中心,当我们使用双指手势实现缩放时,这个接触点中心就是两个接触点的中心点:</p>    <pre>  <code class="language-python">    function getOrigin(first, second) {          return {              x: (first.x + second.x) / 2,              y: (first.y + second.y) / 2          };      }      getOrigin({          x: event.touches[0].pageX,           y: event.touches[0].pageY      }, {          x: event.touches[1].pageX,           y: event.touches[1].pageY      });</code></pre>    <p>计算缩放比例</p>    <p>缩放比例如何确定呢,起始触摸两指间距离除以缩放时两指间距离,即缩放比例:</p>    <pre>  <code class="language-python">    function getDistance(start, stop) {          return Math.sqrt(Math.pow((stop.x - start.x), 2) + Math.pow((stop.y - start.y), 2));      }        function getScale(start, stop) {          return getDistance(start[0], start[1]) / getDistance(stop[0], stop[1]);      }</code></pre>    <p>处理触摸事件</p>    <p>要实现缩放功能除了计算相关缩放中心和缩放比例,另外还需要处理触摸事件:</p>    <pre>  <code class="language-python">    var distance = {};      var origin;      var scale = 1;      function handleTouch(e) {          switch(e.type) {              case 'touchstart':                  if (e.touches.length > 1) {                      distance.start = getDistance({                          x: e.touches[0].screenX,                           y: e.touches[0].screenY                        }, {                          x: e.touches[1].screenX,                           y: e.touches[1].screenY                      });                  }                  break;              case 'touchmove':                  if (e.touches.length === 2) {                      origin = getOrigin({                          x: event.touches[0].pageX,                           y: event.touches[0].pageY                      }, {                          x: event.touches[1].pageX,                           y: event.touches[1].pageY                      });                      distance.stop = getDistance({                          x: e.touches[0].screenX,                           y: e.touches[0].screenY                        }, {                          x: e.touches[1].screenX,                           y: e.touches[1].screenY                      });                      scale = distance.stop / distance.start;                      setScaleAnimation(scale, true);                  }                  break;              case 'touchend':                  scale = 1;                  setScaleAnimation(scale);                  break;              case 'touchcancel':                  scale = 1;                  setScaleAnimation(scale);                  break;              default:;          }      }</code></pre>    <p>使用变换</p>    <p>接下来就是使用CSS3动画缩放图片了,代码如下:</p>    <pre>  <code class="language-python">      function setScaleAnimation(scale, animation) {          var transition_animation = '';          var x, y;          if (animation) {              transition_animation = 'none';          } else {              transition_animation = TRANSFORM_PROPERTY + ' 0.3s ease-out';          }          element.style[TRANSITION] = transition_animation;          // 计算位移偏量          x = origin.x + (-origin.x) * scale;// 缩放中心偏移量          y = origin.y + (-origin.y) * scale;            // 缩放和位移          element.style[TRANSFORM] = 'matrix(' + scale + ', 0, 0, ' + scale + ', ' + x + ', ' + y +  ')';      }</code></pre>    <p>本文关于移动开发手势及实现双指缩放介绍到此完结,实现的实例比较粗糙,但原理基本阐述清楚,感兴趣可以自己动手实现,优化;本文很久以前就开篇了,但直到今天才终于完笔,若有不足,也欢迎指正。</p>    <p> </p>    <p>来自:http://blog.codingplayboy.com/2017/04/16/mobile_gesture/</p>    <p> </p>