频道栏目
读书频道 > 移动开发 > 其他综合 > 移动云计算应用开发入门经典
2.1.2 响应触摸事件绘图
2012-12-26 15:54:02     我来说两句
收藏   我要投稿
本书作为构建移动应用程序和云服务的实用向导,对于那些渴望利用云服务(例如Amazon Web Services)的强大能力作为支持,为移动设备创建跨平台应用程序的Web开发人员来说,是一本必不可少的读物。作者Richard R...  立即去当当网订购

我们已经有一个可运行的移动Web应用程序使用canvas标记来在屏幕上绘制图形,接下来我们将在程序中添加互动性,让应用程序响应手指触摸操作。在下面的示例中,我们会对“曳光弹”程序进行改造,将它变成一个绘图应用程序。通过“曳光弹”程序,你已经对开发环境中的各个环节进行了测试,保证每个环节都没有问题,接下来可以集中精力编写应用程序的逻辑。

当用户点击屏幕时,我们希望在屏幕上画一个点,可以通过画布的arc函数在屏幕上画出一个实心圆来实现这个效果。使用ontouchstart事件处理程序来检测手指在移动设备屏幕上的点击操作。

当用户的手指在屏幕上移动时,我们将对手指移动的路径进行填充。触摸事件为我们提供了一系列连续的点来表示用户手指移动的路径。通过lineTo函数来在这些点之间绘制线,将这些点之间的线连在一起,让用户在屏幕上看到手指移动的曲线。使用ontouchmove事件处理程序,检测手指在移动设备的屏幕上的连续移动。还可以添加一个Clear按钮,让用户清除当前图形并重新开始绘图。

 试一试     使用手指绘图

在下面的示例中,对“曳光弹”程序进行改造,让它变成一个绘图应用程序,使用户可以使用手指在屏幕上绘图。下面是具体的步骤:

(1) 在之前示例使用的draw文件夹中,复制文件draw-tracer.html并将其重命名为draw.html。

