HTML5 一些莫名其妙的体会

写HTML5 已经断断续续的有1个月了。
看了两本书:《The.Essential.Guide.to.HTML5》 和 《Foundation HTML5 ANIMATION WITH JAVASCRIPT》 。 《Foundation HTML5 WITH ANIMATION JAVASCRIPT》 这本书我都是跳着看的,原因很简单。。。尼玛这完全就是FLASH的《MAKING THINGS MOVE》的翻版,嘛~从作者的论述到素材完全照搬嘛,就是不择不扣的JS实现版。
学HTML5的初衷是,当时炒得沸沸扬扬的HTML5要取代FLASH影响到了我。我也不想迷迷糊糊的跟着媒体的声音走,我想做出自己的判断,自己做出选择。那么唯一方法就是去了解HTML5.

HTML5在基本绘图方面:
HTML5用到的标签是 canvas 其本质就是 AS的 Bitmap. 而 canvas.getContext(‘2d’) 就好比是 Bitmap.BitmapData。 两者可以说几乎完全相同,使用差不多的API如 drawRect,drawArc,drawCircle,LineTo,MoveTo 进行图形线条的绘制。同时两者同样的 canvas 和 Bitmap 支持鼠标事件, Context(‘2d’) 和 bitmapdata 不支持 鼠标事件。 那么解决方案呢?这就和我之前写的一篇文章不谋而合。《某传说中的网络游戏引擎显示模块0.2BETA》 申明一个矩形来存储位图的长宽,坐标,去投影虚拟的图像。根据鼠标的坐标去比对这个矩形来判断是否点击在图形上。

我之前以为HTML5会做的更好,待我查阅了很多资料后,失落的发现,解决方案都和我2月前的在FLASH的检测方案相同。。。

HTML5显示层级问题:
HTML5并没有FLASH类似的stage,因此其没有显示列表。 那么有这么一种情况:存在两个按钮,按钮A在按钮B之上(先绘制按钮B,再绘制按钮A), 此时点击按钮A,那么根据矩形区域判断点击方法,程序判断 按钮A和按钮B 同时被点击了。 在FLASH中不存在该问题,因为按钮A会把按钮B的 鼠标事件给遮挡住,无法冒泡。 解决方法是:创建一个数组(模拟显示列表),按照想要的显示顺序,把对象按顺序丢入数组, 当鼠标点击后,判断点击到了哪些对象。当检测到按钮A和按钮B都被点击到的情况下,判断按钮A和按钮B在数组中的索引值,取索引值低的对象做出响应。 这无形中增加了程序逻辑的复杂度。

HTML5 2D帧渲染方面:
HTML5中要渲染新的一帧,比如是一个人物在背景图片上移动的效果,由于人物是移动的,不好把握canvas清空矩形的位置,所以一般的使用方式为:context.clearRect(0,0,canvas.width,canvas.height) 全屏清空。那么全屏清空了,不会移动的背景图片也需要重新绘制,同时还要保证比人物先渲染。其实这也是显示层级问题,canvas不支持单层单清除。

HTML5毕竟刚出来,还有很多不完善 如:声音同步(需要第三方类库),在线流媒体,长连接通信(需要第三方插件),代码资源素材暴露 and so on…
但是至少在纯简单动画方面是可以取代的FLASH的,毕竟canvas 2d绘图是单张刷新的,基本算是效率很高的方式了。
而页游方面,近几年都不可能取代FLASH的.而未来几年的情况是,页面上的广告会慢慢的替换成HTML5,会出现少部分HTML5小游戏(稀少,开发周期过长,开发效率低下)。网页游戏仍然是FLASH的地盘,并且网页3D游戏会慢慢开始流行.
嘛~年后DreamFairy就要回公司了,貌似要开发一款MMORPG(大型在线角色扮演)的新游戏的样子,啊啊,估计又要好忙好忙了。

放上一个HTML5 demo. 包含 层级刷新,预载,简单缓动
[仅仅移动你的鼠标就好]

主文件
test.htm
[cc lang=”javascript” nowrap=”false”]




easing


🙁 抱歉~
您的浏览器貌似不支持HTML5的标签”canvas”的说,试试更换成Chrome,FireFox,IE9…









[/cc]

由于只要三张图片,加载的略快,所以加载完毕后,仍然把加载信息留在canvas上,没有清除。

