《HTML5游戏编程核心技术与实战》——2.3 图像API

2.3 图像API

除了绘制常用的图形以外,canvas提供了一系列的API能够对图像进行操作,常见的图像API有以下3个方法。

drawImage (image, dx, dy):把image图像绘制到画布上(dx, dy)坐标位置。
drawImage (image, dx, dy, w, h):把image图像绘制到画布上(dx, dy)坐标位置,图像的宽度是w,高度是h。
drawImage (image, sx, sy, sw, sh, dx, dy, dw, dh):截取image图像以(sx, sy)为左上角坐标,宽度为sw,高度为sh的一块矩形区域绘制到画布上(dx, dy)坐标位置,图像宽度是dw,高度是dh。
其中image可以是htmlImageElement元素、htmlcanvasElement元素、htmlVideoElement元素,htmlVideoElement元素是HTML5中新增加的video视频播放元素。

https://yqfile.alicdn.com/72e8aa99f7730abe9609f15c55afa6a503f12e7f.png" >

图2-14显示了drawImage中源图像和目标canvas之间的关系。

采用后面两种方式进行绘制的时候可以实现图像的放大和缩小。
**
2.3.1 使用canvas绘制图像**
先看一个简单的例子,我们把< image />标签中的图像显示到一个canvas中。

<body>
  <h2>图片显示</h2>
  <img src="img/t1.jpg" id="img1" />
  <input type="button" id="btnCopy" value="拷贝图片" /><br>
  <canvas id="can" width="400" height="300" ></canvas>
</body>
<script>
  function $(id)
  {
   return document.getElementById(id);
  }
  $("btnCopy").onclick = function()
  {
   //获取canvas的上下文
   var ctx = $("can").getContext("2d");
   //设置canvas的大小
   $("can").width = $("img1").width;
   $("can").height = $("img1").height;
   ctx.drawImage($("img1"), 0, 0);
  }
</script>

代码中有一个图片元素、一个按钮和一个canvas元素,当点击按钮的时候首先获取了id为img1的图片的宽度和高度,然后改变canvas的大小,最后使用drawImage方法把img的图片绘制到canvas元素中,使用drawImage把img1绘制到canvas坐标(0, 0)的位置,大小和原图像一样大。

2.3.2 案例:放大镜
现在,我们可以借助drawImage方法实现一个简单的放大镜效果,效果如图2-15所示。

在这个放大镜效果中,我们可以在任意一张图片上点击,在该位置就会出现一个圆形的放大镜。实现原理也比较简单,首先,需要获取鼠标当前的位置,然后根据放大镜的尺寸,截取图片中相应大小的图片,最后通过drawImage方法把图片中的部分绘制到canvas上面。

这里涉及一个问题,放大镜是圆形的,但实际上如果直接通过canvas绘制,将最终以矩形的方式显示。这里,我们可以使用剪裁的方式进行绘制,所谓剪裁,就是在canvas中指定一块区域,在这个区域之内的都会显示,之外的一律不显示,所以如果我们把剪裁区域设置成圆形就可以了。canvas中使用context.clip()方法定义一个画布的剪裁路径,方法没有任何参数,它会使用当前的路径作为一个剪裁的区域,默认情况下,整个canvas本身就是一个剪裁路径。如 图2-16所示,图中定义了一个圆形的剪裁区域,在圆形之外的区域将不会显示。

https://yqfile.alicdn.com/35d27e7f6c5a83d54d35ecf5cc2619fa90c25ad1.png" >

好了,有了以上的基本知识,现在可以开始制作一个放大镜了,具体代码如下:

<body>
  <h2>放大镜</h2>
  <img src="img/tx1.jpg" id="s1"></img>