(2) 按照下面突出显示的部分对draw.html文件中的代码进行修改:
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport"
    content="user-scalable=no,initial-scale=1.0,maximum-scale=1.0" />
  <style>
  body { padding:10px; margin:0px; background-color: #ccc; }
  #main { margin: 10px auto 0px auto; }
  </style>
 
  <script src="draw.js"></script>
</head>
<body>
<button id="clear">clear</button><br>
<canvas id="main" width="300" height="300"></canvas>
</body>
</html>

代码片段draw/draw.html

第一处修改是加载draw.js文件,而不是加载draw.tracer.js文件。第二处修改是定义一个Clear按钮。

(3) 在draw文件夹下新建一个文件,命名为draw.js。使用代码编辑器,将下面的代码复制到draw.js文件中:
window.onload = function() {
  document.ontouchmove = function(e){ e.preventDefault(); }
  var canvas  = document.getElementById('main');
  var canvastop = canvas.offsetTop;
  var context = canvas.getContext('2d');
  var lastx;
  var lasty;
  context.strokeStyle = "#000000";
  context.lineCap = 'round';
  context.lineJoin = 'round';
  context.lineWidth = 5;
  function clear() {
    context.fillStyle = "#ffffff";
    context.rect(0, 0, 300, 300);
    context.fill();
  }
  function dot(x,y) {
    context.beginPath();
    context.fillStyle = "#000000";
    context.arc(x,y,1,0,Math.PI*2,true);
    context.fill();
    context.stroke();
    context.closePath();
  }
  function line(fromx,fromy, tox,toy) {
    context.beginPath();
    context.moveTo(fromx, fromy);
    context.lineTo(tox, toy);
    context.stroke();
    context.closePath();
  }
  canvas.ontouchstart = function(event){
    event.preventDefault();
   
    lastx = event.touches[0].clientX;
    lasty = event.touches[0].clientY - canvastop;

    dot(lastx,lasty);
  }
  canvas.ontouchmove = function(event){
    event.preventDefault();
    var newx = event.touches[0].clientX;
    var newy = event.touches[0].clientY - canvastop;
    line(lastx,lasty, newx,newy);
        lastx = newx;
    lasty = newy;
  }
  var clearButton = document.getElementById('clear');
  clearButton.onclick = clear;
  clear();
}

代码片段draw/draw.js

(4) 确认nginx正在发布你的新文件。为此,打开桌面Safari浏览器并输入网址http://localhost/draw/draw.html。应看到空的灰色背景的页面,中间是300像素的白色的正方形,在正方形上方有一个Clear按钮。

(5) 在移动设备的浏览器中输入网址http://localhost/
draw/draw.html,记住将localhost换成桌面计算机的IP地址。

(6) 在屏幕上随便画些图形。确认当手指点击屏幕时会画出点,当手指在屏幕上移动时会画出连续的线。图2-2显示了绘图的结果。


 

(7) 单击Clear按钮来清除绘图,并重新开始绘画。

示例说明

示例中的HTML内容和之前的示例几乎一样,只是修改了用于定义应用程序的功能的JavaScript,添加了一些HTML标记来定义Clear按钮。

新的JavaScript延续了“曳光弹”JavaScript的基本方式,它等待window.onload事件开始执行,确保它需要的所有HTML元素都已经加载成功:
window.onload = function() {

下一行代码比较“神奇”,它防止你的应用程序(HTML页面)产生滚动。这需要阻止
ontouchmove事件默认的滚动行为,这一点我们会在第4章中进行详细的解释。现在,你使用下面的标准样板代码:
  document.ontouchmove = function(e){ e.preventDefault(); }

接下来,按照曳光弹代码中的方式获取canvas对象:
  var canvas  = document.getElementById('main');

这一次,你需要获得canvas元素的offsetTop属性的值:
  var canvastop = canvas.offsetTop;

canvas元素距离浏览器窗体的顶部50个像素。当触发触摸事件时,该事件的clientX和clientY属性返回的是触摸相对于浏览器窗口的位置,而canvas元素并不是直接在浏览器窗口顶部,所以要调整触摸事件返回的垂直的Y值。垂直的Y值目前设置的是50个像素,有些大,因为这个值是从浏览器窗口的顶部开始计算的,而不是从canvas的顶部开始计算的。

与曳光弹代码一样,你需要获得canvas的2D上下文,这样才可以实际地绘图:
  var context = canvas.getContext('2d');

为了绘制连续的线,你需要跟踪最新的触摸位置口每次获得新的触摸位置后,你需要从最近的触摸位置到新的触摸位置画线。最近的触摸位置存储在变量lastx 和lasty中:
  var lastx;
  var lasty;

接下来,通过设置context 对象的一些属性来绘制适合类型的线。通过设置strokeStyle 属性的值为#000000来绘制黑色的线;通过设置lineWidth 属性的值为5个像素来绘制粗线;通过设置lineCap和lineJoin属性为特殊的字符串值'round',可以使线的开始和结束部分变得圆滑。下面是设置这些属性的代码:
  context.strokeStyle = "#000000";
  context.lineCap = 'round';
  context.lineJoin = 'round';
  context.lineWidth = 5;

在程序中,需要实现3个高级的绘图操作:清空画布、画一个点和画一条线。这些操作对应于用户点击Clear按钮,点击屏幕,在屏幕上移动手指。这些高级的操作是由一组底层的绘图上下文操作完成的。你可以将这些高级的操作封装到独立的函数中,在函数中执行底层的操作,从而可以保证代码的结构清晰。

clear函数清空画布内容。该函数通过绘制一个白色的矩形,从画布的左上角(0,0)到右下角(300,300)覆盖整个画布来达到清空画布内容的效果。首先,设置fillStyle 属性的值为white,然后调用rect函数定义矩形的路径,接下来调用fill函数画出矩形:
  function clear() {
    context.fillStyle = "#ffffff";
    context.rect(0, 0, 300, 300);
    context.fill();
  }

你可能注意到,clear函数中没有显式地使用beginPath和closePath函数来定义路径,这是因为始终有一个当前的路径,可以向其中添加绘图操作。通过调用路径绘制函数stroke或fill,将绘制当前的路径直到该点。

dot函数在画布上绘制一个黑色的小圆。该函数需要两个参数:x和y,代表点的位置。该函数首先显式地声明了路径,然后设置fillStyle属性以确定颜色,并调用arc函数画点。arc函数需要下面的参数:中心点(x, y)、半径(1)、以弧度为单位的起始角度(0)、结束角弧度(2π)以及一个方向(逆时针)。记住,360°等于2π弧度。这些底层的参数定义了一个完整的圆形路径。接下来,调用fill函数填充圆的内部,调用stroke 函数绘制圆的边界。最后,调用closePath函数关闭路径:
  function dot(x,y) {
    context.beginPath();
    context.fillStyle = "#000000";
    context.arc(x,y,1,0,Math.PI*2,true);
    context.fill();
    context.stroke();
    context.closePath();
  }

line函数绘制一条直线。大多数时候,你的应用程序将频繁收到ontouchmove事件,用户将不会看到笔直的线条,而是由许多短线组成的连续的手指移动轨迹。只有当手指移动速度非常快时,用户才会画出直线。

line函数接受4个参数:参数fromx和fromy定义了线开始的位置,参数tox和toy定义了线结束的位置。通过moveTo函数将位置移到线开始的位置,然后调用lineTo函数绘制线到结束的位置,最后调用stroke 函数画出线:
  function line(fromx,fromy, tox,toy) {
    context.beginPath();
    context.moveTo(fromx, fromy);
    context.lineTo(tox, toy);
    context.stroke();
    context.closePath();
  }

由于线内没有区域,因此不需要调用fill函数。

到目前为止,我们已经完成了绘图函数的功能,接下来,需要编写代码来响应手指的点击与移动操作。当手指触摸屏幕时,可以通过ontouchstart事件处理程序获得通知,这个通知只是告诉你触摸事件启动。用户随后可能会将手指离开屏幕或者继续在屏幕上移动手指。对于本绘图应用程序来说,这些动作之间的区别并不是十分重要,因为始终要绘制点。当用户移动手指时,这些点连在一起形成线。

在ontouchstart事件处理函数中,通过使用preventDefault函数来禁用浏览器的一些默认的操作,如复制和粘贴的操作:
  canvas.ontouchstart = function(event){
event.preventDefault();

在触摸事件中,要把手指的位置存储到变量lastx和lasty中,从而当用户移动手指时,可以通过这些变量来画出线。触摸事件的处理要比鼠标单击事件复杂,我们会在第4章中详细地介绍这些内容。目前,你需要了解的是触摸事件对象包含特殊的touches数组,该数组的第一个元素event.touches[0]包含一个对象,用来描述手指在屏幕上的位置。这个对象的clientX和clientY属性返回触摸点相对于浏览器窗口的像素位置信息。前面我们提到过,canvas元素距离浏览器窗口顶部有50像素的距离,因此需要从返回的clientY值中减去前面获取的canvastop值,结果才是触摸点在画布上的实际的垂直位置:
    lastx = event.touches[0].clientX;
    lasty = event.touches[0].clientY - canvastop;

现在,我们已经获得了最新的触摸位置的信息,可以调用dot函数绘制点:
    dot(lastx,lasty);

当手指在屏幕上移动时,ontouchmove事件处理程序会被连续调用,实时地返回手指在屏幕上的位置。与ontouchstart事件处理程序一样,首先要禁用浏览器的一些默认的行为: 
canvas.ontouchmove = function(event){
  event.preventDefault();

接下来,需要捕获触摸点相对于画布的位置,并将捕获的信息存储到变量newx和newy中:
    var newx = event.touches[0].clientX;
    var newy = event.touches[0].clientY - canvastop;

然后,将这些位置信息传递给line函数,画出线:
    line(lastx,lasty, newx,newy);

最后,将记录的最后一个位置赋给变量lastx和lasty:
    lastx = newx;
    lasty = newy;
  }

用户界面中还剩下Clear按钮没有编写处理程序。通过按钮的id属性获取按钮元素并附加onclick事件处理程序,它就是一个clear函数:
  var clearButton = document.getElementById('clear');
  clearButton.onclick = clear;

当用户单击Clear按钮时,会直接调用clear()函数,清空画布以供用户开始绘图:
  clear();

注意:在JavaScript中,函数调用与将它用作变量是有区别的。调用clear函数时,要在函数名后面加上(),如clear()。这样,clear函数会被调用,函数中的语句会执行。而在程序中输入clear且没有括号时,该函数不会执行。在这里,clear被当成变量,变量的值是函数!在JavaScript中,函数是值,与对象、字符串、数值一样,你可以像处理其他普通值那样处理函数,这是JavaScript中潜在的功能。当输入clearButton.onclick= clear时,你设置了clearButton对象的onclick属性值等于clear,而该值就是clear函数。

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.1.1 如何在屏幕上绘图
下一篇:2.1.3 运用DRY原则
相关文章
图文推荐
排行
热门
最新书评
文章
下载
读书
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站