用到的文件
ball.js
[cc lang="javascript" nowrap="false"]
function Ball(radius,color){
if(radius === undefined){radius=40;}
if(color === undefined) {color="#ff0000";}
this.x=0;
this.y=0;
this.radius=radius;
this.vx=0;
this.vy=0;
this.rotation=0;
this.scaleX=1;
this.scaleY=1;
this.color=utils.parseColor(color);
this.lineWidth=1;
}

Ball.prototype.draw=function(context){
context.save();
context.translate(this.x,this.y);
context.rotate(this.rotation);
context.scale(this.scaleX,this.scaleY);
context.lineWidth=this.lineWidth;
context.fillStyle=this.color;
context.beginPath();
context.arc(0,0,this.radius,0,(Math.PI*2),true);
context.closePath();
context.fill();
if(this.lineWidth>0){
context.stroke();
}
context.restore();
};

Ball.prototype.getBounds=function(){
return{
x:this.x-this.radius,
y:this.y-this.radius,
width:this.radius*2,
height:this.radius*2
};
};
[/cc]

预载库
PxLoader.js
[cc lang="javascript" nowrap="false"]
/**
* PixelLab Resource Loader
* Loads resources while providing progress updates.
*/
function PxLoader(settings) {

// merge settings with defaults
settings = settings || {};

// how frequently we poll resources for progress
if (settings.statusInterval == null) {
settings.statusInterval = 5000; // every 5 seconds by default
}

// delay before logging since last progress change
if (settings.loggingDelay == null) {
settings.loggingDelay = 20 * 1000; // log stragglers after 20 secs
}

// stop waiting if no progress has been made in the moving time window
if (settings.noProgressTimeout == null) {
settings.noProgressTimeout = Infinity; // do not stop waiting by default
}

var entries = [], // holds resources to be loaded with their status
progressListeners = [],
timeStarted,
progressChanged = +new Date;

/**
* The status of a resource
* @enum {number}
*/
var ResourceState = {
QUEUED: 0,
WAITING: 1,
LOADED: 2,
ERROR: 3,
TIMEOUT: 4
};

// places non-array values into an array.
var ensureArray = function(val) {
if (val == null) {
return [];
}

if (Array.isArray(val)) {
return val;
}

return [ val ];
};

// add an entry to the list of resources to be loaded
this.add = function(resource) {

// ensure tags are in an array
resource.tags = ensureArray(resource.tags);

// ensure priority is set
if (resource.priority == null) {
resource.priority = Infinity;
}

entries.push({
resource: resource,
state: ResourceState.QUEUED
});
};

this.addProgressListener = function(callback, tags) {
progressListeners.push({
callback: callback,
tags: ensureArray(tags)
});
};

this.addCompletionListener = function(callback, tags) {
progressListeners.push({
tags: ensureArray(tags),
callback: function(e) {
if (e.completedCount === e.totalCount) {
callback();
}
}
});
};

// creates a comparison function for resources
var getResourceSort = function(orderedTags) {

// helper to get the top tag's order for a resource
orderedTags = ensureArray(orderedTags);
var getTagOrder = function(entry) {
var resource = entry.resource,
bestIndex = Infinity;
for (var i = 0, len = resource.tags.length; i < len; i++) { var index = orderedTags.indexOf(resource.tags[i]); if (index >= 0 && index < bestIndex) { bestIndex = index; } } return bestIndex; }; return function(a, b) { // check tag order first var aOrder = getTagOrder(a), bOrder = getTagOrder(b); if (aOrder < bOrder) return -1; if (aOrder > bOrder) return 1;

// now check priority
if (a.priority < b.priority) return -1; if (a.priority > b.priority) return 1;
}
};

this.start = function(orderedTags) {
timeStarted = +new Date;

// first order the resources
var compareResources = getResourceSort(orderedTags);
entries.sort(compareResources);

// trigger requests for each resource
for (var i = 0, len = entries.length; i < len; i++) { var entry = entries[i]; entry.status = ResourceState.WAITING; entry.resource.start(this); } // do an initial status check soon since items may be loaded from the cache setTimeout(statusCheck, 100); }; var statusCheck = function() { var checkAgain = false, noProgressTime = (+new Date) - progressChanged, timedOut = (noProgressTime >= settings.noProgressTimeout),
shouldLog = (noProgressTime >= settings.loggingDelay);

for (var i = 0, len = entries.length; i < len; i++) { var entry = entries[i]; if (entry.status !== ResourceState.WAITING) { continue; } // see if the resource has loaded entry.resource.checkStatus(); // if still waiting, mark as timed out or make sure we check again if (entry.status === ResourceState.WAITING) { if (timedOut) { entry.resource.onTimeout(); } else { checkAgain = true; } } } // log any resources that are still pending if (shouldLog && checkAgain) { log(); } if (checkAgain) { setTimeout(statusCheck, settings.statusInterval); } }; this.isBusy = function() { for (var i = 0, len = entries.length; i < len; i++) { if (entries[i].status === ResourceState.QUEUED || entries[i].status === ResourceState.WAITING) { return true; } } return false; }; // helper which returns true if two arrays share at least one item var arraysIntersect = function(a, b) { for (var i = 0, len = a.length; i < len; i++) { if (b.indexOf(a[i]) >= 0) {
return true;
}
}
return false;
};

