2007年6月28日

是男人就下100层 silverlight game man 100

the silverlight game "man 100"

我开发了一个silverlight游戏,就是曾经经典的“是男人就下一百层”。现在googlepages可以访问了。大家可以从这个地址访问。http://swanky.wu.googlepages.com/nebula.silverlight.game.man100.html

还有上次的silverlight player 在这个地址http://swanky.wu.googlepages.com/nebula.silverlight.media.player.html

2007年6月18日

关于 silverlight 中媒体下载进度和鼠标事件问题

The ploblems of media download progress and Mouse events in Microsoft Silverlight

在之前开发nebula silverlight media player 遇到了一些比较奇怪的问题,在此标记一下。

媒体下载进度问题。

像我所知道的那样,开发一个播放器,一般来说需要一个timer,在Silverlight中你可以像Joe描述的那样用Storyboard这控件来代替,用法就像Javascript中window.setTimeout 方法一样,一般来说用timer和流媒体下载部分及总大小可以安全地有效地控制Buffer和Play进度问题。但是在Silverlight这些工作已经被“内置”了,使用MediaElement有几个个事件和属性(BufferingProgressChanged, DownloadProgressChanged,BufferingProgress, BufferingTime, DownloadProgress),用事件驱动的方式就能进行控制下载进度的问题了。

但是实际情况不是想像中那样顺利,Buffer似乎在未下载完的和播发的交叠点就进行了,DownloadProgressChanged 事件也不像msdn描述的那样当 DownloadProgress 变化时就触发。

我的暂且方法是像在flash中那样用我们使用前面描述的“timer”来监控DownloadProgress,因为DownloadProgress对下载进度的描述还是准确的。

鼠标事件问题。

msdn对CaptureMouse的条件进行如下描述

  • The mouse is over the Silverlight control client area.
  • No other Silverlight object has captured the mouse.
  • No other non-Silverlight object has captured the mouse.
  • The left mouse button must be in a depressed state.

哦,是的,只有当鼠标在Silverlight之上才有意义。当我们进行一个DragDrop时,当鼠标离开Silverlight控件时,我们就会丢失这个事件。我不得不说这没有Flash处理的好,在Flash中你只需要对stage对象增加鼠标事件句柄就可以对Html的鼠标事件也进行捕捉。

我们需要这样的功能,因为无论Silverlight还是Flash都是在浏览器中,大多不是全屏的。对于这个问题,我还没有太好的解决方法,一旦发现会马上放出。

最后由于Silverlight还是处于Beta的阶段,也许以后会有变动。

2007年6月15日

JScript的内存泄漏之后续篇

JScript Memory Leaks in Microsoft IE solution 2

正像我上篇文章翻译的一样,Microsoft Internet Explorer 在DOM对象增加事件句柄后会带来内存泄漏的问题。上篇文章也给出了解决方案,但是如果在开发者未知该何时调用这个净化程序时,一般开发者会加入类似的代码:

if ( Nebula.isIE ) {
  window.attachEvent("onunload", function () {
    Nebula.clearEvent ( document.body );
  });
}

这里的Nebula.clearEvent就是上述的净化程序,它在你离开页面或刷新页面时触发,这很重要。比如你正在浏览Gmail,但不知道什么原因导致你必须刷新gmail,这时如果不调用净化程序内存仍然是得不到释放。

是的,你也许也发现了我们传进去的是document.body,这意味着要对整个html文档进行递归地循环地“净化”,效率是很低的。于是我改进了这个方法。

/**
 * The basic global namespace
 * @constructor
 */
window.Nebula = {
  /**
   * get the Nebula lib 's version
   * @type {string}
   */
  version: "1.1.0.0",
  
  /**
  * Boolean flag that navigator whether it is MCST IE
  * @type boolean
  */
  isIE: !!window.ActiveXObject,
  
  /**
  * Boolean flag that navigator whether it is Apple Safari
  * @type boolean
  */
  isSafari: navigator.userAgent.toLowerCase().indexOf("safari") > -1 ,
  
  /**
  * Boolean flag that navigator whether it is Opera
  * @type boolean
  */
  isOpera: navigator.userAgent.toLowerCase().indexOf("opera") > -1 ,
  
  /**
   * event names
   */
  _eventList: ["onbeforecut", "onblur", "onclick", "oncontextmenu", "oncut", 
     "ondblclick", "ondeactivate", "ondragenter", "ondragleave", "ondragover",
     "ondrop",  "onfocus", "onfocusin", "onfocusout", "onhelp", "onkeydown", 
     "onkeypress", "onkeyup", "onmousedown", "onmouseenter", "onmouseleave", 
     "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel",
     "onmove", "onmoveend", "onmovestart", "onpaste", "onreadystatechange",
     "onresize", "onresizeend", "onresizestart", "onselectstart"
   ],
 
  
  /**
   * clear html element 's event handle, fix ie MEMORY LEAKS bug
   * @param d , the element of cleared
   */
  clearEvent: function (d) {
    var a, i, l, n;
    
    if (!d || !this.isIE) {
      return;
    }
    
    a = this._eventList;
    
    try{
      if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
          n = a[i];
          if (typeof d[n] === "function") {
            d[n] = null;
          }
        }
      }
      a = d.childNodes;
      if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
          this.clearEvent(d.childNodes[i]);
        }
      }
    }catch(ex){
      //alert(ex.message);
    }
  }
 
};



