用HTML5 Canvas制作摆动的树
下载源代码
〖 作者:cyclegtx 〗〖 发布日期:2014-07-05 〗
根据工作的需要,制作一个摆动的树做为页面的背景。为了增加页面的交互性,我又为背景中的树增加了鼠标(触控)事件,使他能够根据鼠标(触控)做出相应的动作,当手指做上下或者左右滑动的时候树会跟着摆动。先看看最终效果。
Step1.完成HTML页面,新建一个Tree类
完成HTML页面后新建一个Tree类用来记录树的各个属性。其中x,y为树根部的坐标值,branchLen,branchWidth分别是树枝的长度与宽度,depth为树枝的层数,canvas用来接页面中的canvas元素(默认是ID为canvas的元素)。
<html>
<meta charset="utf-8" />
<head>
<style>
body {
margin: 0
background: #7ACFFA
}
#canvas {
position: absolute
top: 0left: 0
}
</style></head><body>
<canvas id="canvas" width="1" height="1"></canvas>
<script type='text/javascript'>
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60)
}
})()
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
function Tree(x,y,branchLen,branchWidth,depth,canvas){
this.canvas = canvas || document.getElementById('canvas')
this.ctx = this.canvas.getContext('2d')
this.x = x||0
this.y = y||0
this.branchLen = branchLen||0
this.branchWidth = branchWidth||0
var depth = depth || 5
}
</script>
</body></html>
Step2.添加drawRoot方法,用来绘制树干
首先在drawRoot中画第一个枝干。drawRoot的参数意义同上。并且在Tree类的构造函数中运行drawRoot并把Tree接受到的参数传入。最后new一个Tree类,使树根位于屏幕的底部正中心,树枝长100px,树枝宽度为8px,树枝层数为8层(暂时用不上)。
var atree = new Tree(canvas.width/2-4,canvas.height,100,8,8,canvas)
在drawRoot中我们需要用lineTo()画出树枝。树枝的起始的坐标值(x,y)已经给出,结束的坐标值(toX,toY)需要进行计算。第一个画的是树干,由于树干垂直于地面所以结束坐标toX等于初始坐标x,而结束坐标toY等于初始y减去树干长度branchLen(注意坐标的0,0点在canvas的左上角)。
var toX = xvar toY = y-branchLen
function Tree(x,y,branchLen,branchWidth,depth,canvas){
this.canvas = canvas || document.getElementById('canvas')
this.ctx = this.canvas.getContext('2d')
this.x = x||0
this.y = y||0
this.branchLen = branchLen||0
this.branchWidth = branchWidth||0
var depth = depth || 5
this.drawRoot(this.x,this.y,this.branchLen,this.branchWidth)
}
Tree.prototype.drawRoot = function(x,y,branchLen,branchWidth){
var toX = x
var toY = y-branchLen
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
}
var atree = new Tree(canvas.width/2-4,canvas.height,100,8,8,canvas)
运行代码:
Step3.添加drawBranch方法,用来绘制树枝
drawBranch同样是根据初始与结束坐标画出一条直线代表树枝。与树干不同的是树枝不再是垂直与地面而是与树干保持一定的角度,而且树枝的初始值是树干的结束点(toX,toY)。所以在drawBranch中我们加入新参数angle用来表示树枝与树干的垂直夹角α,这样就可以根据α算出toX与toY。请看图。
这样我们在画完树干后再分别画两个不同角度的树枝,一个是30°一个-30°。并将传给树枝的宽度branchWidth减小一个像素,使其与树干粗细不同。
Tree.prototype.drawRoot = function(x,y,branchLen,branchWidth){
var toX = x
var toY = y-branchLen
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
this.drawBranch(toX,toY,branchLen,branchWidth-1,30)
this.drawBranch(toX,toY,branchLen,branchWidth-1,-30)
}
Tree.prototype.drawBranch = function(x,y,branchLen,branchWidth,angle){
var angle = angle || 0
var radian = (90-angle)*(Math.PI/180)
var toX = x+Math.cos(radian)*branchLen
var toY = y-Math.sin(radian)*branchLen
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
}
运行代码:
Step4.修改drawBranch函数,重复画树枝
在drawBranch函数的最后再次调用两次drawBranch
this.drawBranch(toX,toY,branchLen,branchWidth-1,angle+30)
this.drawBranch(toX,toY,branchLen,branchWidth-1,angle-30)
使其调用自己完成递归,注意这里传入的角度是在之前的角度的基础上在增加或者减少30度。
为了使递归停下来我们需要一个停止条件,就是之前一直没有用到的depth参数。我们在每次画下一层之前使其减1表示已经完成了一层树枝的绘制,直至depth减小到0表示绘制完所有的层数。
function Tree(x,y,branchLen,branchWidth,depth,canvas){
this.canvas = canvas || document.getElementById('canvas')
this.ctx = this.canvas.getContext('2d')
this.x = x||0
this.y = y||0
this.branchLen = branchLen||0
this.branchWidth = branchWidth||0
var depth = depth || 5
this.drawRoot(this.x,this.y,this.branchLen,this.branchWidth,depth)
}
Tree.prototype.drawRoot = function(x,y,branchLen,branchWidth,depth){
var toX = x
var toY = y-branchLen
var depth = depth||5
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
depth--
if(depth>0){
this.drawBranch(toX,toY,branchLen,branchWidth-1,30,depth)
this.drawBranch(toX,toY,branchLen,branchWidth-1,-30,depth)
}
}
Tree.prototype.drawBranch = function(x,y,branchLen,branchWidth,angle,depth){
var angle = angle || 0
var radian = (90-angle)*(Math.PI/180)
var toX = x+Math.cos(radian)*branchLen
var toY = y-Math.sin(radian)*branchLen
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
depth--
if(depth>0){
this.drawBranch(toX,toY,branchLen,branchWidth-1,angle+30,depth)
this.drawBranch(toX,toY,branchLen,branchWidth-1,angle-30,depth)
}
}
运行代码:
由于树之间角度过大,而且所有树枝长度都相等,看起来并不像一棵树。所以我们需要在Tree的构造函数中加入几个参数用来调整树的姿态。
function Tree(x,y,branchLen,branchWidth,depth,canvas){
......
this.branchLenFactor = 0.8
this.rootLenFactor = 1.2
this.branchAngle = 20
......
}
branchLenFactor:画每一层树枝的时候乘在branchLen上面,用来控制树枝长度。rootLenFactor:画树根的时候乘在branchLen上面,用来控制树根长度。branchAngle: 用来控制树枝之间的角度
Tree.prototype.drawRoot = function(x,y,branchLen,branchWidth,depth){
var toX = x
var toY = y-branchLen*this.rootLenFactor
var depth = depth||5
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
depth--
if(depth>0){
this.drawBranch(toX,toY,branchLen*this.branchLenFactor,branchWidth-1,this.branchAngle,depth)
this.drawBranch(toX,toY,branchLen*this.branchLenFactor,branchWidth-1,-this.branchAngle,depth)
}
}
Tree.prototype.drawBranch = function(x,y,branchLen,branchWidth,angle,depth){
var angle = angle || 0
var radian = (90-angle)*(Math.PI/180)
var toX = x+Math.cos(radian)*branchLen
var toY = y-Math.sin(radian)*branchLen
this.ctx.save()
this.ctx.strokeStyle="rgba(37, 141, 194, 0.93)"
this.ctx.beginPath()
this.ctx.lineCap = "butt"
this.ctx.lineJoin="round"
this.ctx.lineWidth = branchWidth
this.ctx.moveTo(x,y)
this.ctx.lineTo(toX,toY)
this.ctx.closePath()
this.ctx.stroke()
this.ctx.restore()
depth--
if(depth>0){
this.drawBranch(toX,toY,branchLen*this.branchLenFactor,branchWidth-1,angle+this.branchAngle,depth)
this.drawBranch(toX,toY,branchLen*this.branchLenFactor,branchWidth-1,angle-this.branchAngle,depth)
}
}
运行代码:(查看效果)
据我了解是这样HTLM应该是超文本标记语言不是协议,HTTP那东西才是协议,js和javascript实际上是一样的,javascript简称叫 js , JScript 是 微软出的 跟javascript差不多,现在都叫JS了,HTML/CSS/javascript W3C都有制作规范与标准。dom树是属于javascript的一部分,javascript大体包含3个西:ECMAScript语法(在此基础上建立的javascript语法,还如FLASH的ActionScript也基于ECMAScript扩展的),DOM对象(文档),BOM对象(浏览器).
而DOM对象的出现是为了方便javascript *** 作HTML标签而已.BOM对象也就是 *** 作浏览器了.标准的HTML文档实质上就是一个是就一个树形结构的XML文档
DOM全名document object model 文档对象模型,也就是基于HTML文档出来的,所以 *** 作DOM实际上就是在 *** 作HTML文档对象(每一个标签).
CSS只能修饰HTML的样式而已,而javascript能把HTML与CSS相结合,也就是以前所说的DHTML(动态HTML)。
http只是传输协议,没他的话网站都打不开了。
html这套标签机制也是又W3C规范的,每个浏览器对CSS,HTML,JS的解析都存在问题,所以当时就出现了W3C这个民间的组织,重点是民间组织,O(∩_∩)O哈哈~,组成就是为了说服各大浏览器厂商统一解析CSS,HTML,JS,目前各大浏览器厂商对标准支持的越来越好了(IE最差,可能有钱有势吧,老弄些IE特有属性与方法,而不采用DOM核心方法),但是还是存在差异,希望开发者再也不用为浏览器兼容考虑那一天的到来。
看到你的补充,我很无语,你最开始问的问题,连基本概念性的东西都搞不明白,还说书上一大堆,你真的搞明白了没?CSS *** 作DOM都跑出来了,够囧,基本几个东西的关系你都搞不清楚,你还问机制怎么实现的,你干脆去问浏览器怎么做的吧,说话有点冲动了,别介意,也不晓得你详细看了我上面的问答没,老兄一步一个脚印吧,你还没到那境界!
我的百度空间有篇文章,是写生成树菜单的。http://hi.baidu.com/2hill/blog/item/f22f4ed827b8e23733fa1cca.html
代码我都已经写好了,你复制就行了,当然,也有一些解释,应该可以看懂的。
我是用Js读取XML实现的
到我的空间逛逛吧,有更多收获,http://www.yuefan.net
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)