var onProgress = function(resource, statusType) {
// find the entry for the resource
var entry = null;
for(var i=0, len = entries.length; i < len; i++) { if (entries[i].resource === resource) { entry = entries[i]; break; } } // we have already updated the status of the resource if (entry == null || entry.status !== ResourceState.WAITING) { return; } entry.status = statusType; progressChanged = +new Date; var numResourceTags = resource.tags.length; // fire callbacks for interested listeners for (var i = 0, numListeners = progressListeners.length; i < numListeners; i++) { var listener = progressListeners[i], shouldCall; if (listener.tags.length === 0) { // no tags specified so always tell the listener shouldCall = true; } else { // listener only wants to hear about certain tags shouldCall = arraysIntersect(resource.tags, listener.tags); } if (shouldCall) { sendProgress(entry, listener); } } }; this.onLoad = function(resource) { onProgress(resource, ResourceState.LOADED); }; this.onError = function(resource) { onProgress(resource, ResourceState.ERROR); }; this.onTimeout = function(resource) { onProgress(resource, ResourceState.TIMEOUT); }; // sends a progress report to a listener var sendProgress = function(updatedEntry, listener) { // find stats for all the resources the caller is interested in var completed = 0, total = 0; for (var i = 0, len = entries.length; i < len; i++) { var entry = entries[i], includeResource; if (listener.tags.length === 0) { // no tags specified so always tell the listener includeResource = true; } else { includeResource = arraysIntersect(entry.resource.tags, listener.tags); } if (includeResource) { total++; if (entry.status === ResourceState.LOADED || entry.status === ResourceState.ERROR || entry.status === ResourceState.TIMEOUT) { completed++; } } } listener.callback({ // info about the resource that changed resource: updatedEntry.resource, // should we expose StatusType instead? loaded: (updatedEntry.status === ResourceState.LOADED), error: (updatedEntry.status === ResourceState.ERROR), timeout: (updatedEntry.status === ResourceState.TIMEOUT), // updated stats for all resources completedCount: completed, totalCount: total }); }; // prints the status of each resource to the console var log = this.log = function(showAll) { if (!window.console) { return; } var elapsedSeconds = Math.round((+new Date - timeStarted) / 1000); window.console.log('PxLoader elapsed: ' + elapsedSeconds + ' sec'); for (var i = 0, len = entries.length; i < len; i++) { var entry = entries[i]; if (!showAll && entry.status !== ResourceState.WAITING) { continue; } var message = 'PxLoader: #' + i + ' ' + entry.resource.getName(); switch(entry.status) { case ResourceState.QUEUED: message += ' (Not Started)'; break; case ResourceState.WAITING: message += ' (Waiting)'; break; case ResourceState.LOADED: message += ' (Loaded)'; break; case ResourceState.ERROR: message += ' (Error)'; break; case ResourceState.TIMEOUT: message += ' (Timeout)'; break; } if (entry.resource.tags.length > 0) {
message += ' Tags: [' + entry.resource.tags.join(',') + ']';
}

window.console.log(message);
}
};
}

// shims to ensure we have newer Array utility methods

// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) == '[object Array]';
};
}

// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement /*, fromIndex */) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) { if (k in t && t[k] === searchElement) { return k; } } return -1; }; } [/cc] 用到的PxLoader文件预载库 官网:http://thinkpixellab.com/pxloader/

发表评论

电子邮件地址不会被公开。 必填项已用*标注