</body>
<script>
  //根据编号获取对象
  function $(id)
  {
   return document.getElementById(id);
  } 
  //定义放大镜
  var Glass = {
   bind:function(imgId, zRat)
   {
    var self = this;
    this.canvas = document.createElement("canvas");  
    this.canvas.style.display="none";
    this.canvas.style.position="absolute";
    this.ctx = this.canvas.getContext("2d");
    this.canvas.width = 100;
    this.canvas.height = 100;
    this.hEle = $(imgId);
    //设置放大比例
    this.zRat = zRat|2;
    //设置鼠标按下事件     
    document.body.appendChild(this.canvas);
    document.body.onmousedown=function(e){
     if(e.srcElement.id==imgId){
       e.preventDefault();
       draw(e);
       //定义绘制方法
       function draw(e)
       {
        //获取鼠标位置
        var x = e.pageX, y = e.pageY;
        //获取图片相对位置
        var exOff = x-self.hEle.offsetLeft,
           eyOff = y-self.hEle.offsetTop;      
        //设置获取图片周围长度
        var rLen = 50/self.zRat;
        self.copyImg(exOff-rLen, eyOff-rLen, rLen*2, rLen*2);
        self.show(x-50, y-50);
       }
       document.body.onmousemove = draw;
       document.body.onmouseup = function(){
        self.hide();
        document.body.onmousemove = null;
       };
     }                  
    };
   },
   copyImg:function(x, y, w, h)
   {   
    this.ctx.arc(50, 50, 50, 0, Math.PI*2, true);
    //设置路径剪裁形成圆形
    this.ctx.clip();
    this.ctx.drawImage(this.hEle, x, y, w, h, 0, 0, 100, 100);
   },
   show:function(x, y)
   {
    this.canvas.style.display = "block";
    this.canvas.style.pixelLeft = x;   
    this.canvas.style.pixelTop = y;   
   },
   hide:function()
   {
    this.canvas.style.display = "none";
   }
  }; 
  Glass.bind("s1");
</script>

除了实现静态的效果外,还可以通过drawImage实现动画效果。

2.3.3 案例:帧动画实现
来看一个比较复杂的例子,该例子中实现了一个简单的人物动画,先简单了解一下动画的原理。通常我们看到的动画称为逐帧动画,它是利用人眼睛视觉暂留的原理,即物体被移动后其形象在人眼视网膜上还可有约1秒的停留。利用这个原理,我们在一秒钟内如果连续放映20张静态的图片,这样就形成了动画的效果,当然20是最基本的要求,如果流畅的话至少要30帧/秒,也就是说每秒钟放30张静态图片,每一帧图片称为一帧,在第4章的制作游戏引擎中会详细介绍动画机制。

本例子中我们有3张人物行走的图片,这3张图片构成了人物行走的过程,为了提高效率通常情况下制作动画的时候,会把人物的动作放在一张图片中,如图2-17所示(该图截取自游戏《超级玛丽》)。

这张图片包含了玛丽行走的动画,一共由3帧组成,我们在canvas上循环绘制这三张图片就形成了玛丽行走的动画,代码如下:

<body>
 <h2>图片动画</h2>
 <img id="img1" src="img/mr.png" />
 <input id="btnGO" type="button" value="开始" /><br>
 <canvas id="c1" width="320" height="200" ></canvas><br> 
</body>
<script>
  //是否开始动画
  var isAnimStart = false, animHandle = null;//动画句柄
  //保存每帧动画起始坐标,本例图片共有3帧
  var frames=[
   [0, 0],
   [32, 0],
   [64, 0]
  ];
  //定义每帧图像的宽度和高度
  var fWidth = 32, fHeight = 32;
  function $(id)
  {
   return document.getElementById(id);
  }
  //开始
  function init()
  {
   //注册GO按钮事件
   $("btnGO").onclick=function()
   {
     //如果没开始动画,则开始动画
     if(!isAnimStart)
     {
      //获取canvas上下文
      var ctx = $("c1").getContext("2d");
      //设置当前帧序号
      var fIndex = 0;
      //找到canvas的中点
      var cX = 160, cY = 100;
      animHandle = setInterval(function(){
        //先清空画布
        ctx.clearRect(0, 0, 320, 200);
        //绘制当前帧
        ctx.drawImage(img1, frames[fIndex][0], frames[fIndex][1], fWidth, fHeight,
                cX-64, cY-64, fWidth*4, fHeight*4);
        //计算下一帧
        fIndex++;
        if(fIndex>=frames.length)
        {
         fIndex = 0;
        }
      }, 100)
      $("btnGO").value = "停止";
      isAnimStart = true; 
     }
     else
     {
      $("btnGO").value = "开始";
      clearInterval(animHandle);
      isAnimStart = false;
     }
    }
  }
  init();
