`

Extjs YUI Charts 实现原理分析

    博客分类:
  • js
阅读更多
首先说一下 YUI Extjs Astra三者的Charts的关系:
Extjs原名YUI-Extjs,最初Extjs是对YUI的封装
Extjs 3.0新增的Charts功能实际上是对YUI Charts的封装
YUI Charts是对Astra Charts的封装
Astra Charts是Astra Flash Components的组件之一
Astra Flash Components是YAHOO提供的flash/flex组件库
Extjs和YUI两者实现CHARTS的示例代码:
//yui LineChart 核心代码
var mychart = new YAHOO.widget.LineChart( "chart", myDataSource,
{
series: seriesDef,
xField: "month",
yAxis: currencyAxis,
dataTipFunction: YAHOO.example.getDataTipText,
//only needed for flash player express install
expressInstall: "assets/expressinstall.swf"
});

//extjs LineChart 核心代码
new Ext.Panel({
        title: 'ExtJS.com Visits Trend',
        renderTo: 'container',
        width:500,
        height:300,
        layout:'fit',
        items: {
            xtype: 'linechart',
            store: store,
            xField: 'name',
            yField: 'visits',
listeners: {
itemclick: function(o){
var rec = store.getAt(o.index);
Ext.example.msg('Item Selected', 'You chose {0}.', rec.get('name'));
}
}
        }
    });

具体实现代码大家请参考官方网站:
http://extjs.com/deploy/dev/examples/chart/charts.html
http://developer.yahoo.com/yui/charts/
由于Extjs的charts功能只是对YUI charts的封装所以我这里只是介绍一下YUI Charts的实现原理。
首先看一下YUI Charts主要类结构

YAHOO.widget.Chart是所有Chart的基类,其中饼图YAHOO.widget.PieChart是直接继承YAHOO.widget.Chart;而其他柱状图、连线图等其他图形是继承 YAHOO.widget.CartesianChart。而YAHOO.widget.Chart的父类YAHOO.widget.FlashAdapter是实现Chart功能最重要的类,就像它的名字显示的含义一样这个类是js和flash交互的适配器。
下面再看一下为了支持YUI实现Charts功能YUI在Astra Charts上进行的封装

右边2个类是Astra Charts原有的类
YUIAdapter和Charts是为了YUI实现Chart相关功能对Astra Charts进行的封装
Sprite 是不需要时间轴的对象的相应基类。
下面看一下YAHOO.widget.LineChart的初始化过程,也就是执行上面第一段代码时的时序图
上图中的XXXChart代表YAHOO.widget.LineChart的一些父类,这里为了画图时简单一点。
new YAHOO.widget.LineChart( …)构造Chart对象,实质上是调用父类的构造函数,最终调用FlashAdapter的构造函数。FlashAdapter的私有方法_embedSWF中创建了一个SWFObject对象
	/**
	 * Embeds the SWF in the page and associates it with this instance.
	 *
	 * @method _embedSWF
	 * @private
	 */
	_embedSWF: function(swfURL, containerID, swfID, version, backgroundColor, expressInstall, wmode)
	{
		//standard SWFObject embed
		var swfObj = new YAHOO.deconcept.SWFObject(swfURL, swfID, "100%", "100%", version, backgroundColor);

		if(expressInstall)
		{
			swfObj.useExpressInstall(expressInstall);
		}

		//make sure we can communicate with ExternalInterface
		swfObj.addParam("allowScriptAccess", "always");
		
		if(wmode)
		{
			swfObj.addParam("wmode", wmode);
		}
		
		//again, a useful ExternalInterface trick
		swfObj.addVariable("allowedDomain", document.location.hostname);

		//tell the SWF which HTML element it is in
		swfObj.addVariable("elementID", swfID);

		// set the name of the function to call when the swf has an event
		swfObj.addVariable("eventHandler", "YAHOO.widget.FlashAdapter.eventHandler");

		var container = YAHOO.util.Dom.get(containerID);
		var result = swfObj.write(container);
		if(result)
		{
			this._swf = YAHOO.util.Dom.get(swfID);
			//if successful, let's add an owner property to the SWF reference
			//this will allow the event handler to communicate with a YAHOO.widget.FlashAdapter
			this._swf.owner = this;
		}
		else
		{
			YAHOO.log("Unable to load SWF " + swfURL);
		}
	},

上面的代码注释大家应该基本可以看明白是怎么回事,我再稍微做一下解释。
SWFObject是一种易用的、符合标准的在网页中嵌入flash对象的方法 ,能够自动检测PC、Mac机器上各种主流浏览器对Flash插件的支持情况。
SWFObject属于另一个开源项目:http://code.google.com/p/swfobject/
swfObj.addParam(“allowScriptAccess”, “always”);//allowscriptaccess=always 允许随时执行脚本操作  使 Flash 应用程序可与其所在的 HTML 页通信
swfObj.addVariable(“allowedDomain”, document.location.hostname);//actionscript与JS的交互不允许跨域
swfObj.addVariable(“eventHandler”, “YAHOO.widget.FlashAdapter.eventHandler”);//指定js提供给actionscript的事件回调函数
swfObj.write(container);//在container这个DOM内插入SWF对象代码
this._swf//就是对SWF对象的引用
this._swf.owner = this;//这里的this代表的就是执行new YAHOO.widget.LineChart( …)后生成的对象

在执行完上面时序图的初始化之后,swf对象就被嵌入了HTML中,后续就要执行FLASH的初始化工作。请看下面的时序图

如上图显示FLASH PLAYER先去调用了Charts的构造函数,但是Charts的构造函数是空的所以实际上最初只是调用了YUIAdapter的构造函数,下面是YUIAdapter的一些核心代码,其他关于charts.swf的代码请看附件charts.rar
        public function YUIAdapter()
        {
            var swfReady:Object;
            if (this.stage)
            {
                this.stage.addEventListener(Event.RESIZE, stageResizeHandler);
                this.stage.scaleMode = StageScaleMode.NO_SCALE;
                this.stage.align = StageAlign.TOP_LEFT;
            }// end if
            try
            {
                ExternalInterface.marshallExceptions = true;
            }// end try
            catch (error:Error)
            {
            }// end catch
            this._errorText = new TextField();
            this._errorText.defaultTextFormat = new TextFormat("_sans", 10, 16711680);
            this._errorText.wordWrap = true;
            this._errorText.autoSize = TextFieldAutoSize.LEFT;
            this._errorText.selectable = false;
            this._errorText.mouseEnabled = false;
            this.addChild(this._errorText);
            this.addEventListener(Event.ADDED, addedHandler);
            if (ExternalInterface.available)
            {
                this.initializeComponent();
                swfReady;
                this.dispatchEventToJavaScript(swfReady);
            }
            else
            {
                throw new IOError("Flash YUIComponent cannot communicate with JavaScript content.");
            }// end else if
            return;
        }// end function

        public function getAltText() : String
        {
            return this._altText;
        }// end function

        protected function refreshComponentSize() : void
        {
            if (this.component)
            {
                var _loc_1:int;
                this.component.y = 0;
                this.component.x = _loc_1;
                this.component.width = this.stage.stageWidth;
                this.component.height = this.stage.stageHeight;
            }// end if
            return;
        }// end function

        protected function showFatalError(param1:Object) : void
        {
            if (!param1)
            {
                param1 = "";
            }// end if
            if (this._errorText)
            {
                this._errorText.appendText(param1.toString());
                this._errorText.scrollV = this._errorText.maxScrollV;
                this._errorText.mouseEnabled = true;
                this._errorText.selectable = true;
            }// end if
            return;
        }// end function

        protected function addedHandler(param1:Event) : void
        {
            this.setChildIndex(this._errorText, this.numChildren--);
            return;
        }// end function

        protected function log(param1:Object, param2:String = null) : void
        {
            if (param1 == null)
            {
                param1 = "";
            }// end if
            this.dispatchEventToJavaScript({type:"log", message:param1.toString(), category:param2});
            return;
        }// end function

        protected function initializeComponent() : void
        {
            this.elementID = this.loaderInfo.parameters.elementID;
            this.javaScriptEventHandler = this.loaderInfo.parameters.eventHandler;
            var allowedDomain:* = this.loaderInfo.parameters.allowedDomain;
            if (allowedDomain)
            {
                Security.allowDomain(allowedDomain);
                this.log("allowing: " + allowedDomain);
            }// end if
            try
            {
                ExternalInterface.addCallback("getAltText", getAltText);
                ExternalInterface.addCallback("setAltText", setAltText);
            }// end try
            catch (error:SecurityError)
            {
            }// end catch
            return;
        }// end function

        protected function dispatchEventToJavaScript(param1:Object) : void
        {
            var event:* = param1;
            try
            {
                if (ExternalInterface.available)
                {
                    ExternalInterface.call(this.javaScriptEventHandler, this.elementID, event);
                }// end if
            }// end try
            catch (error:Error)
            {
                if (error is SecurityError)
                {
                    this.showFatalError("Warning: Cannot establish communication between YUI Charts and JavaScript. YUI Charts must be served from HTTP and cannot be viewed locally with file:/// protocol unless location is trusted by Flash Player.\n\nFor more information see:\nhttp://www.adobe.com/products/flashplayer/articles/localcontent/\n\n");
                }// end if
            }// end catch
            return;
        }// end function

请看一下上面代码的部分解释
ExternalInterface.marshallExceptions = true;//指示外部接口是否应该尝试将 ActionScript 异常传递到当前浏览器并将 JavaScript 异常传递到 Flash Player。
ExternalInterface.available//指示此播放器是否位于提供外部接口的容器中。 
this.javaScriptEventHandler = this.loaderInfo.parameters.eventHandler;//从参数中得到JS提供给Actionscript的事件回调函数
ExternalInterface.addCallback(“getAltText”, getAltText);//将 ActionScript 方法注册为可从容器调用。 
ExternalInterface.call(this.javaScriptEventHandler, this.elementID, event);//调用由 Flash Player 容器公开的函数,不传递参数或传递多个参数。 

YAHOO.widget.FlashAdapter.eventHandler()这个方法类似于静态方法,将请求分发给LineChart的对象去执行_eventHandler方法。后面代码执行顺序请大家按上面的时序图去分析。
下面是Javascript和Actionscript通信的一些基础知识:
<param?name="allowScriptAccess"?value="always"/>
<embed ... allowscriptaccess="always" type="application/x-shockwave-flash"/>
value参数说明:
always 允许随时执行脚本操作。
never 禁止所有脚本执行操作。
samedomain 只有在 Flash 应用程序来自与 HTML 页相同的域时才允许执行脚本操作。
ExternalInterface.marshallExceptions:指示外部接口是否应该尝试将 ActionScript 异常传递到当前浏览器并将 JavaScript 异常传递到 Flash Player。必须显式将此属性设置为 true,以便在 ActionScript 中捕获 JavaScript 异常以及在 JavaScript 中捕获 ActionScript 异常。
ExternalInterface.available:指示此播放器是否位于提供外部接口的容器中。如果外部接口可用,则此属性为 true;否则,为 false。 
ExternalInterface
public static function addCallback(functionName:String, closure:Function):void functionName:String — 容器可用于调用函数的名称。 closure:Function — 要调用的 closure 函数。 将 ActionScript 方法注册为可从容器调用。成功调用 addCallBack() 后,容器中的 JavaScript 或 ActiveX 代码可以调用在 Flash Player 中注册的函数。
public static function call(functionName:String, ... arguments): 调用由 Flash Player 容器公开的函数,不传递参数或传递多个参数。如果该函数不可用,调用将返回 null;否则,它返回由该函数提供的值。不允许在 Opera 或 Netscape 浏览器中使用递归;在这些浏览器上,递归调用将生成 null 响应。(Internet Explorer 和 Firefox 浏览器上支持递归。)
参考文档:
ExternalInterface :http://livedocs.adobe.com/flex/3_cn/langref/flash/external/ExternalInterface.html
SWFObject:http://code.google.com/p/swfobject/
http://farthinker.cn/2007/12/27/swfobject-2_0-doc-translation/
再申明一下小弟对Actionscript编程没有任何经验,上面的分析都是凭我以往的开发经验做的分析,请各位老大拍砖指正我分析的错误。
  • 大小: 24.6 KB
  • 大小: 10.7 KB
  • 大小: 22.3 KB
  • 大小: 4.8 KB
分享到:
评论
3 楼 SeanHe 2009-08-24  
atian25 写道
好帖,yahoo这个东西似乎不开源?
感觉YUI charts的那个doc也言之不详...也没找到更深入的东西

Extjs 3.0新增的Charts功能实际上是对YUI Charts的封装
YUI Charts是对Astra Charts的封装
老大,上面这两句话其实我也是看了你的帖子才知道的。
关于Charts的资料确实很少,我找到唯一的资料就是老大您的帖子。上面的内容我自己研究了好几天才了解来了个大概
2 楼 mengzhe1208 2009-08-24  
ddddd
1 楼 atian25 2009-08-24  
好帖,yahoo这个东西似乎不开源?
感觉YUI charts的那个doc也言之不详...也没找到更深入的东西

相关推荐

    Extjs4Charts图表

    ExtJS 4 是一款强大的JavaScript框架,用于构建富客户端应用程序。...提供的"Extjs4Charts图表"压缩包文件应该包含了预设的图表示例,可以直接运行查看效果,对于初学者来说,这是一个很好的学习资源。

    YUI 2.8.0 charts.swf

    用于替换Extjs生成图表的charts.swf文件。实现图表的另存为图片的功能

    ExtJs charts.swf源码

    本文将深入探讨ExtJs charts.swf源码的核心原理、功能特性以及如何进行修改与优化。 一、核心原理 charts.swf是一个基于Adobe Flash技术的二进制文件,它包含了用于绘制图表的所有图形和动画逻辑。在ExtJs中,...

    深入剖析ExtJS_2.2实现及应用

    这本书旨在帮助开发者超越仅仅依赖官方文档的阶段,深入理解ExtJS的内在工作原理,从而能够更高效、更优化地使用这个强大的JavaScript框架。 ExtJS是一个功能丰富的JavaScript库,用于构建用户界面,特别是企业级的...

    Extjs 轻松实现下拉框联动

    最近小弟做了Extjs实现实现下拉框联动的效果,参考了好久才学会,闲下来发一个简单的例子。。呵呵

    Extjs 下拉菜单实现拼音输入进行检索

    Extjs 下拉菜单实现拼音输入进行检索

    ExtJS Grid功能实现

    ExtJS 表格的功能实现,包括单元格编辑,数据的获取。

    extjs+java实现短信猫发生短信dom

    标题中的“extjs+java实现短信猫发生短信dom”揭示了这个项目是关于使用EXTJS(一个基于JavaScript的用户界面库)和Java技术来构建一个系统,该系统能够通过短信猫(一种硬件设备,用于通过调制解调器发送和接收短信...

    Jquery、YUI、ExtJs 三大javascript框架表单验证带提示功能的demo

    本教程将深入探讨Jquery、YUI和ExtJs这三大JavaScript框架在表单验证和提示功能方面的应用。 **jQuery** jQuery是一款轻量级、高性能的JavaScript库,以其简洁的API和跨浏览器兼容性闻名。在表单验证方面,jQuery...

    ExtjS实现聊天功能

    在这个"ExtJS实现聊天功能"的项目中,我们将探讨如何利用ExtJS框架构建一个类似于QQ的聊天应用程序。 首先,我们要理解聊天功能的核心要素。一个基本的聊天应用通常包含以下部分: 1. **用户界面**:ExtJS提供了...

    ExtJS实现Excel导出

    ### ExtJS实现Excel导出:深入解析与实践 在当今高度数字化的工作环境中,数据的管理和呈现方式至关重要。其中,Excel作为数据处理和分析的重要工具,其导出功能在各种应用场景中显得尤为关键。ExtJS,作为一种强大...

    ExtJs +Echart 实现桑基图绘制

    ExtJs +Echart 实现桑基图绘制,包括前端页面搭建,后端数据赋值,桑基图数据绑定,桑基图Json后台格式定义,Json格式转换到前台接收等,下载后如果右运行不了,请加群,备注ExtJs桑基图代码,如果好用,请给五分...

    ExtJS+2.2实现及应用连载.rar

    本资料“ExtJS+2.2实现及应用连载”旨在深入探讨这一版本的特性和实际应用。 一、ExtJS 2.2的核心特性 1. 组件化开发:ExtJS 2.2提供了大量预定义的UI组件,如表格、表单、树形视图、菜单、工具栏等,开发者可以像...

    extjs与swfupload实现java文件批量上传 s2sh

    `ExtJS`是一个流行的JavaScript库,用于构建富客户端界面,而`SWFUpload`则是一个经典的Flash插件,用于实现文件上传,尤其适用于批量上传。这里我们将深入探讨如何在Java环境中,结合Struts2、Spring(s2sh框架)...

    extjs实现的带标签、翻页动画的书

    ExtJS 是一个强大的JavaScript 框架,专用于构建富客户端Web应用程序。它提供了一整套组件和工具,包括...通过查看和分析"example"目录中的文件,开发者可以学习到如何在实际项目中应用ExtJS,提升Web应用的用户体验。

    ssh整合实现登录的例子,包含源代码,用extjs做的登录界面

    在这个例子中,我们将深入探讨如何利用SSH整合来实现一个登录功能,并且这个功能的前端界面是通过ExtJS库创建的。 1. **Spring框架**:Spring是核心的依赖注入(DI)和面向切面编程(AOP)框架,它管理着应用中的...

    extjs+swfupload实现多文件上传下载删除带进度条

    `ExtJS`和`SwfUpload`是两个在Web开发中用于实现这些功能的工具。本篇将详细讲解如何利用`ExtJS`和`SwfUpload`来创建一个支持多文件上传、下载和删除,并带有进度条显示的系统。 `ExtJS`是一个基于JavaScript的UI...

    extjs实现增删查改

    在“extjs实现增删查改”这个主题中,我们将探讨如何使用ExtJS来实现基本的数据操作功能。 首先,增删查改(CRUD,Create, Read, Update, Delete)是任何数据管理应用的核心功能。在ExtJS中,我们可以利用Grid ...

Global site tag (gtag.js) - Google Analytics