Skip to content

2014 04 08 canvas入门

zhangnan05 edited this page Sep 2, 2014 · 1 revision

layout: page title: canvas基础

{% include JB/setup %} ##背景 这里主要是我做的一些学习笔记,所以看起来比较乱. #基础

  • 元素: canvas
  • 2d上下文:ctx = canvas.getContext('2d')
  • canvas动画的本质是不停的(requestAnimationFrame)绘制(stroke)和清理(clearRect)

conan

#线

画直线

  • 方法 ctx.beginPath() 定义了一个新的路径绘制动作的开始。
  • 方法 ctx.moveTo(0 , 0)为指定点创建了一个新的子路径,这个点就变成了新的上下文点。我们可以把 moveTo() 方法看成用来定位我们的绘图鼠标用的。
  • 方法 ctx.lineTo(100, 100) 以上下文点为起点,到方法参数中指定的点之间画一条直线。
  • 方法 ctx.stroke()为所画的线赋予颜色,并使其可见。如果没有特别的指定颜色的话,则默认使用黑色画直线。
  • 属性context.lineWidth = 5;定义直线宽度(px)
  • 属性context.strokeStyle = #dedede定义直线的颜色
  • 属性context.lineGap = [butt, round,square]定义直线断点样式

##画弧线

###圆弧

  • 方法context.arc(x, y, radius, startAngle, endAngle, antiClockwise);每条弧线都需要由中心点、半径、起始弧度、结束 弧度(弧度= 角度 * Math.PI/ 180)和绘图方向(顺时针 false 还是逆时针 true )这几个参数来确定。

###圆角

  • 方法context.arcTo(controlX,controlY,endX,endY,radius);画圆角使用方法 arcTo()此方法需要一个控制点、一个终止点和半径作为必要的参数。

###画二次曲线

  • 方法 context.quadraticCurveTo(controlX, controlY, endX, endY);二次曲线使用 quadraticCurveTo()方法来绘制。每条二次曲线要由上下文点(content.moveTo)、一个控制点和一个终止点来定义。

###贝塞尔曲线

  • 方法 content.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY)。每条二次曲线要由上下文点(content.moveTo)、2个控制点和一个终止点来定义。

###综合路径

  • 路径是由多条子路径连接构成的。每条子路径的终止点就将作为新的上下文点。我们可以使用lineTo(), arcTo(), quadraticCurveTo()和 bezierCurveTo()创建新的子路径,子路径的结合使用content.lineJoin = [miter, round, bevel]。每次要开始画一条路径的时候就要使用 beginPath() 方法

#面

  • 要画一个用户自定义的图形,需要创建一个路径,然后用context.closePath() 方法关闭此路径即可。可以使用使用context.fillStyle = 'blue' 自定义填充颜色,使用contentText.fille()进行填充。

注意: fill 方法要在 strok 方法之前执行,否则 fill 会覆盖掉 stroke 的一半区域。

线性渐变

首先要使用createLinearGradient方法

var grd = context.createLinearGradient(startX, startY, endX, endY);
grd.addColorStop(offset, color);
context.fillStyle = grd;
context.fill() 

方法从上下文对象中创建线性渐变对象,使用对象中的addColorStop指定颜色。

图案填充

var pattern = context.createPattern(imageObj, repeatOption);
context.fillStyle = pattern;
context.fill();

绘制图片

context.drawImage(imageObj, x, y, width, height);

注意这里需要等到imageObj加载完成之后才能绘制

裁剪图片

方法 drawImage() 还可以增加另六个参数来实现对图像的裁剪。这六个参数是, sourceX, sourceY, sourceWidth, sourceHeight, destWidth 和 destHeight。这六个参数对应的含 义可以参阅后面的示意图。

context.drawImage(imageObj, sx, sy, sw, sh, dx, dy, dw, dh);

上面的参数sx,sy和sw,sh可以剪切图片。剪切完了之后可以使用dx, dy, dw, dh等缩放调整位置。

#文字

    conText.font = "40px sans-serif";
    var fps = "20";
    //边缘颜色
    conText.strokeStyle = "blue";
    //填充颜色
    conText.fillStyle = 'yellow';
    //边缘的宽度
    conText.lineWidth = ".6";
    //对齐方向
    conText.textAlign = "right"
    //内部
    conText.fillText('FPS:'+fps.toFixed(2),innerWidth , innerHeight- 50);
    //边缘
    conText.strokeText('FPS:'+fps.toFixed(2),innerWidth , innerHeight- 50)

#进阶 ##保留当前设置 /* * save() 的作用是在创建剪裁区之前将 canvas 的当前状态保存起来, * 这样在后面就可以恢复上下文对象的原始状态了 */ context.clip(); context.beginPath(); context.arc(x, y, radius, 0, 2 * Math.PI, false); context.save(); onText.shadowColor = "#888"; conText.shadowBlur = "10"; //do some thing //... conText.restore();