</script>

来看看代码功能,首先定义了几个变量,isAnimStart标记是否已经开始播放动画标记,用来切换播放和停止动画。animHandle保存定时器的句柄,可以通过它关闭定时器,定义frames数组变量记录图片中3个动作帧左上角的起始坐标,在绘制动画的时候,需要根据这个坐标来截取图片中的每帧图像。fWidth和fHeight分别表示每帧图片的宽度和高度,本例中使用的玛丽图像帧是32×32大小。然后定义init初始化方法,在该方法中绑定了开始按钮的事件,为了实现动画的播放,使用了serInterval方法,该方法每100毫秒执行一次,每次读取frames中的一帧图像,并把它显示在画布上,其核心代码是:

//先清空画布
ctx.clearRect(0, 0, 320, 200);
//绘制当前帧
ctx.drawImage(img1, frames[fIndex][0], frames[fIndex][1], fWidth,fHeight, cX-64,
           cY-64, fWidth*4, fHeight*4);

这段代码首先使用ctx.clearRect方法清空整个画布,因为在定时器动画中需要每100毫秒更新画布中的内容,那么每次更新时,需要先把上个画面清除掉,然后再进行绘制,否则会把几张动画重叠起来。然后使用了ctx.drawImage方法,把img1图片中的内容复制到canvas上面,复制的时候第1个参数是选取的图片,第2~5的参数分别对应着img1图片上的一块矩形区域,最后4个参数对应着canvas上面的一块矩形区域,实际上就是把图片上的某一矩形区域部分绘制到canvas上面,由于画到canvas上面的宽度和高度分别是fWidth×4和fHeight×4,实际上也就是把原图像放大了4倍。

除了直接对图片元素操作外,canvas还提供了直接对像素元素进行处理的API。

2.3.4 像素操作
canvas另外一个非常强大的功能就是可以对图像中的任一个像素进行处理。我们知道图像实际上是由很多像素点组成,一幅宽度为320,高度200的图片是由320×200=64 000个像素点构成,每个像素由red、green、blue三种颜色组成。

canvas提供了以下API让我们可以进行像素操作。

getImageData (sx, sy, sw, sh):获取canvas上以(sx, sy)为左上角,宽度为sw,高度为sh的一块矩形区域的像素数据。通过getImageData获取到了imageData对象,该对象有以下3个属性。
width:每行的像素数量。
height:每列的像素数量。
data:存有从canvas中获取的每个像素的RGBA的值,该数组为每个像素保存了四个值,分别是红色、绿色、蓝色和alpha透明度,每个值在0~255之间,数组填充的数据是从上到下,从左到右,比如imgData.data[0]~imgData.data[3]就保存了canvas图像中左上角第一个像素点的RGBA的数据,imgData.data[4]~imgData.data[7]保存了canvas图像中左上角第二个像素点的RGBA的数据,依次类推,从左到右,从上到下。
createImageData (sw, sh):创建一个宽度为sw,高度为sh的imageData对象,该对象中所有的像素都是黑色的。
createImageData (imageData):创建一个imageData对象的副本,像素值和imageData的一致。
putImageData (imageData, dx, dy, [dirtyX, dirtyY, dirtyWidth, dirtyHeight]):在绘图画布上绘制给定的ImageData对象。假如脏矩形被提供,则只有在脏矩形上面的像素被绘制。本方法对全局透明、阴影和全局组合等属性均忽略。
data数组中的数据可以获取也可以设置,我们可以把data中的数据进行修改后重新绘制达到修改图像的目的。