if ( Nebula.isIE ) {
  window.attachEvent("onunload", function () {
    Nebula.clearEvent ( document.body );
  });
}

我把DOM元素可能用到event列出来,然后去再去循环这些event,而不是DOM的所有属性。这样好处将会提高效率,减少数万次计算。

JScript的内存泄漏

JScript Memory Leaks in Microsoft IE

当一个系统没有妥善地管理它的内存分配时,就被称为内存泄漏,内存泄漏是一个bug,这问题会导致性能下降和运行错误。

微软的Internet Explorer有许多的泄漏问题,最严重莫过于与JScript的交互作用了。当一个Dom对象包含一个对JavaScript对象的引用时(比如一个事件句柄函数event handling function), 并且那个JavaScript对象包含了对那个Dom对象的引用,那么一个环状结构形成了。这本质并不是一个问题。在这时如果没有其他的引用DOM对象和事件句柄时,那么垃圾回收器(一个自动的内存资源管理器)将收回他们的内存,允许它们的空间被再次的分配。垃圾回收可以理解那个环状结构,并不会对它们感到困惑。不幸地是,IE的DOM结构不受JScript管理,它有它自己的内存管理器,它的内存管理器不理解那个环状结构,这导致它变的很困惑,这导致了当环状结构发生的时候,内存回收不再出现。内存不再被回收被称作泄漏,随着时间过去,这会导致内存最终穷尽。内存空间已被用满,导致浏览器死掉。

我们可以对上述进行论证。在第一个程序里,队列测试1,我们将增加10000个DOM对象(span对象),与此同时,删除最近的10个对象。当你运行它,你会发现在windows任务管理器的性能页观察PF(页面文件)使用情况为平稳的正常。PF的改变使用率可以指示内存分配效率.

接下来,运行第二个程序,队列测试2,它的结果不再与测试1相同了,唯一的变动是它对每个对象增加了一个click handler,在Mozilla和Opera中,PF使用保持平稳,但在IE中我们看到PF稳步地增加,内存泄漏导致了每秒数兆字节的增加。通常这个泄漏是不值得注意的。但是Ajax技术变得流行,单页停留时间变的很长, 页面更多的变化,导致失败变得普遍。

IE不能做好它的工作去回收环状结构,这任务落到了我们的身上,如果我们明确地打碎环状机构,那么IE就能回收内存了。根据微软所说,闭包(closures )会引起内错泄漏,这当然是严重错误的,但是这导致微软给了程序员们一个不好的建议去应对微软的bug。这关闭了从DOM方很容易解决问题的那一面,而交给了事实上不能解决问题的JScript方面。

当我们不再使用一个对象时,我们必须清除它所有的event handlers来破坏循环引用,所有我们必须要做的就是将每个event handler属性设置为null值,这可以明确的进行,又或者我们可以写一个通用的净化函数。

净化程序将一个引用DOM对象作为参数,它循环元素所有属性,如果发现任何函数,就将其设为null。这将破坏循环引用,允许内存再生。这也将元素的派生元素,这将很好清除循环引用。净化函数对Mozilla和Opera是无害的,它应用于IE。净化程序应该在移除任何元素时被调用,在removeChild方法,和设置innerHTML属性的之前使用。

function purge(d) {
    var a = d.attributes, i, l, n;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            n = a[i].name;
            if (typeof d[n] === 'function') {
                d[n] = null;
            }
        }
    }
    a = d.childNodes;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            purge(d.childNodes[i]);
        }
    }
}

最后我们运行第三个程序,队列测试3,净化程序在删除DOM元素之前被调用。

特别声明,文章翻译于JScript Memory Leaks
版权由原作者所有。

2007年6月14日

nebula silverlight media player 测试发布了

nebula silverlight media player beta is published

我用Microsoft Silverlight创建了一个wmv媒体播放器。它基于Silverlight Application (.Net)创建,使用的c#语言。

一开始我本打算用Javascript来创建,因为那样更容易把 nebula flash FLVPlayer代码“迁移”过来,但是后来我发现可能是Silverlight在处于beta的状态,有许多的问题难以解决。比如说在使用一些事件的时候莫名其妙地报错。

它和nebula flash FLVPlayer具有完全相同的功能:

  • 对于已经buffer的部分快进快退
  • 逐帧播放
  • 全屏
  • 音量控制
  • 保持画面比例/拉伸切换

不过可惜的是我不能把它放到互联网上,因为google pages被封了。不过我准备了一些截图,可以欣赏一下。

随后我将把一些碰到的问题和难点写出来,主要包括关于BufferingProgressChanged的使用,动态使用DoubleAnimation等对象动画,以及与flash开发的不同及比较等,敬请期待!

点击图片可查看大图

