[HTML5资料]Canvas入门基础教程

[HTML5资料]Canvas入门基础教程

ID:33909978

大小:490.48 KB

页数:81页

时间:2019-03-01

上传者:U-14522
[HTML5资料]Canvas入门基础教程_第1页
[HTML5资料]Canvas入门基础教程_第2页
[HTML5资料]Canvas入门基础教程_第3页
[HTML5资料]Canvas入门基础教程_第4页
[HTML5资料]Canvas入门基础教程_第5页
资源描述:

《[HTML5资料]Canvas入门基础教程》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库

HTML5资料1Canvas教程是一个新的用于通过脚本(通常是JavaScript)绘图的HTML元素。例如,他可以用于绘图、制作图片的组合或者简单的动画(当然并不那么简单)。Itcanforinstancebeusedtodrawgraphs,makephotocompositionsordosimple(andnotsosimple)animations.1.1基本用法Basicusage元素Let'sstartthistutorialbylookingattheelementitself.让我们从元素的定义开始吧。Thislooksalotliketheelement,theonlydifferenceisthatitdoesn'thavethesrcandaltattributes.看起来很像,唯一不同就是它不含src和alt属性。Theelementhasonlytwoattributes-widthandheight.ThesearebothoptionalandcanalsobesetusingDOMpropertiesorCSSrules.它只有两个属性,width和height,两个都是可选的,并且都可以DOM或者CSS来设置。Whennowidthandheightattributesarespecified,thecanvaswillinitiallybe300pixelswideand150pixelshigh.如果不指定width和height,默认的是宽300像素,高150像素。TheelementcanbesizedarbitrarilybyCSS,butduringrenderingtheimageisscaledtofititslayoutsize.(Ifyourrenderingsseemdistorted,tryspecifyingyourwidthandheightattributesexplicitlyintheattributes,andnotwithCSS.)虽然可以通过CSS来调整canvas的大小,但渲染图像会缩放来适应布局的(如果你发现渲染结果看上去变形了,不必一味依赖CSS,可以尝试显式指定canvas的width和height属性值)。Theidattributeisn'tspecifictotheelementbutisoneofdefaultHTMLattributeswhichcanbeappliedto(almost)everyHTMLelement(likeclassforinstance).It'salwaysagoodideatosupplyanidbecausethismakesitmucheasiertoidentifyitinourscript.id属性不是专享的,就像标准的HTLM标签一样,任何一个HTML元素都可以指定其id值。一般,为元素指定id是个不错的主意,这样使得在脚本中应用更加方便。Theelementcanbestyledjustlikeanynormalimage(margin,border,background,etc).Theseruleshoweverdon'taffecttheactualdrawingonthecanvas.We'llseehowthisisdonelaterinthistutorial.Whennostylingrulesareappliedtothecanvasitwillinitiallybefullytransparent.元素可以像普通图片一样指定其样式(边距,边框,背景等等)。然而这些样式并不会对canvas实际 生成的图像产生什么影响。下面我们会看到如何应用样式。如果不指定样式,canvas默认是全透明的。替用内容Becausetheelementisstillrelativelynewandisn'timplementedinsomebrowsers(suchasFirefox1.0andInternetExplorer),weneedameansofprovidingfallbackcontentwhenabrowserdoesn'tsupporttheelement.因为相对较新,有些浏览器并没实现,如Firefox1.0和InternetExplorer,所以我们需要为那些不支持canvas的浏览器提供替用显示内容。Luckilythisisverystraightforward:wejustprovidealternativecontentinsidethecanvaselement.Browserswhodon'tsupportitwillignoretheelementcompletelyandrenderthefallbackcontent,otherswilljustrenderthecanvasnormally.Forinstancewecouldprovideatextdescriptionofthecanvascontentorprovideastaticimageofthedynamicallyrenderedcontent.Thiscanlooksomethinglikethis:我们只需要直接在canvas元素内插入替用内容即可。不支持canvas的浏览器会忽略canvas元素而直接渲染替用内容,而支持的浏览器则会正常地渲染canvas。例如,我们可以把一些文字或图片填入canvas内,作为替用内容:currentstockprice:$3.15+0.15结束标签是必须的IntheAppleSafariimplementation,isanelementimplementedinmuchthesamewayis;itdoesnothaveanendtag.However,fortohavewidespreaduseontheweb,somefacilityforfallbackcontentmustbeprovided.Therefore,Mozilla'simplementationrequiresanendtag().在AppleSafari里,的实现跟很相似,它并不没有结束标签。然而,为了使能在web的世界里广泛适用,需要给替用内容提供一个容身之所,因此,在Mozilla的实现里结束标签()是必须的。 Iffallbackcontentisnotneeded,asimplewillbefullycompatiblewithbothSafariandMozilla--Safariwillsimplyignoretheendtag.如果没有替用内容,对Safari和Mozilla是完全兼容的——Safari会简单地忽略结束标签。Iffallbackcontentisdesired,someCSStricksmustbeemployedtomaskthefallbackcontentfromSafari(whichshouldrenderjustthecanvas),andalsotomasktheCSStricksthemselvesfromIE(whichshouldrenderthefallbackcontent).如果有替用内容,那么可以用一些CSS技巧来为并且仅为Safari隐藏替用内容,因为那些替用内容是需要在IE里显示但不需要在Safari里显示。渲染上下文(RenderingContext)createsafixedsizedrawingsurfacethatexposesoneormorerenderingcontexts,whichareusedtocreateandmanipulatethecontentshown.We'llfocusonthe2Drenderingcontext,whichistheonlycurrentlydefinedrenderingcontext.Inthefuture,othercontextsmayprovidedifferenttypesofrendering;forexample,itislikelythata3DcontextbasedonOpenGLESwillbeadded.创建的固定尺寸的绘图画面开放了一个或多个渲染上下文(renderingcontext),我们可以通过它们来控制要显示的内容。我们专注于2D渲染上,这也是目前唯一的选择,可能在将来会添加基于OpenGLES的3D上下文。Theisinitiallyblank,andtodisplaysomethingascriptfirstneedstoaccesstherenderingcontextanddrawonit.ThecanvaselementhasaDOMmethodcalledgetContext,usedtoobtaintherenderingcontextanditsdrawingfunctions.getContext()takesoneparameter,thetypeofcontext.初始化是空白的,要在上面用脚本画图首先需要其渲染上下文(renderingcontext),它可以通过canvas元素对象的getContext方法来获取,同时得到的还有一些画图用的函数。getContext()接受一个用于描述其类型的值作为参数。varcanvas=document.getElementById('tutorial');varctx=canvas.getContext('2d');InthefirstlineweretrievethecanvasDOMnodeusingthegetElementByIdmethod.WecanthenaccessthedrawingcontextusingthegetContextmethod. 上面第一行通过getElementById方法取得canvas对象的DOM节点。然后通过其getContext方法取得其画图操作上下文。检查浏览器的支持Thefallbackcontentisdisplayedinbrowserswhichdonotsupport;scriptscanalsocheckforsupportwhentheyexecute.ThiscaneasilybedonebytestingforthegetContextmethod.Ourcodesnippetfromabovebecomessomethinglikethis:除了在那些不支持的浏览器上显示替用内容,还可以通过脚本的方式来检查浏览器是否支持canvas。方法很简单,判断getContext是否存在即可。varcanvas=document.getElementById('tutorial');if(canvas.getContext){varctx=canvas.getContext('2d');//drawingcodehere}else{//canvas-unsupportedcodehere}代码模板Hereisaminimalistictemplate,whichwe'llbeusingasastartingpointforlaterexamples.Youcandownloadthisfiletoworkwithonyoursystem.我们会用下面这个最简化的代码模板来(后续的示例需要用到)作为开始,你可以下载文件到本地备用。Canvastutorialfunctiondraw(){varcanvas=document.getElementById('tutorial');if(canvas.getContext){varctx=canvas.getContext('2d');} }canvas{border:1pxsolidblack;}Ifyoulookatthescriptyou'llseeI'vemadeafunctioncalleddraw,whichwillgetexecutedoncethepagefinishesloading(viatheonloadattributeonthebodytag).ThisfunctioncouldalsohavebeencalledfromasetTimeout,setInterval,oranyothereventhandlerfunctionjustaslongthepagehasbeenloadedfirst.细心的你会发现我准备了一个名为draw的函数,它会在页面装载完毕之后执行一次(通过设置body标签的onload属性),它当然也可以在setTimeout,setInterval,或者其他事件处理函数中被调用。一个简单的例子Tostartoff,here'sasimpleexamplethatdrawstwointersectingrectangles,oneofwhichhasalphatransparency.We'llexplorehowthisworksinmoredetailinlaterexamples.作为开始,来一个简单的吧——绘制两个交错的矩形,其中一个是有alpha透明效果。我们会在后面的示例中详细的让你了解它是如何运作的。观看示例functiondraw(){varcanvas=document.getElementById("canvas"); if(canvas.getContext){varctx=canvas.getContext("2d");ctx.fillStyle="rgb(200,0,0)";ctx.fillRect(10,10,55,50);ctx.fillStyle="rgba(0,0,200,0.5)";ctx.fillRect(30,30,55,50);}}1.2绘图Drawingshapes绘制图形网格ThegridBeforewecanstartdrawing,weneedtotalkaboutthecanvasgridorcoordinatespace.TheHTMLtemplateonthepreviouspagehadacanvaselement150pixelswideand150pixelshigh.I'vedrawnthisimagewiththedefaultgridoverlayed.Normally1unitinthegridcorrespondsto1pixelonthecanvas.Theoriginofthisgridispositionedinthetopleftcorner(coordinate(0,0)).Allelementsareplacedrelativetothisorigin.Sothepositionofthetopleftcornerofthebluesquarebecomesxpixelsfromtheleftandypixelsfromthetop(coordinate(x,y)).Laterinthistutorialwe'llseehowwecantranslatetheorigintoadifferentposition,rotatethegridandevenscaleit.Fornowwe'llsticktothedefault.在真正开始之前,我们需要先探讨canvas的网格(grid)或者坐标空间(coordinatespace)。在前一页的HTML模板里有一个150像素宽,150像素高的canvas对象。我在画面上叠加上默认网格,如右图。通常网格的1个单元对应canvas上的1个像素。网格的原点是定位在左上角(坐标(0,0))。画面里的所有物体的位置都是相对这个原点。这样,左上角的蓝色方块的位置就是距左边x像素和距上边Y像素(坐标(x,y))。后面的教程中我们将学会如何把移动原点,旋转以及缩放网格。不过现在我们会使用默认的状态。 绘制图形DrawingshapesUnlikeSVG,canvasonlysupportsoneprimitiveshape-rectangles.Allothershapesmustbecreatedbycombiningoneormorepaths.Luckily,wehaveacollectionofpathdrawingfunctionswhichmakeitpossibletocomposeverycomplexshapes.不像SVG,canvas只支持一种基本形状——矩形,所以其它形状都是有一个或多个路径组合而成。还好,有一组路径绘制函数让我们可以绘制相当复杂的形状。矩形RectanglesFirstlet'slookattherectangle.Therearethreefunctionsthatdrawrectanglesonthecanvas:我们首先看看矩形吧,有三个函数用于绘制矩形的:fillRect(x,y,width,height):DrawsafilledrectanglestrokeRect(x,y,width,height):DrawsarectangularoutlineclearRect(x,y,width,height):ClearsthespecifiedareaandmakesitfullytransparentEachofthesethreefunctionstakesthesameparameters.xandyspecifythepositiononthecanvas(relativetotheorigin)ofthetop-leftcorneroftherectangle.widthandheightareprettyobvious.Let'sseethesefunctionsinaction.它们都接受四个参数,x和y指定矩形左上角(相对于原点)的位置,width和height是矩形的宽和高。好,实战一下吧。Belowisthedraw()functionfromthepreviouspage,butnowI'veaddedthethreefunctionsabove.下面就是上页模板里的draw()函数,但添加了上面的三个函数。绘制矩形的例子Rectangularshapeexample观看示例functiondraw(){varcanvas=document.getElementById('tutorial');if(canvas.getContext){ varctx=canvas.getContext('2d');ctx.fillRect(25,25,100,100);ctx.clearRect(45,45,60,60);ctx.strokeRect(50,50,50,50);}}Theresultshouldlooksomethingliketheimageontheright.ThefillRectfunctiondrawsalargeblacksquare100x100pixels.TheclearRectfunctionremovesa60x60pixelssquarefromthecenterandfinallythestrokeRectdrawsarectangularoutline50x50pixelsinsidetheclearedsquare.Inthefollowingpageswe'llseetwoalternativemethodsfortheclearRectfunctionandwe'llalsoseehowtochangethecolorandstrokestyleoftherenderedshapes.出来的结果应该和右边的是一样的。fillRect函数画了一个大的黑色矩形(100x100),clearRect函数清空了中间60x60大小的方块,然后strokeRect函数又在清空了的空间内勾勒出一个50x50的矩形边框。在接下去的页面里,我们会看到和clearRect函数差不多另外两个方法,以及如何去改变图形的填充和边框颜色。Unlikethepathfunctionswe'llseeinthenextsection,allthreerectanglefunctionsdrawimmediatelytothecanvas.与下一节的路径函数不一样,这三个函数的效果会立刻在canvas上反映出来。绘制路径DrawingpathsTomakeshapesusingpaths,weneedacoupleofextrasteps.不像画矩形那样的直截了当,绘制路径是需要一些额外的步骤的。beginPath()closePath()stroke()fill() ThefirststeptocreateapathiscallingthebeginPathmethod.Internally,pathsarestoredasalistofsub-paths(lines,arcs,etc)whichtogetherformashape.Everytimethismethodiscalled,thelistisresetandwecanstartdrawingnewshapes.第一步是用beginPath创建一个路径。在内存里,路径是以一组子路径(直线,弧线等)的形式储存的,它们共同构成一个图形。每次调用beginPath,子路径组都会被重置,然后可以绘制新的图形。Thesecondstepiscallingthemethodsthatactuallyspecifythepathstobedrawn.We'llseetheseshortly.第二步就是实际绘制路径的部分,很快我们就会看到。Thethird,andanoptionalstep,wouldbetocalltheclosePathmethod.Thismethodtriestoclosetheshapebydrawingastraightlinefromthecurrentpointtothestart.Iftheshapehasalreadybeenclosedorthere'sonlyonepointinthelist,thisfunctiondoesnothing.第三步是调用closePath方法,它会尝试用直线连接当前端点与起始端点来关闭路径,但如果图形已经关闭或者只有一个点,它会什么都不做。这一步不是必须的。Thefinalstepwillbecallingthestrokeand/orfillmethods.Callingoneofthesewillactuallydrawtheshapetothecanvas.strokeisusedtodrawanoutlinedshape,whilefillisusedtopaintasolidshape.最后一步是调用stroke或fill方法,这时,图形才是实际的绘制到canvas上去。stroke是绘制图形的边框,fill会用填充出一个实心图形。Note:Whencallingthefillmethodanyopenshapeswillbeclosedautomaticallyanditisn'tnecessarytousetheclosePathmethod.注意:当调用fill时,开放的路径会自动闭合,而无须调用closePath。Thecodeforadrawingsimpleshape(atriangle)wouldlooksomethinglikethis.画一个简单图形(如三角形)的代码如下。ctx.beginPath(); ctx.moveTo(75,50);ctx.lineTo(100,75);ctx.lineTo(100,25);ctx.fill();moveToOneveryusefulfunction,whichdoesn'tactuallydrawanything,butispartofthepathlistdescribedabove,isthemoveTofunction.Youcanprobablybestthinkofthisasliftingapenorpencilfromonespotonapieceofpaperandplacingitonthenext.moveTo是一个十分有用的方法,虽然并不能用它来画什么,但却是绘制路径的实用方法的一部分。你可以把它想象成是把笔提起,并从一个点移动到另一个点的过程。moveTo(x,y)ThemoveTofunctiontakestwoarguments-xandy,-whicharethecoordinatesofthenewstartingpoint.它接受x和y(新的坐标位置)作为参数。WhenthecanvasisinitializedorthebeginPathmethodiscalled,thestartingpointissettothecoordinate(0,0).InmostcaseswewouldusethemoveTomethodtoplacethestartingpointsomewhereelse.WecouldalsousethemoveTomethodtodrawunconnectedpaths.Takealookatthesmileyfaceontheright.I'vemarkedtheplaceswhereIusedthemoveTomethod(theredlines).当canvas初始化或者调用beginPath的时候,起始坐标设置就是原点(0,0)。大多数情况下,我们用moveTo方法将起始坐标移至其它地方,或者用于绘制不连续的路径。看看右边的笑脸,红线就是使用moveTo移动的轨迹。Totrythisforyourself,youcanusethecodesnippetbelow.Justpasteitintothedrawfunctionwesawearlier.试一试下面的代码,粘贴到之前用过的draw函数内在看看效果吧。moveTo的使用示例观看示例 ctx.beginPath();ctx.arc(75,75,50,0,Math.PI*2,true);//Outercirclectx.moveTo(110,75);ctx.arc(75,75,35,0,Math.PI,false);//Mouth(clockwise)ctx.moveTo(65,65);ctx.arc(60,65,5,0,Math.PI*2,true);//Lefteyectx.moveTo(95,65);ctx.arc(90,65,5,0,Math.PI*2,true);//Righteyectx.stroke();//thegoneheart完整例子ctx.beginPath();ctx.arc(75,75,50,0,Math.PI*2,true);//Outercirclectx.moveTo(110,75);ctx.arc(75,75,35,0,Math.PI,false);//Mouth(clockwise)ctx.moveTo(65,65);ctx.arc(60,65,5,0,Math.PI*2,true);//Lefteyectx.moveTo(95,65);ctx.arc(90,65,5,0,Math.PI*2,true);//Righteyectx.stroke();ctx.beginPath();ctx.moveTo(40,75);ctx.lineTo(60,65);ctx.lineTo(90,65);ctx.moveTo(110,75);ctx.lineTo(125,75);ctx.stroke();Note:removethemoveTomethodstoseetheconnectinglines.Note:Foradescriptionofthearcfunctionanditsparameterslookbelow. 注意:你可以注释moveTo方法来观察那些连接起来的线。注意:arc方法的用法见下面。绘制各种线条LinesFordrawingstraightlinesweusethelineTomethod.我们用lineTo方法来画直线。lineTo(x,y)Thismethodtakestwoarguments-xandy,-whicharethecoordinatesoftheline'sendpoint.Thestartingpointisdependentonpreviousdrawnpaths,wheretheendpointofthepreviouspathisthestartingpointforthefollowing,etc.ThestartingpointcanalsobechangedbyusingthemoveTomethod.lineTo方法接受终点的坐标(x,y)作为参数。起始坐标取决于前一路径,前一路径的终点即当前路径的起点,起始坐标也可以通过moveTo方法来设置。lineTo的使用示例Intheexamplebelowtwotrianglesaredrawn,onefilledandoneoutlined.(Theresultcanbeseenintheimageontheright).FirstthebeginPathmethodiscalledtobeginanewshapepath.WethenusethemoveTomethodtomovethestartingpointtothedesiredposition.Belowthistwolinesaredrawnwhichmakeuptwosidesofthetriangle.示例(如右图)画的是两个三角形,一个实色填充,一个勾边。首先调用beginPath方法创建一个新路径,然后用moveTo方法将起始坐标移至想要的位置,然后画两条直线来构成三角形的两条边。You'llnoticethedifferencebetweenthefilledandstrokedtriangle.Thisis,asmentionedabove,becauseshapesareautomaticallyclosedwhenapathisfilled.Ifwewouldhavedonethisforthestrokedtriangleonlytwolineswouldhavebeendrawn,notacompletetriangle.可以注意到fill和strok绘三角形的区别,上面也提到过,使用fill路径会自动闭合,但使用stroke不会,如果不关闭路径,勾画出来的只有两边。观看示例 //填充三角形ctx.beginPath();ctx.moveTo(25,25);ctx.lineTo(105,25);ctx.lineTo(25,105);ctx.fill();//勾边三角形ctx.beginPath();ctx.moveTo(125,125);ctx.lineTo(125,45);ctx.lineTo(45,125);ctx.closePath();ctx.stroke();弧线ArcsFordrawingarcsorcirclesweusethearcmethod.ThespecificationalsodescribesthearcTomethod,whichissupportedbySafaributhasn'tbeenimplementedinthecurrentGeckobrowsers.我们用arc方法来绘制弧线或圆。标准说明中还包含arcTo方法,当前Safari是支持的,但基于Gecko的浏览器还未实现。arc(x,y,radius,startAngle,endAngle,anticlockwise)Thismethodtakesfiveparameters:xandyarethecoordinatesofthecircle'scenter.Radiusisselfexplanatory.ThestartAngleandendAngleparametersdefinethestartandendpointsofthearcinradians.Thestartingandclosinganglearemeasuredfromthexaxis.Theanticlockwiseparameterisabooleanvaluewhichwhentruedrawsthearcanticlockwise,otherwiseinaclockwisedirection.方法接受五个参数:x,y是圆心坐标,radius是半径,startAngle和endAngle分别是起末弧度(以x轴为基准),anticlockwise为true表示逆时针,反之顺时针。Warning:IntheFirefoxbetabuilds,thelastparameterisclockwise.Thefinalreleasewillsupportthefunctionasdescribedabove.Allscriptsthatusethismethodinitscurrentformwillneedtobeupdatedoncethefinalversionisreleased. 警告:在Firefox的beta版本里,最后一个参数是clockwise,而最终版本不是。因此如果是从beta升级至发行版需要做相应修改。Note:Anglesinthearcfunctionaremeasuredinradians,notdegrees.ToconvertdegreestoradiansyoucanusethefollowingJavaScriptexpression:varradians=(Math.PI/180)*degrees.注意:arc方法里用到的角度是以弧度为单位而不是度。度和弧度直接的转换可以用这个表达式:varradians=(Math.PI/180)*degrees;。arc的使用示例Thefollowingexampleisalittlemorecomplexthantheoneswe'veseenabove.I'vedrawn12differentarcsallwithdifferentanglesandfills.IfIwouldhavewrittenthisexamplejustlikethesmileyfaceabove,firstlythiswouldhavebecomeaverylonglistofstatementsandsecondly,whendrawingarcs,Iwouldneedtoknoweverysinglestartingpoint.Forarcsof90,180and270degrees,liketheonesIusedhere,thiswouldn'tbetomuchofaproblem,butformorecomplexonesthisbecomeswaytoodifficult.这个示例比之前见到过的要复杂一些,画了12个不同的弧形,有不同夹角和填充状态的。如果我用上面画笑脸的方式来画这些弧形,那会是一大段的代码,而且,画每一个弧形时我都需要知道其圆心位置。像我这里画90,180和270度的弧形看起来不是很麻烦,但是如果图形更复杂一些,则实现起来会越来越困难。Thetwoforloopsareforloopingthroughtherowsandcolumnsofarcs.ForeveryarcIstartanewpathusingbeginPath.BelowthisI'vewrittenoutalltheparametersasvariables,soit'seasiertoreadwhat'sgoingon.Normallythiswouldbejustonestatement.Thexandycoordinatesshouldbeclearenough.radiusandstartAnglearefixed.TheendAnglestartsofas180degrees(firstcolumn)andisincreasedwithstepsof90degreestoformacompletecircle(lastcolumn).Thestatementfortheclockwiseparameterresultsinthefirstandthirdrowbeingdrawnasclockwisearcsandthesecondandfourthrowascounterclockwisearcs.Finally,theifstatementmakesthetophalfstrokedarcsandthebottomhalffilledarcs.这里使用两个for循环来画多行多列的弧形。每一个弧形都用beginPath方法创建一个新路径。然后为了方便阅读和理解,我把所有参数都写成变量形式。显而易见,x和y作为圆心坐标。radius和startAngle都是固定,endAngle从180度半圆开始,以90度方式递增至圆。anticlockwise则取决于奇偶行数。最后,通过if语句判断使前两行表现为勾边,而后两行为填充效果。观看示例 for(i=0;i<4;i++){for(j=0;j<3;j++){//chinese_xu原始代码ctx.beginPath();varx=25+j*50;//xcoordinatevary=25+i*50;//ycoordinatevarradius=20;//ArcradiusvarstartAngle=0;//StartingpointoncirclevarendAngle=Math.PI+(Math.PI*j)/2;//Endpointoncirclevaranticlockwise=i%2==0?false:true;//clockwiseoranticlockwisectx.arc(x,y,radius,startAngle,endAngle,anticlockwise);if(i>1){ctx.fill();}else{ctx.stroke();}}}//chinese_xu原始代码并没有按照1/4圆递增来画。//修改后输出4行4列,要把画布扩大到200*200观看for(i=0;i<4;i++){for(j=0;j<4;j++){ctx.beginPath();varx=25+j*50;//xcoordinatevary=25+i*50;//ycoordinatevarradius=20;//ArcradiusvarstartAngle=0;//StartingpointoncirclevarendAngle=Math.PI*(2-j/2);//Endpointoncirclevaranticlockwise=i%2==0?false:true;//clockwiseoranticlockwisectx.arc(x,y,radius,startAngle,endAngle,anticlockwise); if(i>1){ctx.fill();}else{ctx.stroke();}}}贝塞尔和二次方曲线BezierandquadraticcurvesThenexttypeofpathsavailableareBéziercurves,availableinthecubicandquadraticvarieties.Thesearegenerallyusedtodrawcomplexorganicshapes.接下来要介绍的路径是贝塞尔曲线,它可以是二次和三次方的形式,一般用于绘制复杂而有规律的形状。quadraticCurveTo(cp1x,cp1y,x,y)//BROKENinFirefox1.5(seeworkaroundbelow)bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)Thedifferencebetweenthesecanbestbedescribedusingtheimageontheright.AquadraticBéziercurvehasastartandanendpoint(bluedots)andjustonecontrolpoint(reddot)whileacubicBéziercurveusestwocontrolpoints.上面两行代码的区别见右图。它们都有一个起点一个终点(图中的蓝点),但二次方贝塞尔曲线只有一个(红色)控制点点)而三次方贝塞尔曲线有两个。Thexandyparametersinboththesemethodsarethecoordinatesoftheendpoint.cp1xandcp1yarethecoordinatesofthefirstcontrolpoint,andcp2xandcp2yarethecoordinatesofthesecondcontrolpoint.参数x和y是终点坐标,cp1x和cp1y是第一个控制点的坐标,cp2x和cp2y是第二个的。UsingquadraticandcubicBéziercurvescanbequitechallenging,becauseunlikevectordrawingsoftwarelikeAdobeIllustrator,wedon'thavedirectvisualfeedbackastowhatwe'redoing.Thismakesitprettyhardtodrawcomplexshapes.Inthefollowingexample,we'llbedrawingsomesimpleorganicshapes,butifyouhavethetimeand,mostofall,thepatience,muchmorecomplexshapescanbecreated. 使用二次方和三次方的贝塞尔曲线是相当有挑战的,因为不像在矢量绘图软件AdobeIllustrator里那样有即时的视觉反馈。因为用它来画复杂图形是比较麻烦的。但如果你有时间,并且最重要是有耐心,再复杂的图形都可以绘制出来的。下面我们来画一个简单而又规律的图形。There'snothingverydifficultintheseexamples.Inbothcasesweseeasuccessionofcurvesbeingdrawnwhichfinallyresultinacompleteshape.这些例子都比较简单。我们绘制的都是完整的图形。quadraticCurveTo的使用示例查看示例//Quadratriccurvesexamplectx.beginPath();ctx.moveTo(75,25);ctx.quadraticCurveTo(25,25,25,62.5);ctx.quadraticCurveTo(25,100,50,100);ctx.quadraticCurveTo(50,120,30,125);ctx.quadraticCurveTo(60,120,65,100);ctx.quadraticCurveTo(125,100,125,62.5);ctx.quadraticCurveTo(125,25,75,25);ctx.stroke();ItispossibletoconvertanyquadraticBéziercurvetoacubicBéziercurvebycorrectlycomputingbothcubicBéziercontrolpointsfromthesinglequadraticBéziercontrolpoint,althoughthereverseisNOTtrue.AnexactconversionofacubicBéziercurvetoaquadraticBéziercurveisonlypossibleifthecubictermiszero,morecommonlyasubdivisionmethodisusedtoapproximateacubicBézierusingmultiplequadraticBéziercurves.通过计算,可以由二次曲线的单个控制点得出相应三次方曲线的两个控制点,因此二次方转三次方是可能的,但是反之不然。仅当三次方程中的三次项为零是才可能转换为二次的贝塞尔曲线。通常地可以用多条二次方曲线通过细分算法来近似模拟三次方贝塞尔曲线。bezierCurveTo的使用示例查看示例 //Beziercurvesexamplectx.beginPath();ctx.moveTo(75,40);ctx.bezierCurveTo(75,37,70,25,50,25);ctx.bezierCurveTo(20,25,20,62.5,20,62.5);ctx.bezierCurveTo(20,80,40,102,75,120);ctx.bezierCurveTo(110,102,130,80,130,62.5);ctx.bezierCurveTo(130,62.5,130,25,100,25);ctx.bezierCurveTo(85,25,75,37,75,40);ctx.fill();Firefox1.5quadraticCurveTo()bug的应对方案ThereisabugintheFirefox1.5implementationofquadatricCurveTo().ItdoesNOTdrawaquadraticcurve,asitisjustcallingthesamecubiccurvefunctionbezierCurveTo()calls,andrepeatingthesinglequadraticcontrolpoint(x,y)coordinatetwice.ForthisreasonquadraticCurveTo()willyieldincorrectresults.IfyourequiretheuseofquadraticCurveTo()youmustconvertyourquadraticBéziercurvetoacubicBéziercurveyourself,soyoucanusetheworkingbezierCurveTo()method.在Firefox1.5里,quadatricCurveTo()的实现是有bug的,它不是直接绘制二次方曲线,而是调用bezierCurveTo(),其中两个控制点都是二次方曲线的那个单控制点。因此,它会绘制出不正确的曲线。如果必须使用到quadraticCurveTo(),你需要自行去将二次方曲线转换成三次方的,这样就可以用bezierCurveTo()方法了。varcurrentX,currentY;//settolastx,ysenttolineTo/moveTo/bezierCurveToorquadraticCurveToFixed()functionquadraticCurveToFixed(cpx,cpy,x,y){/*Fortheequationsbelowthefollowingvariablenameprefixesareused:qp0isthequadraticcurvestartingpoint(youmustkeepthisfromyourlastpointsenttomoveTo(),lineTo(),orbezierCurveTo()).qp1isthequadatriccurvecontrolpoint(thisisthecpx,cpyyouwouldhavesenttoquadraticCurveTo()).qp2isthequadraticcurveendingpoint(thisisthex,yargumentsyouwouldhavesenttoquadraticCurveTo()).Wewillconvertthesepointstocomputethetwoneededcubiccontrolpoints(thestarting/endingpointsarethesameforboth thequadraticandcubiccurves.Theequationsforthetwocubiccontrolpointsare:cp0=qp0andcp3=qp2cp1=qp0+2/3*(qp1-qp0)cp2=cp1+1/3*(qp2-qp0)Inthecodebelow,wemustcomputeboththexandytermsforeachpointseparately.cp1x=qp0x+2.0/3.0*(qp1x-qp0x);cp1y=qp0y+2.0/3.0*(qp1y-qp0y);cp2x=cp1x+(qp2x-qp0x)/3.0;cp2y=cp1y+(qp2y-qp0y)/3.0;Wewillnowa)replacetheqp0xandqp0yvariableswithcurrentXandcurrentY(which*you*muststoreforeachmoveTo/lineTo/bezierCurveTo)b)replacetheqp1xandqp1yvariableswithcpxandcpy(whichwewouldhavepassedtoquadraticCurveTo)c)replacetheqp2xandqp2yvariableswithxandy.whichleavesuswith:*/varcp1x=currentX+2.0/3.0*(cpx-currentX);varcp1y=currentY+2.0/3.0*(cpy-currentY);varcp2x=cp1x+(x-currentX)/3.0;varcp2y=cp1y+(y-currentY)/3.0;//andnowcallcubicBeziercurvetofunctionbezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);currentX=x;currentY=y;} 矩形路径RectanglesBesidesthethreemethodswesawabovewhichdrawrectangularshapesdirectlytothecanvas,wealsohaveamethodrectwhichaddsarectangularpathtothepathlist.除了上面提到的三个方法可以直接绘制矩形之外,我们还有一个rect方法是用于绘制矩形路径的。rect(x,y,width,height)Thismethodtakesfourarguments.Thexandyparametersdefinethecoordinateofthetopleftcornerofthenewrectangularpath.widthandheightdefinethewidthandtheheightoftherectangle.它接受四个参数,x和y是其左上角坐标,width和height是其宽和高。Whenthismethodisexecuted,themoveTomethodisautomaticallycalledwiththeparameters(0,0)(i.e.itresetsthestartingpointtoitsdefaultlocation).当它被调用时,moveTo方法会自动被调用,参数为(0,0),于是起始坐标又恢复成初始原点了。综合MakingcombinationsInallexamplesonthispageI'veonlyusedonetypeofpathfunctionpershape.Howeverthere'sabsolutelynolimitationtotheamountortypeofpathsyoucanusetocreateashape.SointhislastexampleI'vetriedtocombineallofthepathfunctionstomakeasetofveryfamousgamecharacters.上面所用到的例子都只用到了一种类型的路径,当然canvas不会限制所使用的路径类型的多少。所以,我们来看一个路径大杂烩。综合样例I'mnotgoingtorunthroughthiscompletescript,butthemostimportantthingstonotearethefunctionroundedRectandtheuseofthefillStyleproperty.Itcanbeveryusefullandtimesavingtodefineyourownfunctionstodrawmorecomplexshapes.InthisscriptitwouldhavetakenmetwiceasmanylinesofcodeasIhavenow.WewilllookatthefillStylepropertyingreaterdepthlaterinthistutorial.HereI'musingittochangethefillcolorfromthedefaultblack,towhite,andbackagain. 在整个例子里,最值得注意的是roundedRect函数的使用和fillStyle属性的设置。自定义函数对于封装复杂图形的绘制是非常有用的。在这个例子里使用自定义函数就省掉了大约一半的代码。在接下来的例子里会深入探讨fillStyle属性的使用。这里是用它来改变填充颜色,从默认的黑色,到白色,然后再回到黑色。查看示例functiondraw(){varctx=document.getElementById('canvas').getContext('2d');roundedRect(ctx,12,12,150,150,15);roundedRect(ctx,19,19,150,150,9);roundedRect(ctx,53,53,49,33,10);roundedRect(ctx,53,119,49,16,6);roundedRect(ctx,135,53,49,33,10);roundedRect(ctx,135,119,25,49,10);ctx.beginPath();ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false);//chiensexu本来是true呵呵,反了ctx.lineTo(31,37);ctx.fill();for(i=0;i<8;i++){ctx.fillRect(51+i*16,35,4,4);}for(i=0;i<6;i++){ctx.fillRect(115,51+i*16,4,4);}for(i=0;i<8;i++){ctx.fillRect(51+i*16,99,4,4);}ctx.beginPath();ctx.moveTo(83,116); ctx.lineTo(83,102);ctx.bezierCurveTo(83,94,89,88,97,88);ctx.bezierCurveTo(105,88,111,94,111,102);ctx.lineTo(111,116);ctx.lineTo(106.333,111.333);ctx.lineTo(101.666,116);ctx.lineTo(97,111.333);ctx.lineTo(92.333,116);ctx.lineTo(87.666,111.333);ctx.lineTo(83,116);ctx.fill();ctx.fillStyle="white";ctx.beginPath();ctx.moveTo(91,96);ctx.bezierCurveTo(88,96,87,99,87,101);ctx.bezierCurveTo(87,103,88,106,91,106);ctx.bezierCurveTo(94,106,95,103,95,101);ctx.bezierCurveTo(95,99,94,96,91,96);ctx.moveTo(103,96);ctx.bezierCurveTo(100,96,99,99,99,101);ctx.bezierCurveTo(99,103,100,106,103,106);ctx.bezierCurveTo(106,106,107,103,107,101);ctx.bezierCurveTo(107,99,106,96,103,96);ctx.fill();ctx.fillStyle="black";ctx.beginPath();ctx.arc(101,102,2,0,Math.PI*2,true);ctx.fill();ctx.beginPath();ctx.arc(89,102,2,0,Math.PI*2,true);ctx.fill();}functionroundedRect(ctx,x,y,width,height,radius){ ctx.beginPath();ctx.moveTo(x,y+radius);ctx.lineTo(x,y+height-radius);ctx.quadraticCurveTo(x,y+height,x+radius,y+height);ctx.lineTo(x+width-radius,y+height);ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);ctx.lineTo(x+width,y+radius);ctx.quadraticCurveTo(x+width,y,x+width-radius,y);ctx.lineTo(x+radius,y);ctx.quadraticCurveTo(x,y,x,y+radius);ctx.stroke();}1.3使用图像应用图像UsingimagesOneofthemorefunfeaturesofthecanvasistheabillitytouseimages.Thesecanbeusedtododynamicphotocompositingorusedasbackdropsofgraphsetc.It'scurrentlyalsotheonlywaytoaddtexttothem(Thespecificationdoesnotcontainanyfunctionstodrawtext).ExternalimagescanbeusedinanyformatsupportedbyGecko(e.g.PNG,GIForJPEGformat).Othercanvaselementsonthesamepagecanalsobeusedasthesource.Canvas相当有趣的一项功能就是可以引入图像,它可以用于图片合成或者制作背景等。而目前仅可以在图像中加入文字(标准说明中并没有包含绘制文字的功能)。只要是Gecko支持的图像(如PNG,GIF,JPEG等)都可以引入到canvas中,而且其它的canvas元素也可以作为图像的来源。引入图像ImportingimagesImportingimagesisbasicallyatwostepprocess:引入图像只需要简单的两步:FirstlyweneedareferencetoaJavaScriptImageobjectorothercanvaselementasasource.Itisn'tpossibletouseimagesbysimplyprovidingaURL/pathtothem.SecondlywedrawtheimageonthecanvasusingthedrawImagefunction. 第一当然是来源图片,不是简单的URL路径,但可以是一个JavaScript的Image对象引用,又或者其它的canvas元素。然后用drawImage方法将图像插入到canvas中。Let'slookatsteponefirst.Therearebasicallyfouroptionsavailable:先来看看第一步,基本上有四种可选方式:引用页面内的图片UsingimageswhichareonthesamepageWecanaccessallimagesonapagebyusingeitherthedocument.imagescollection,thedocument.getElementsByTagNamemethod,orifweknowtheIDattributeoftheimage,thedocument.getElementByIdmethod.我们可以通过document.images集合、document.getElementsByTagName方法又或者document.getElementById方法来获取页面内的图片(如果已知图片元素的ID。使用其它canvas元素UsingothercanvaselementsJustaswithnormalimagesweaccessothercanvaselementsusingeitherthedocument.getElementsByTagNamemethodorthedocument.getElementByIdmethod.Makesureyou'vedrawnsomethingtothesourcecanvasbeforeusingitinyourtargetcanvas.和引用页面内的图片类似地,用document.getElementsByTagName或document.getElementById方法来获取其它canvas元素。但你引入的应该是已经准备好的canvas。Oneofthemorepracticalusesofthiswouldbetouseasecondcanvaselementasathumbnailviewoftheotherlargercanvas.一个常用的应用就是为另一个大的canvas做缩略图。由零开始创建图像CreatinganimagefromscratchAnotheroptionistocreatenewImageobjectsinourscript.Themaindisadvantageofthisapproachisthatifwedon'twantourscripttohaltinthemiddlebecauseitneedstowaitforanimagetoload,weneedsomeformofimagepreloading.另外,我们可以用脚本创建一个新的Image对象,但这种方法的主要缺点是如果不希望脚本因为等待图片装置而暂停,还得需要突破预装载。 Basicallytocreateanewimageobjectwedothis:我们可以通过下面简单的方法来创建图片:viewplainprint?varimg=newImage();//CreatenewImageobjectimg.src='myImage.png';//SetsourcepathWhenthisscriptgetsexecuted,theimagestartsloading.Ifloadingisn'tfinishedwhenadrawImagestatementgetsexecuted,thescripthaltsuntiltheimageisfinishedloading.Ifyoudon'twantthistohappen,useanonloadeventhandler:当脚本执行后,图片开始装载。若调用drawImage时,图片没装载完,脚本会等待直至装载完毕。如果不希望这样,可以使用onload事件:viewplainprint?varimg=newImage();//CreatenewImageobjectimg.onload=function(){//executedrawImagestatementshere}img.src='myImage.png';//SetsourcepathIfyou'reonlyusingoneexternalimagethiscanbeagoodapproachbutonceyouneedtotrackmorethanoneweneedtoresorttosomethingmorecunning.It'sbeyondthescopeofthistutorialtolookatimagepreloadingtacticsbutyoucancheckoutJavaScriptImagePreloaderforacompletesolution.如果你只用到一张图片的话,这已经够了。但一旦需要不止一张图片,那就需要更加复杂的处理方法,但图片预装载策略超出本教程的范围,感兴趣的话可以参考JavaScriptImagePreloader。通过data:url方式嵌入图像Embeddinganimageviadata:urlAnotherpossiblewaytoincludeimagesisviathedata:url.DataurlsallowyoutocompletelydefineanimageasaBase64encodedstringofcharactersdirectlyinyourcode.Oneadvantageofdataurlsisthattheresultingimageisavailableimmediatelywithoutanotherroundtriptotheserver.(AnotheradvantageisthatitisthenpossibletoencapsulateinonefileallofyourCSS,Javascript,HTML,andimages,makingitmoreportabletootherlocations.)Somedisadvantagesofthismethodarethatyourimageisnotcached,andforlargerimagestheencodedurlcanbecomequitelong: 我们还可以通过data:url方式来引用图像。Dataurls允许用一串Base64编码的字符串的方式来定义一个图片。其优点就是图片内容即时可用,无须再到服务器兜一圈。(还有一个优点是,可以将CSS,JavaScript,HTML和图片全部封装在一起,迁移起来十分方便。)缺点就是图像没法缓存,图片大的话内嵌的url数据会相当的长:viewplainprint?varimg_src='data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';drawImageOncewehaveareferencetooursourceimageobjectwecanusethedrawImagemethodtorenderittothecanvas.Aswewe'llseelaterthedrawImagemethodisoverloadedandhasthreedifferentvariants.Initsmostbasicformitlookslikethis.一旦获得了源图对象,我们就可以使用drawImage方法将它渲染到canvas里。drawImage方法有三种形态,下面是最基础的一种。drawImage(image,x,y)Whereimageisareferencetoourimageorcanvasobject.xandyformthecoordinateonthetargetcanvaswhereourimageshouldbeplaced.其中image是image或者canvas对象,x和y是其在目标canvas里的起始坐标。drawImage示例1InthefollowingexampleIwillbeusinganexternalimageasthebackdropofasmalllinegraph.Usingbackdropscanmakeyourscriptconsiderablysmallerbecausewedon'tneedtodrawanelaboratebackground.I'monlyusingoneimageheresoIusetheimageobject'sonloadeventhandlertoexecutethedrawingstatements.ThedrawImagemethodplacesthebackdroponthecoordinate(0,0)whichisthetopleftcornerofthecanvas.下面一个例子我用一个外部图像作为一线性图的背景。用背景图我们就不需要绘制负责的背景,省下不少代码。这里只用到一个image对象,于是就在它的onload事件响应函数中触发绘制动作。drawImage方法将背景图放置在canvas的左上角(0,0)处。查看示例 viewplainprint?functiondraw(){varctx=document.getElementById('canvas').getContext('2d');varimg=newImage();img.onload=function(){ctx.drawImage(img,0,0);ctx.beginPath();ctx.moveTo(30,96);ctx.lineTo(70,66);ctx.lineTo(103,76);ctx.lineTo(170,15);ctx.stroke();}img.src='images/backdrop.png';}缩放ScalingThesecondvariantofthedrawImagemethodaddstwonewparametersanditallowsustoplacescaledimagesonthecanvas.drawImage方法的又一变种是增加了两个用于控制图像在canvas中缩放的参数。drawImage(image,x,y,width,height)Wherewidthandheightistheimage'ssizeonthetargetcanvas.当中width和height分别是图像在canvas中显示大小。drawImage示例2InthisexampleI'mgoingtouseanimageasawallpaperandrepeatitseveraltimesonthecanvas.Thisisdonesimplybyloopingandplacingthescaledimagesatdifferentpositions.Inthecodebelowthefirstforloopsthroughtherowsthesecondforloopthecolumns.Theimageisscaledonethirdofitsoriginalsizewhichis50x38pixels.We'llseehowthiscouldalsohavebeenachieved,bycreatingacustompattern,laterinthistutorial. 在这个例子里,我会用一张图片像背景一样在canvas中以重复平铺开来。实现起来也很简单,只需要循环铺开经过缩放的图片即可。见下面的代码,第一层for循环是做行重复,第二层是做列重复的。图像大小被缩放至原来的三分之一,50x38px。这种方法可以用来很好的达到背景图案的效果,在下面的教程中会看到。Note:Imagescanbecomeblurrywhenscalinguporgrainyifthey'rescaleddowntoomuch.Scalingisprobablybestnotdoneifyou'vegotsometextinitwhichneedstoremainlegible.注意:图像可能会因为大幅度的缩放而变得起杂点或者模糊。如果您的图像里面有文字,那么最好还是不要进行缩放,因为那样处理之后很可能图像里的文字就会变得无法辨认了。查看示例viewplainprint?functiondraw(){varctx=document.getElementById('canvas').getContext('2d');varimg=newImage();img.onload=function(){for(i=0;i<4;i++){for(j=0;j<3;j++){ctx.drawImage(img,j*50,i*38,50,38);}}}img.src='images/rhino.jpg';}切片SlicingThethirdandlastvariantofthedrawImagemethodhaseightnewparameters.Wecanusethismethodtoslicepartsofasourceimageanddrawthemtothecanvas.drawImage方法的第三个也是最后一个变种有8个新参数,用于控制做切片显示的。drawImage(image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight) Thefirstparameterimage,justaswiththeothervariants,iseitherareferencetoanimageobjectorareferencetoadifferentcanvaselement.Fortheothereightparametesit'sbesttolookattheimageontheright.Thefirstfourparametersdefinethelocationandsizeofthesliceonthesourceimage.Thelastfourparametersdefinethepositionandsizeonthedestinationcanvas.第一个参数和其它的是相同的,都是一个图像或者另一个canvas的引用。其它8个参数最好是参照右边的图解,前4个是定义图像源的切片位置和大小,后4个则是定义切片的目标显示位置和大小。Slicingcanbeausefultoolwhenyouwanttomakecompositions.Youcouldhaveallelementsinasingleimagefileandusethismethodtocompositeacompletedrawing.Forinstance,ifyouwanttomakeachartyoucouldhaveaPNGimagecontainingallthenecessarytextinasinglefileanddependingonyourdatacouldchangethescaleofyourchartwithoutverymuchdiffculty.Anotheradvantageisthatyoudon'tneedtoloadeveryimageindividually.切片是个做图像合成的强大工具。假设有一张包含了所有元素的图像,那么你可以用这个方法来合成一个完整图像。例如,你想画一张图表,而手上有一个包含所有必需的文字的PNG文件,那么你可以很轻易的根据实际数据的需要来改变最终显示的图表。这方法的另一个好处就是你不需要单独装载每一个图像。drawImage示例3InthisexampleI'mgoingtousethesamerhinoaswe'veseenabove,butnowI'mgoingtosliceitsheadoutandcompositeitintoapictureframe.Theimageofthepictureframeincludesadropshadowwhichhasbeensavedasa24-bitPNGimage.Because24-bitPNGimagesincludeafull8-bitalphachannel,unlikeGIFand8-bitPNGimages,Icanplaceitontoanybackgroundanddon'thavetoworryaboutamattecolor.在这个例子里面我用到上面已经用过的犀牛图像,不过这次我要给犀牛头做个切片特写,然后合成到一个相框里面去。相框带有阴影效果,是一个以24-bitPNG格式保存的图像。因为24-bitPNG图像带有一个完整的8-bitalpha通道,与GIF和8-bitPNG不同,我可以将它放成背景而不必担心底色的问题。Itookadifferentapproachtotheloadingoftheimagesthantheexampleabove.IjustplacedtheimagesdirectlyinmyHTMLdocumentandusedaCSSruletohidethemfromview(display:none).Iassignedbothimagesanidattributetomakethemeasiertoselect.Thescriptitselfisverysimple.Ifirstdrawtheslicedandscaledimageonthecanvas(firstdrawImagestatement),andthenplacetheframeontop(seconddrawImagestatement). 我用一个与上面用到的不同的方法来装载图像,直接将图像插入到HTML里面,然后通过CSS隐藏(display:none)它。两个图像我都赋了id,方便后面使用。看下面的脚本,相当简单,首先对犀牛头做好切片(第一个drawImage)放在canvas上,然后再上面套个相框(第二个drawImage)。查看示例viewplainprint?functiondraw(){varcanvas=document.getElementById('canvas');varctx=canvas.getContext('2d');//Drawslicectx.drawImage(document.getElementById('source'),33,71,104,124,21,20,87,104);//Drawframectx.drawImage(document.getElementById('frame'),0,0);}示例:画廊ArtgalleryexampleInthefinalexampleofthischapterI'vemadealittleartgallery.Thegalleryconsistsofatablecontainingseveralimages.Whenthepageisloaded,foreachimageinthepageacanvaselementisinsertedandaframeisdrawnarroundit.我这一章最后的示例是弄一个小画廊。画廊由挂着几张画作的格子组成。当页面装载好之后,为每张画创建一个canvas元素并用加上画框然后插入到画廊中去。Inmycase,allimageshaveafixedwidthandheight,andsodoestheframethat'sdrawnaroundit.Youcouldenhancethescriptsothatitusestheimage'swidthandheighttomaketheframefitperfectlyaroundit.在我这个例子里面,所有“画”都是固定宽高的,画框也是。你可以做些改进,通过脚本用画的宽高来准确控制围绕它的画框的大小。Thecodebelowshouldbeself-explanatory.Weloopthroughtheimagesarrayandaddnewcanvaselementsaccordingly.Probablytheonlythingtonote,forthosenotsofamilarwiththeDOM,istheuseof theinsertBeforemethod.insertBeforeisamethodoftheparentnode(atablecell)oftheelement(theimage)beforewhichwewanttoinsertournewnode(thecanvaselement).下面的代码应该是蛮自我解释的了。就是遍历图像对象数组,依次创建新的canvas元素并添加进去。可能唯一需要注意的,对于那些并不熟悉DOM的朋友来说,是insertBefore方法的用法。insertBefore是父节点(单元格)的方法,用于将新节点(canvas元素)插入到我们想要插入的节点之前。查看示例viewplainprint?functiondraw(){//Loopthroughallimagesfor(i=0;i20000)break;varx2=(R+r)*Math.cos(i*Math.PI/72)-(r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))vary2=(R+r)*Math.sin(i*Math.PI/72)-(r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))ctx.lineTo(x2,y2);x1=x2;y1=y2;i++;}while(x2!=R-O&&y2!=0);ctx.stroke();}旋转RotatingThesecondtransformationmethodisrotate.Weuseittorotatethecanvasaroundthecurrentorigin. 第二个介绍rotate方法,它用于以原点为中心旋转canvas。rotate(angle)Thismethodonlytakesoneparameterandthat'stheanglethecanvasisrotatedby.Thisisaclockwiserotationmeasuredinradians(illustratedintheimageontheright).这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。Therotationcenterpointisalwaysthecanvasorigin.Tochangethecenterpoint,wewillneedtomovethecanvasbyusingthetranslatemethod.旋转的中心点始终是canvas的原点,如果要改变它,我们需要用到translate方法。rotate的例子Intheexample,youcanseeontheright,Iusedtherotatemethodtodrawshapesinacircularpattern.Youcouldalsohavecalculatedtheindividualxandycoordinates(x=r*Math.cos(a);y=r*Math.sin(a)).Inthiscaseitdoesn'treallymatterwhichmethodyouchoose,becausewe'redrawingcircles.Calculatingthecoordinatesresultsinonlyrotatingthecenterpositionsofthecirclesandnotthecirclesthemselves,whileusingrotateresultsinboth,butofcoursecircleslookthesamenomatterhowfartheyarerotatedabouttheircenters.在这个例子里,见右图,我用rotate方法来画圆并构成圆形图案。当然你也可以分别计算出x和y坐标(x=r*Math.cos(a);y=r*Math.sin(a))。这里无论用什么方法都无所谓的,因为我们画的是圆。计算坐标的结果只是旋转圆心位置,而不是圆本身。即使用rotate旋转两者,那些圆看上去还是一样的,不管它们绕中心旋转有多远。Againwehavetwoloops.Thefirstdeterminesthenumberofrings,andtheseconddeterminesthenumberofdotsdrawnineachring.Beforedrawingeachring,Isavethecanvasstate,soIcaneasilyretrieveit.Foreachdotthatisdrawn,Irotatethecanvascoordinatespacebyananglethatisdeterminedbythenumberofdotsinthering.Theinnermostcirclehassixdots,soineachstep,Irotateoveranangleof360/6=60degrees.Witheachadditionalring,thenumberofdotsisdoubled,andtheangleinturnishalved.这里我们又用到了两层循环。第一层循环决定环的数量,第二层循环决定每环有多少个点。每环开始之前,我都保存一下canvas的状态,这样恢复起来方便。每次画圆点,我都以一定夹角来旋转canvas,而这个夹角则是由环上的圆点数目的决定的。最里层的环有6个圆点,这样,每次旋转的 夹角就是360/6=60度。往外每一环的圆点数目是里面一环的2倍,那么每次旋转的夹角随之减半。查看示例viewplainprint?functiondraw(){varctx=document.getElementById('canvas').getContext('2d');ctx.translate(75,75);for(vari=1;i<6;i++){//Loopthroughrings(frominsidetoout)ctx.save();ctx.fillStyle='rgb('+(51*i)+','+(255-51*i)+',255)';for(varj=0;j=12?hr-12:hr;ctx.fillStyle="black";//writeHoursctx.save();ctx.rotate(hr*(Math.PI/6)+(Math.PI/360)*min+(Math.PI/21600)*sec)ctx.lineWidth=14;ctx.beginPath();ctx.moveTo(-20,0);ctx.lineTo(80,0);ctx.stroke();ctx.restore();//writeMinutesctx.save();ctx.rotate((Math.PI/30)*min+(Math.PI/1800)*sec)ctx.lineWidth=10;ctx.beginPath();ctx.moveTo(-28,0);ctx.lineTo(112,0);ctx.stroke();ctx.restore(); //Writesecondsctx.save();ctx.rotate(sec*Math.PI/30);ctx.strokeStyle="#D40000";ctx.fillStyle="#D40000";ctx.lineWidth=6;ctx.beginPath();ctx.moveTo(-30,0);ctx.lineTo(83,0);ctx.stroke();ctx.beginPath();ctx.arc(0,0,10,0,Math.PI*2,true);ctx.fill();ctx.beginPath();ctx.arc(95,0,10,0,Math.PI*2,true);ctx.stroke();ctx.fillStyle="#555";ctx.arc(0,0,3,0,Math.PI*2,true);ctx.fill();ctx.restore();ctx.beginPath();ctx.lineWidth=14;ctx.strokeStyle='#325FA2';ctx.arc(0,0,142,0,Math.PI*2,true);ctx.stroke();ctx.restore();}动画例子3Thisisthecodeforaleft-to-rightloopingpanoramicimagescroller.Makesuretheimageislargerthanthecanvas. 这是一个从左到右滚动的全景动画的代码。请注意图片的大小需要比canvas大。本例中用的是这张图片:http://commons.wikimedia.org/wiki/File:Capitan_Meadows,_Yosemite_National_Park.jpgviewplainprint?varimg=newImage();//UserVariablesimg.src='Capitan_Meadows,_Yosemite_National_Park.jpg';varCanvasXSize=800;varCanvasYSize=200;varspeed=30;//lowerisfastervarscale=1.05;vary=-4.5;//verticaloffset//EndUserVariablesvardx=0.75;varimgW=img.width*scale;varimgH=img.height*scale;varx=0;if(imgW>CanvasXSize){x=CanvasXSize-imgW;}//imagelargerthancanvasvarclearX;varclearY;if(imgW>CanvasXSize){clearX=imgW;}//imagelargerthancanvaselse{clearX=CanvasXSize;}if(imgH>CanvasYSize){clearY=imgH;}//imagelargerthancanvaselse{clearY=CanvasYSize;}varctx;functioninit(){//GetCanvasElementctx=document.getElementById('canvas').getContext('2d'); //SetRefreshRatereturnsetInterval(draw,speed);}functiondraw(){//ClearCanvasctx.clearRect(0,0,clearX,clearY);//Ifimageis<=CanvasSizeif(imgW<=CanvasXSize){//reset,startfrombeginningif(x>(CanvasXSize)){x=0;}//drawaditionalimageif(x>(CanvasXSize-imgW)){ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH);}}//Ifimageis>CanvasSizeelse{//reset,startfrombeginningif(x>(CanvasXSize)){x=CanvasXSize-imgW;}//drawaditionalimageif(x>(CanvasXSize-imgW)){ctx.drawImage(img,x-imgW+1,y,imgW,imgH);}}//drawimagectx.drawImage(img,x,y,imgW,imgH);//amounttomovex+=dx;}htmlcode.CanvaswidthandheightshouldmatchtheCanvasXSize,CanvasYSize.viewplainprint?其它例子粒子喷泉(ParticleFountain) 你可以用鼠标来控制物理粒子的产生。Canvascape一个3D(第一人称射击)冒险游戏。http://www.gradius-js.com一个经典的2D太空射击游戏。ABasicRayCaster怎样使用键盘操控动画效果的好例子。canvasadventure同样是一个键盘操控动画的好例子。AninteractiveBlob有趣的泡泡。Flyingthroughastarfield在星星,圆圈,方块间滑翔。iGrapher股票数据图表的例子。1.8参考资料:1.8.1https://developer.mozilla.org/cn/Canvas_tutorialSeedocument:Canvas_tutorialCanvas教程Canvastutorial-MDCSeedocument:Canvas_tutorialBasicusage-MDCSeedocument:Basic_usageDrawingshapes绘制图形-MDCSeedocument:Drawing_shapes应用图像Usingimages-MDCSeedocument:Using_images运用样式与颜色-MDCSeedocument:Applying_styles_and_colors变形Transformations-MDCSeedocument:Transformations 组合Compositing-MDCSeedocument:Compositing基本的动画-MDCSeedocument:Basic_animations

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