利用可以对像素操作的特性,我们可以完成一些简单的图像处理效果,类似于Photoshop中的滤镜效果,如转成灰度图、浮雕效果等,接下来看看相关的例子。

2.3.5 案例:转换灰度图
现在来看一个简单的像素操作的例子,该例子中会把任意一张彩色图片转换成灰度图片,如图2-18所示。

彩色图像要转换成灰度图,需要对图中的每一个像素点进行处理,我们可以通过getImage Data()方法获取每个点的像素值,然后把该点的色彩转成灰度。彩色转灰度的算法很多,这里,我们采用以下算法:
灰度值=(R×30 + G×59 + B×11 + 50) / 100

具体实现代码如下:

<body>
 <h2>像素操作</h2>
 <img id="img1" src="img/t1.jpg" />
 <input id="btnGO" type="button" value="转成灰度图" /><br>
 <canvas id="c1" width="320" height="200" ></canvas><br> 
</body>
<script>
  function $(id)
  {
   return document.getElementById(id);
  }
  //开始
  function init()
  {
   //注册GO按钮事件
   $("btnGO").onclick=function()
    {
     c1.width = img1.width;
     c1.height = img1.height;
     //先把image绘制到canvas上
     var ctx = c1.getContext("2d");
     ctx.drawImage(img1, 0, 0, c1.width, c1.height);
     //获取像素数据
     var imgData = ctx.getImageData(0, 0, c1.width, c1.height);
     for(var i = 0;i<imgData.data.length;i+=4)
     {
       //获取RGB像素值
       var r = imgData.data[i],
          g = imgData.data[i+1],
          b = imgData.data[i+2];
      //计算灰度值,常用公式 Gray = (R*30 + G*59 + B*11 + 50) / 100
       var gray = (r*30+g*59+b*11+50)*0.01;
        imgData.data[i] = gray;
        imgData.data[i+1] = gray;
       imgData.data[i+2] = gray;
     }
     //最后把imgdata数据绘制到canvas中
     ctx.putImageData(imgData, 0, 0);
   }
  }
init();

代码先通过contex.drawImage把图像绘制到canvas上,需要注意的是,只有canvas上有图像了才能通过getImageData获取canvas的图像数据,然后定义imgData变量得到canvas的图像数据,接着通过循环imgData数据,修改canvas图像中的每个像素数据。因为每个图像数据由4个值构成,所以循环data数据每次加4,然后接着获取每个像素的RGB值,然后通过公式转成灰度。

把彩色信息转成灰度信息后,最后重新设置每个像素的颜色值,就完成了彩色图到灰度图的转变。我们虽然改变了图像的像素值,但是还不能让图像立刻变成我们想要的样子,还需要重新把像素的数据绘制到canvas中,所以我们使用了putImageData方法。

这样就完成了整个的图像转变过程。

2.3.6 案例:浮雕效果
接下来,来实现一个浮雕效果,浮雕效果如图2-19所示。

https://yqfile.alicdn.com/c730b1328421c27c2b165b5fb6e416eef0c84ca9.png" >

浮雕效果的实现原理也有很多种,常用的算法是把每个点周围的8个点和一个转换矩阵进行卷积操作,得到的值作为该点的新色彩。但计算量过大,这里采用一种相对简单的算法,该算法是这样的,对于任一点的像素来说,新的色彩值等于该点的色彩和右边像素的色彩值相减,然后加上128。

具体实现如下:

<body>
 <h2>像素操作</h2>
 <img id="img1" src="img/t1.jpg" />
 <input id="btnGO" type="button" value="转成浮雕图" />
 <canvas id="c1" width="320" height="200" ></canvas><br> 