Update 07.09.2007: google pages 可以访问了 可以通过这个地址访问 http://swanky.wu.googlepages.com/nebula.silverlight.media.player.html

2007年6月13日

在基于c#的Silverlight中设置Canvas.*属性

set elements's Canvas.Left (Canvas.*) atttribute in Silverlight base on c#

当需要在代码控制某个控件的位置的时候,在JavaScript

中使用
object["Canvas.Left"] = value;
而在c#中需要使用
DependencyObject.SetValue(DependencyProperty property, T obj);

//my code 
myControl.SetValue(Canvas.LeftProperty, myControl.Width/2);

这有使用的不同需要注意。

2007年6月4日

使用了ExternalInterface的swf在ie中如果id中有点(.)将会报错。

swf using ExternalInterface in IE, if there is dot(.) in it's id, IE will be fired error.

这几天研究flash flv player,发现了似乎bug的东西,前几天或者说是更早的时候我发现当swf使用了ExternalInterface接口和javascript通信的时候,在ie中如果id中有点(.)将会报错,ie则会报错,通过vs2005跟踪到以下代码:

__flash__removeCallback(nebula.player.FLVPlayer, "_stop");

这是其一。 在调用通信接口时会看到:

function __flash__arrayToXML(obj) {
 var s = "<array>";
 for (var i=0; i<obj.length; i++) {
  s += "<property id=\"" + i + "\">" + __flash__toXML(obj[i]) + "</property>";
 }
 return s+"</array>";
}
function __flash__argumentsToXML(obj,index) {
 var s = "<arguments>";
 for (var i=index; i<obj.length; i++) {
  s += __flash__toXML(obj[i]);
 }
 return s+"</arguments>";
}
function __flash__objectToXML(obj) {
 var s = "<object>";
 for (var prop in obj) {
  s += "<property id=\"" + prop + "\">" + __flash__toXML(obj[prop]) + "</property>";
 }
 return s+"</object>";
}
function __flash__escapeXML(s) {
 return s.replace(/&/g, "&amp;").replace(/"&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
}
function __flash__toXML(value) {
   var type = typeof(value);
 if (type == "string") {
  return "<string>" + __flash__escapeXML(value) + "</string>";
 } else if (type == "undefined") {
        return "<undefined/>";
 } else if (type == "number") {
        return "<number>" + value + "</number>";
 } else if (value == null) {
        return "<null/>";
 } else if (type == "boolean") {
        return value ? "<true/>" : "<false/>";
 } else if (value instanceof Date) {
        return "<date>" + value.getTime() + "</date>";
   } else if (value instanceof Array) {
       return __flash__arrayToXML(value);
   } else if (type == "object") {
       return __flash__objectToXML(value);
   } else {
     return "<null/>"; //???
 }
}
function __flash__addCallback(instance, name) {
  instance[name] = function () { 
    return eval(instance.CallFunction("<invoke name=\""+name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>"));
  }
}
function __flash__removeCallback(instance, name) {
  instance[name] = null;
}

这是其二,一般是在__flash__addCallback这个方法里报错, 一般是说instance没有定义。我的浏览器是IE6.0,flash player的版本是9,0,45,0,这个问题只有在IE中存在。

我们知道在IE里我们可以在JavaScript直接使用一个HtmlElement的Id来引用它而不用事先声明它或使用getElementById获取它,我想问题就存在这里了,当Id中存在点"."的时候,如果仍然这样引用显然会报错,比如在这个例子中会说"nebula未定义",从Adobe的代码似乎这样写的了。显然是无法将dom的带“点”的id直接声明成变量。

依我看,这应该是一个地地道道的bug。

我不知道如何submit或confirm Adobe的bug,有谁知道,不妨指点一下。

2007年6月2日

googlepages被封了吗?

大约昨天下午的时候blogger访问困难,不过稍后的时候变“正常”了,在同时googlepages却无法访问了,我以为是网络的问题。当我今天回家的时候,swanky.wu.googlepages.com仍然无法访问。做了一查询显示

您查询的IP:219.158.3.30

  • 本站主数据:中国 网通骨干网
  • 查询结果2:中国 网通骨干网
  • 查询结果3:北京市 网通

确切无疑是被做掉了,我真不知道该憎恨谁?我也不知道该把东西放到哪了?

2007年6月1日

nebula flash FLVPlayer 0.1.05.30测试发布了

这个版本经过测试解决了大多问题,大小10,479 字节。使用“_open”方法可以打开flv地址。具有以下特性:

  • 对于已经buffer的部分快进快退
  • 对于逐帧的flv文件可以按帧播放(在转flv时使用了key frame placement:custom, key frame interval 1 frame)
  • 全屏
  • 音量控制
  • 保持画面比例/拉伸切换

你可以访问这个页面来参考它是如何工作的:http://swanky.wu.googlepages.com/nebula.player.FLVPlayer.html

你可以通过http://swanky.wu.googlepages.com/nebula.player.FLVPlayer.0.1.05.30.swf这个地址来下载它。

大多的属性或方法没有通过ExternalInterface共享出来,对于这个0.1版本可能是越简单越好。