阴影 (慎用,影响性能)

//保存      
conText.save();
conText.rect( innerWidth - 800, 200, 780, 400);
conText.shadowColor = "#888";
conText.shadowBlur = "10";
conText.fillStyle = "#fefefe";
conText.shadowOffsetX = 10;
conText.shadowOffsetY = 10;
conText.fill();
//恢复
conText.restore();

透明(慎用,影响性能)

conText.globalAlpha = ".5";

剪切

conText.clip();

全局组合操作

conText.globalCompositeOperation = ['source-atop', 'source-in', 'source-out', 'source-over', 'destination-atop', 'destination-in', 'destination-out', 'destination-over', 'ligher', 'darker','xor', 'copy'];

【重要】注意使用另一个canvas元素来组合剪切合并操作,原因是因为会把其他绘制的图像剪切掉。

##tranlate移动 var translateX = innerWidth - 100, translateY = innerHeight - 100; conText.translate(translateX, translateY); conText.fillStyle = "green"; conText.fillRect(0,0, 50, 50); conText.font = "12px sans-serif"; conText.fillText('translate',0, 70); //restore position conText.translate( -translateX, -translateY);

注意:移动的0坐标永远是本身(移动相对于本身),这和DOM操作有所不同的地方。例如移动了10像素(translate(10, 0)),想要移动回来,则必须移动-10px(translate(-10, 0)),而非0像素

##scale缩放 conText.scale(rateX, rateY);//rateX、rateY小于1则是缩小,大于1则是放大 conText.fillRect(100, 100, 100, 100); 注意缩放的是整体canvas里面的内容,而非当前所绘图

##rotate旋转 旋转canvas用的方法是 conText.rotate(angle)。此方法接受一个以弧度为单位的旋转参数,整个canvas将以坐标原点,也就是由 translate()所确定的原点为圆心进行旋转(默认为左上角)。 注意:旋转完成所需绘图后别忘记旋转回来,否则后面的图片也会在旋转中进行

  • 无法清除(clearRect)往往是因为未使用beginPath();

  • CANVAS游戏的基本思路:一个总的控制者Stage,调用各个sprite的draw方法,每一个sprite都有自己的left、top、width、height,sprite有一个artist对象负责使用以上参数进行绘制自身, 绘制方法有原生方法broke fill等,以及sprite图标和drawImage方法绘制。 behavio负责修改这些参数形成一个个的行为,一个sprite有多个行为(跳远,爆炸,行走等)

  • draw方法是一个绘制机器,想个发动机一样,不断绘制里面的sprite(只要是visible为true时)

  • 任何有动作的个体对象都做成一个sprite,然后将其加入到sprite list进行绘制。

  • 2个有密切关联的sprite(豌豆炮手发射炮弹动作,分为豌豆和炮弹),可以通过互相引用关联起来,在其中一个sprite(这里是豌豆)的行为里面定制发射动作,修改另一个sprite的状态(显示、隐藏)

  • 行为是可以相互操作的,一个行为可能影响另一个行为,可以把同一个性质的行为抽离独立公用。

  • 当需要做一个周期性的行为时(cycle),找到一个极点去判断,例如开始(start index),然后去延迟执行。

  • 任何一个动作执行后都保存时间戳,作为下一次的判断点。

  • requestAnimationFrame每次调用第一个参数是一个(time stamp),这个时间戳不是Date.now(),而是距离第一次调用该方法的间隔值(如果是不停执行的情况下),所以第一次调用时是2、30ms左右。

  • 将加速度换算成像素加速度,首先将屏幕宽度定义好宽度米(metre)MW,在获取实际宽度像素(pixed)PW,从而知道比率PW, rate = PW/MW,在使用重力加速度(g = 9.801)计算,最后加速度为g = 9.8 * rate

  • 动画补间函数是传入一个当前时间总共运行持续时间(during)的比率(最大为1),返回一个实际运行的距离总共运行的距离的百分比。这里总共时间是恒定的,当前时间是稳定直线变化的(liner),以时间为参照点计算距离。

碰撞检测

外接图形判别法

  • 分为事前判别和事后判别

  • 事前是根据现有的帧率计算下一帧的结果,其中当前情况是没有相撞的(ball.bottom <basket.top),下一帧就相撞了(ball.bottom > basket.top)。存在不准确的情况,是用当前的帧率来计算的,可能频率突然变成原来2倍,导致相撞了。所以不精确

  • 事后判别是当前是否相撞,他的问题是如果小球速度太快就检测不到,或者穿过很多了(事前检测就是解决这个问题的)。

  • 光线投射判别,适合木桶接球的情况,根据2个物体(sprite)2次位置变化的点的射线,相交的点(斜截公式:Y = b*X+m)在其中一个物体相交面距离长度之间,且在2个点相交面的距离小于0,则相撞了。见书籍HTML5 CANVAS核心技术337页。

Clone this wiki locally