</body>
<script>
  function $(id)
  {
   return document.getElementById(id);
  }
  //开始
  function init()
  {
   //注册GO按钮事件
   $("btnGO").onclick=function()
   {
     c1.width = img1.width;
     c1.height = img1.height;
     //先把image绘制到canvas上
     var ctx = c1.getContext("2d");
     ctx.drawImage(img1, 0, 0, c1.width, c1.height);
     //获取像素数据
     var imgData = ctx.getImageData(0, 0, c1.width, c1.height);
     var iData = imgData.data;
     for(var i = 0;i<img1.height-1;i++)
     {
      for(var j = 0;j<img1.width;j++)
      {
       //获取像素在dataImage起始位置
       var start = (i*img1.width+j)<<2;         
       var r = iData[start]-iData[start+4]+128,
          g = iData[start+1]-iData[start+5]+128,
          b = iData[start+2]-iData[start+6]+128;
       //越界处理
       r = (r<0)?0:(r>255)?255:r;
       g = (g<0)?0:(g>255)?255:g;
       b = (b<0)?0:(b>255)?255:b;
       //转灰度图
       var g=(r*30+g*59+b*11+50)*0.01;
       iData[start] = g;
       iData[start+1] = g;
       iData[start+2] = g;
      }
     }     
     //最后把imgdata数据绘制到canvas中
     ctx.putImageData(imgData, 0, 0);
    }
  }
  init();
</script>

需要注意的是,在得到新的像素点色彩值之后,需要进行越界处理,并转成灰度色彩,否则,该像素点还有可能是彩色点。

接下来,来看看坐标变换相关的一些知识。

时间: 2024-10-26 04:45:18

《HTML5游戏编程核心技术与实战》——2.3 图像API的相关文章

《HTML5游戏编程核心技术与实战》一2.4 坐标变换

2.4 坐标变换 HTML5游戏编程核心技术与实战在绘制图像的过程中,经常可能需要对图像进行旋转.缩放等变形处理,canvas也提供了一系列的API帮助我们完成这些操作. 关于画布的坐标变换,canvas提供了以下常用的API,这些API的操作必须要在绘制之前调用,否则不会产生任何效果. translate (x, y):平移,把画布的原点坐标移动到(x, y)位置,x表示将坐标原点向左移x个像素,y表示将坐标原点向下移动y个像素.正常情况下canvas的原点坐标位于左上角,那么我们可以通过tr

《HTML5游戏编程核心技术与实战》一2.2 图形API

