Make Things Move -- Javascript html5版(二)实现最基本的Sprite类和stage管理对象

  现在这几篇写的都是比较基础的东西,有过相应开发经验的朋友可直接忽略啦。

  计算机模拟的动画都是由很多静态的一连串影像(sprite)在一定帧率(fps)内逐帧播放出来的。

  对于js来说,我们可以用提供的定时器(setInterval或setTimeout,两者其实都可以),制造一个逐帧播放的舞台现场(stage),每一帧清空画布后再把所有的精灵全画上去,下一帧再清再画,只要改变精灵的相应属性就能产生动画的效果。所以我们对于精灵(Sprite)类来说,要提供一个画(draw)的方法,一个进入每一帧时(onenter)要执行的方法;对于舞台(stage)对象,就提供往舞台添加和删除精灵(addChild/removeChild)的方法,定时器的相关操作方法(start,stop,step)。再来看,其实Sprite也是可以添加精灵达到一样的层次关系的,所以也依然可以提供addChild,removeChild的方法,所以我们可以把Sprite做为基类,stage对象继承了Sprite的方法后可根据需要重写和添加自己的方法

  Sprite类

  根据实际需要实现了其他一些方法。在js/lib/目录下新建一个Sprite.js文件(不了解文件目录结构请查看上一篇介绍文章,如果是读者自己规划的文件结构那还是按自己的方法来放吧):

  