2.2 图形API HTML5游戏编程核心技术与实战 创建canvas和获取了canvas的环境上下文之后,就可以开始进行绘图了.绘图的方式有两类:一类是进行图形操作,另一类是图像操作.本小节主要涉及图形相关的API,要使用canvas的API进行绘图,通常需要进行下列步骤. (1)获取canvas元素.通过document.getElementById()取得元素. (2)获取canvas元素的环境上下文.通过canvas.getContext ("2d")获取2D图像上下文. (3

《HTML5游戏编程核心技术与实战》一2.1 基本知识

2.1 基本知识 HTML5游戏编程核心技术与实战在HTML4时代,不安装插件的情况下,基于浏览器的绘图组件是最初由微软向W3C递交的VML(Vector Markup Language矢量标记语言)技术,但未被W3C采纳,只能在IE5.0及其后续版本中使用,后来VML和PGML(由Adobe和SUN提出)合并成SVG(Scalable Vector Graphics,可伸缩矢量图形)规范.SVG技术是基于XML的矢量图形绘制技术,对于普通的图像应用来说足够,但是对于性能要求较高的游戏渲染来说,

《HTML5游戏编程核心技术与实战》一导读

前 言 HTML5游戏编程核心技术与实战为什么写这本书对游戏的热爱使我萌发了写书的念头.漫画和电子游戏是童年最美好的回忆,任天堂的红白机陪伴着我度过了童年最快乐的时光,20世纪80年代,大街小巷的街机室成了孩子们快乐的天堂.随着时光流逝,许多经典的游戏画面已成为过去,但对游戏的热情依然不减,希望能借此书得以慰藉逝去的青春.青春不在,游戏热血永存! IT技术可谓日新月异,要想不被社会淘汰,就必须要不断学习,不断充实自己.HTML5是Web技术中提出的新规范.新生的HTML5技术虽然目前还不十分成熟

《HTML5游戏编程核心技术与实战》一2.8 小结

2.8 小结 HTML5游戏编程核心技术与实战本章介绍了canvas的一些常用的操作,作为HTML5的最重要的特性,canvas使得在Web中开发免插件的较高性能的游戏提供了条件,接下来的第3章中,我们介绍HTML5中的多媒体元素.

《HTML5游戏编程核心技术与实战》一2.5 绘制文字

2.5 绘制文字 HTML5游戏编程核心技术与实战canvas中除了可以绘制图形图像外,还可以绘制文字,同时也可以指定文字的字体.大小.对齐方式以及填充文字的纹理. fillText (text, x, y, [maxWidth]):在canvas上填充文字,text表示需要绘制的文字,x.y分别表示绘制在canvas上的横.纵坐标,最后一个参数可选,表示显示文字的最大宽度,防止文字显示溢出.strokeText (text, x, y, [maxWidth]):在canvas上描边文字,参数的

《HTML5游戏编程核心技术与实战》一2.7 案例:《你画我猜》

2.7 案例:<你画我猜> HTML5游戏编程核心技术与实战在这一小节中,我们将利用前面介绍的知识,来创作一个<你画我猜>游戏中的主要功能.<你画我猜>是一款老少皆宜的多人在线的网络游戏,2012年风靡一时,玩法其实也来源于生活当中,经常在娱乐节目中出现.通常在节目中是这样玩的,主持人写出一个词语,然后由一个参与者根据这个词语画出相应的图案,由另一个参与者来根据这个图案猜出这个词语,而<你画我猜>就把现实生活中的这个玩法转到了电脑上,玩法就是这么简单.当然,

《HTML5游戏编程核心技术与实战》一2.6 其他全局属性

2.6 其他全局属性 HTML5游戏编程核心技术与实战在context中还有一些常见的全局属性,做一些了解. globalAlpha:透明度,这个值用来设置在画布上绘制的透明度,值的范围从0-1之间,使用这个属性我们可以完成一些常见的效果,比如游戏中常见的淡入淡出效果. 以下代码展示了一张图片淡入,也就是逐渐显示的效果. globalCompositeOperation:全局混合模式,这个属性定义了如果在画布上绘制多个图像时,图像进行叠加的方式,也称为混合模式,类似于Photoshop这种图像处

《HTML5游戏编程核心技术与实战》一1.1 网页游戏和HTML5

1.1 网页游戏和HTML5 HTML5游戏编程核心技术与实战 1.1.1 网页游戏简介 从电子游戏载体来说,电子游戏现在基本上分成了3个主要的阵营:第一部分是以电视游戏为主,第二部分以个人电脑游戏为主,第三部分是以手机和平板为主.而从游戏的玩家数量来说,游戏经历了从单机游戏时代到现在的网络游戏时代.随着互联网的普及,以及电脑硬件的飞速发展,互联网游戏正处于高速发展的时期,特别是网页游戏得到了空前的发展. 网页游戏也称Web Game,是一种无端网游,和<魔兽>系列.<星际>系列等

《HTML5游戏编程核心技术与实战》一1.2 HTML5新特性初探

1.2 HTML5新特性初探 HTML5游戏编程核心技术与实战准备好HTML5开发工具之后,我都迫不急待地想看看HTML5给我们带来了哪些让人激动不已的新特性,相信,你也会和我一样,对它感到兴奋.由于HTML5新增加了很多元素,这里,我们重点简单介绍一下在游戏中会使用到的一些元素,在介绍每一个新的元素时,我都会列举一些小的例子.现在假定读者有一定的HTML以及JavaScript方面的知识,如果您对这些代码完全不清楚,没关系,后面的章节中会详细地解说.目前,我们只需要知道HTML5能做什么就足够