this.Sprite = __class__({

    x: 0,         // 精灵的中心点在stage上的x坐标
    y: 0,         // 精灵的中心点在stage上的y坐标
    width: 0,     // 宽
    height: 0,    // 高
alpha: 1, // 透明度 0 ~ 1 scaleX:
1, // X轴上的缩放比 scaleY: 1, // Y轴上的缩放比 rotation: 0, // 旋转角度 visible: true, // 可见否 DEG_TO_RAD: Math.PI / 180, // 一角度多少弧度,常量 __init__: function () { this._object = [] // 存放精灵 },
// 把精灵画到画布前的修正,如把x,y修正为sprite的中心点坐标,画的时候不会以左上角为锚点 __beforedraw__:
function (ctx) { ctx.translate(-this.width/2, -this.height/2) if (this.scaleX !== 1 || this.scaleY !== 1) ctx.scale(this.scaleX, this.scaleY) if (this.rotation % 360 !== 0) ctx.rotate(this.DEG_TO_RAD * this.rotation) if (this.alpha !== 1) ctx.globalAlpha = this.alpha this.draw(ctx) // 真正调用画的方法 this._drawall(ctx) // 把所有的子精灵都 画出来 }, draw: function (ctx) { // 现阶段版本要自己实现画的方法 // implement your own draw method }, _drawall: function (ctx) { if (this.size() > 0) for (var i = 0, obj; obj = this._object[i]; i++) { this.onenter() if (obj.visible) { // 精灵可见才画,不可见它与它的子精灵都不会画到stage上 ctx.save() obj.__beforedraw__(ctx) ctx.restore() } } },
// 进入每帧时调用,如果有做逐帧动画的需求可以实现(就是重写)该方法 onenter:
function () { // implement your own onenter method }, addChild: function (obj) { return this.addChildAt(this.size(), obj) }, removeChild: function (obj) { this.removeChildAt(this.indexOf(obj)) return this },
// 获取指定索引的子精灵 child:
function (index) { return this._object[index] },
// 添加精灵到指定位置 addChildAt:
function (index, obj) { if (this._object.indexOf(obj) === -1) this._object.splice(index, 0, obj) return this },
// 删除指定位置的精灵,如果存在 removeChildAt:
function (index) { if (this.child(index) !== void 0) this._object.splice(index, 1) return this },
// 获取指定精灵的索引 indexOf:
function (obj) { return this._object.indexOf(obj) },
// 获取包含的精灵个数 size:
function () { return this._object.length },
// 移除所有精灵 clear:
function () { this._object.length = 0 return this } })

 

  stage

  舞台管理对象,之所以不用写成一个类是基本不会反复生成多个舞台实例,一个界面一个canvas就够了。上面也提到stage具有的很多方法Sprite类都有,所以我们可以继承Sprite类,又因为只用做成一个对象,所以直接实例化Sprite类就OK了,然后在这个实例中添加或重写方法:

  js/lib/stage.js

this.stage = new Sprite()

// 拷贝方法到stage对象中 __copy__({
// 初始化stage,传入一个canvas dom对象 init: function (canvas) { this.canvas = canvas this.context = canvas.getContext('2d') this.width = canvas.width this.height = canvas.height return this }, // 运行,参数fps为帧率,默认每秒24帧 run: function (fps) { fps = Number(fps) || 24 this._interval = Math.round(1000 / fps) this._starttimer() this.running = true return this }, // 停止运行 stop: function () { this.running = false this._stoptimer() return this }, running: false, __timer: null, _starttimer: function () { if (!this.__timer) this.__timer = setInterval(function (obj) { obj._drawall() }, this._interval, this) }, _stoptimer: function () { if (this.__timer) { clearInterval(this.__timer) this.__timer = null } }, // override _drawall: function () { if (this.size()) { // 清空画布 this.context.clearRect(0, 0, this.width, this.height) Sprite.prototype._drawall.call(this, this.context) } } }, stage)

 

  这两个东西都实现了,现在尝试来使用它们。

     其实还有一个大前提,就是你已经基本了解 html5 canvas的API,如果这个条件不满足可以先去补习一下下,很简单的几个东西。

  感慨web前端这块好象没有特别智能如 android eclipse和ios xcode这样的ide,对文件的引用什么的都不是很友好,或者是我寡闻了,除了静态语言其他时间全是vi的可悲啊……先来调整一下之前定义的东西,在js/lib/sys.js里再加一个工具吧,路径添加和获取的对象,稍稍方法我们用来加载js文件:

this.__path__ = function () {
    var arr = []
    return {
  
        //  添加路径
        add: function () {
            for (var i = 0, str; str = arguments[i]; ++i)
                arr.push(str)
        },

        // 获取所有路径,供__load__使用
        list: function () {
            return arr.slice()
        }
    }
}()

 

      修改js/etc/mtm_module.js文件吧,不用再为每个目录定义一个数组了

__path__.add(
    'js/mtm/Test.js',
    'js/mtm/OtherTest.js'
)

   个性一个js/bin/main.js里开头调用__load__方法的参数为:

// __path__.list() 可获取所添加的要加载的路径
__load__(__path__.list(), function () {})

 

  因为现在在Lib下加了两个文件 (Sprite.js,stage.js),以后要是再加其他类文件 的话都要手动去写标签太麻烦(个人感觉),在js/etc/目录下新建一个lib_module.js文件

__path__.add(
    'js/lib/Sprite.js',
    'js/lib/stage.js'
)

    痛苦的是还要在Index.html里用标签引用lib_module.js ……

 

  好了先来初始化stage,在main.js中加载完文件的回调函数里写:

__load__(__path__.list(), function () {

    var cvs = document.createElement('CANVAS'),
        doc = document.documentElement
    cvs.width = doc.clientWidth
    cvs.height = doc.clientHeight
    document.body.appendChild(cvs)

    stage.init(cvs).run() // 初始化并运行

})

 

  我们在js/mtm/Ball.js里写一个例子,Ball类:

var Ball = __class__(Sprite, {

    __init__: function (color, radius) {
        Sprite.call(this)
        this.color = color || 'red'
        this.radius = radius || 20
    },

    draw: function (ctx) {
        ctx.fillStyle = this.color
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true)
        ctx.fill()
    }

})

使用这个Ball类只要生成实例,并添加到stage就OK了,在js/bin/main.js的 初始化stage完后试试吧.

var ball = new Ball()

// 设置ball的位置在stage中央
ball.x = stage.width / 2
ball.y = stage.height / 2

stage.addChild(ball)

用浏览器打开Index.html页面就看到一个红色圆球在浏览器中心点上了,如果一路进来都没写错什么东西的话。

  工具都基本准备完了,下去就开始一个个实现精彩的例子(有些例子没必要就不实现了)。

posted @ 2013-09-08 09:21  __fon123__  阅读(3794)  评论(0编辑  收藏  举报