<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Architecting Life &#187; Web</title>
	<atom:link href="http://xujiwei.com/blog/posts/develop/web/feed/" rel="self" type="application/rss+xml" />
	<link>http://xujiwei.com/blog</link>
	<description>Just do it</description>
	<lastBuildDate>Thu, 05 Apr 2012 17:19:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>推开窗户看世界 — Objective-C之外</title>
		<link>http://xujiwei.com/blog/besides-objective-c/</link>
		<comments>http://xujiwei.com/blog/besides-objective-c/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 10:03:45 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Mono]]></category>
		<category><![CDATA[PhoneGap]]></category>
		<category><![CDATA[Titanium]]></category>

		<guid isPermaLink="false">http://xujiwei.com/blog/?p=527</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/besides-objective-c/" title="推开窗户看世界 — Objective-C之外"></a>Besides Objective-C View more presentations from Jiwei Xu 引子 App Store 如今正风靡世界，许多人都想尝试去自己开发一个 iOS App，不过苹果官方推荐使用的是 Objective-C 这个语言。而 Objective-C 与 C、Java 之类的语言风格差异有些大，短时间并不容易掌握，又或者许多人只是想尝试开发一个可以运行在 iOS 上的程序而已，不想再去额外去学一门新的语言，那这个时候就可以考虑另外的一些技术来开发 iOS App。 在移动开发领域，得益于现在手机的性能越来越好，浏览器的功能越来越强大，JavaScript 也成为一门很流行的程序开发语言，而对应到 iOS 平台上，也有多种使用 JavaScript 来开发 iOS App 的技术。这次要介绍的三种技术之中，就有两种使用了 JavaScript。 另外，许多开发跨平台移动应用开发的解决方案，首选要支持的就是 iOS，因此在 iOS 这个平台上可以看到很多种不使用 Objective-C 去开发 &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/besides-objective-c/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/besides-objective-c/" title="推开窗户看世界 — Objective-C之外"></a><div style="width:425px" id="__ss_10556901"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/ohdarling88/besides-objectivec" title="Besides Objective-C" target="_blank">Besides Objective-C</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/10556901" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe> <div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/" target="_blank">presentations</a> from <a href="http://www.slideshare.net/ohdarling88" target="_blank">Jiwei Xu</a> </div> </div>

<h2>引子</h2>

<p>App Store 如今正风靡世界，许多人都想尝试去自己开发一个 iOS App，不过苹果官方推荐使用的是 Objective-C 这个语言。而 Objective-C 与 C、Java 之类的语言风格差异有些大，短时间并不容易掌握，又或者许多人只是想尝试开发一个可以运行在 iOS 上的程序而已，不想再去额外去学一门新的语言，那这个时候就可以考虑另外的一些技术来开发 iOS App。</p>

<p>在移动开发领域，得益于现在手机的性能越来越好，浏览器的功能越来越强大，JavaScript 也成为一门很流行的程序开发语言，而对应到 iOS 平台上，也有多种使用 JavaScript 来开发 iOS App 的技术。这次要介绍的三种技术之中，就有两种使用了 JavaScript。</p>

<p>另外，许多开发跨平台移动应用开发的解决方案，首选要支持的就是 iOS，因此在 iOS 这个平台上可以看到很多种不使用 Objective-C 去开发 iOS App 的技术，而且其中技术所编写出来的程序，除了可以在 iOS 上运行之外，还可以运行在 Android 以及其他系统平台上。能少写一份代码，何乐而不为呢～</p>

<h2>不使用 Objective-C</h2>

<p>这次要介绍的是三个不使用 Objective-C 来开发 iOS App 的解决方案：</p>

<ul>
<li><a href="http://www.appcelerator.com/products/titanium-mobile-application-development/">Titanium Mobile</a></li>
<li>PhoneGap(http://phonegap.com)</li>
<li>Mono Touch(http://xamarin.com/monotouch)</li>
</ul>

<p>这三个解决方案各有特点，它们的运行机制也有所不同，至于在开发 iOS App 时选用何种方案，可以根据每种方案不同的特点去选择适合自己的。</p>

<h2>Titanium Mobile</h2>

<p>Titanium Mobile 是 AppCelerator 推出的一个基于 JavaScript 的跨平台移动应用开发技术。通过 Titanium Mobile，可以直接使用 JavaScript 开发能运行于 iOS 和 Android 平台的应用程序，而代码只需要写一次。</p>

<p>Titanium Mobile 是通过将 JavaScript 代码映射到对应平台的 Native Code，在 Titanium Mobile 中操作某一个 UI 对象，或者是其他对象时，实时上是在操作对应平台中实际的 UI 对象，例如使用 Ti.UI.createView() 创建一个视图，它在 iOS 中运行时会实际对应到 UIView，使用 Ti.UI.createTabGroup() 就会对应到 iOS 中的 UITabBarController。这样做的好处就是，可以直接使用 JavaScript 来创建出原生的 UI，而不需要使用额外的代码来让程序 UI 更像系统的 UI。</p>

<p>另外，AppCelerator 在收购了 <a href="http://aptana.com/">Apatna</a> 之后，推出了适用于 Titanium 的 IDE <a href="http://www.appcelerator.com/products/titanium-studio/">Titanium Studio</a>，通过使用 Titanium Studio，可以很方便的创建项目、带智能提示的编辑器，以及很方便的调试项目。</p>

<p>在 Titanium Studio 中调试 iOS 项目时，可以做到单步调试，也就意味着，可以拥有不弱于使用 Xcode 开发项目的体验。断点、单步、变量查看功能一应具全。如果需要发布到 App Store，也可以很方便的通过图形化界面来打包。</p>

<p>当然，使用 Titanium Studio 也需要配合 Xcode 来使用，必须要在安装了 Xcode 的情况下，才可以使用 Titanium Studio 来调试和测试项目。</p>

<p>在 Titanium Studio 推出之前，Titanium Mobile 是靠一个 Developer Tool 来进行打包等操作的，相对比较繁琐，而有了 Titanium Studio 之后，让一切变得简单，我也是从那时开始关注 Titanium Mobile。</p>

<p>对于前端开发工程师来说，只需要理解了 iOS 开发中的一些概念，就可以使用 Titanium Mobile 来开发一个像模像样的 iOS App。并且 Titanium Mobile 实现了 CommonJS 规范，可以很方便来模块化程序代码。</p>

<h2>PhoneGap</h2>

<p>PhoneGap 是一个使用 HTML+CSS+JavaScript 来开发移动 App 的解决方案，使用它来开发 App 只需要有 Web 开发基础即可。它在今年10月份被 Adobe 收购了，然后加入到 Apache Software Foundation 进行孵化。</p>

<p>目前 PhoneGap 已经支持了市面上大多数的智能手机平台，其中就有 iOS。</p>

<p>在我看来，PhoneGap 其实只是提供了一个运行于各种智能手机平台的浏览器的壳，通过这个壳，PhoneGap 的 JavaScript 库可以和系统进行沟通，从而实现在 Web 页面中与系统交互的功能。</p>

<p>因为 PhoneGap 只提供了系统功能的 API 调用，而没有提供任何和界面相关的 API，那么界面就只能自己来折腾了。</p>

<p>幸好开源的世界是强大的，已经有了一堆这样的界面库来供我们选择，像 <a href="http://jqtouch.com/">jQTouch</a>、<a href="http://jquerymobile.com/">jQuery Mobile</a>、<a href="http://www.sencha.com/products/touch">Sencha Touch</a> 之类，都提供了类似于 iOS 的界面组件，可以让我们省去许多界面上的编码工作。</p>

<p>使用浏览器来运行 App 的一个坏处就是：慢。因为整个 App 就是一个网页，如果编码不当，整个程序使用起来体验会比较差，和原生的应用会有很大区别。</p>

<p>在上面提到的几个 UI 框架之中，我比较喜欢的是 jQTouch，它只提供了基本 UI 框架和一些视图切换效果，做出来的程序是一个单页面 App，在整个程序的反应速度上会感觉比较好。不过 jQTouch 也缺少一些常用的组件像对话框，而这些在 jQuery Mobile 和 Sencha Touch 之中又有提供。选择使用哪个框架就要看个人喜好了，它们的网站都提供了在线预览功能</p>

<p>其实，如果用不到系统的 API，只是想把自己的程序包装成一个很像 App 的 App 的话，只要用一用 PhoneGap 提供的壳就行了，它的 API 几乎可以不用看，配合一些 UI 框架可以快速的产出一个 iOS App 来。</p>

<p>当然了，PhoneGap 最大的好处就是，几乎跨了几乎所有的主流智能手机平台，这对于初创团队来说，是迅速推出各个平台客户端的一个好方法。另外 PhoneGap 官方网站也提供了一系列配套服务，例如使用 PhoneGap Build 可以直接编译各个平台的程序安装包，而不需要开发者自己在本地配置每个平台的编译环境。</p>

<h2>Mono Touch</h2>

<p>Mono Touch 是一个使用 C# 来编写跨平台应用的框架，同样支持了 iOS 和 Android 两大平台。</p>

<p>Mono Touch 对于 .NET 程序员来说应该是一个好消息，除了调试，其他的都可以在 Windows 中搞定，调试的时候可以通过在虚拟机里运行 Mac OS X 来解决。Mono Touch 因为是基于 Mono 的，也有个配套的 IDE 可以用：Mono Develop。因为都是 .NET，当然也可以用 Vistual Studio 这个更强大的工具了。</p>

<p>与 Titanium Mobile 和 PhoneGap 不同的是，Mono Touch 如果要编译到设备，或者发布到 App Store 的话，是需要收费的。</p>

<p>另外，因为 Mono Touch 也是编译成可执行代码再部署到设备上的，因此运行速度相对于 PhoneGap 所制作的应用来说，应该会快上一些。</p>

<p>不过 Mono Touch 要收费，并且收费还不是很便宜，可能会影响到它的普及率。</p>

<h2>小结</h2>

<p>在上面讲的三个解决方案之外，还有其他好多大大小小的移动应用开发解决方案，但是用于 iOS 上的解决方案，主要也就是映射代码、或者是包装的形式了，对于应用而不是游戏类型来说，一般都是够用的，在具体选择的时候，也可以根据自己更为熟悉哪种语言，或者框架来挑选。</p>

<p>如果你发现了其他有意思的移动应用开发技术，也可以和我分享一下 :)</p>

<h2>参考资料</h2>

<ol>
<li><a href="http://www.appcelerator.com/products/titanium-mobile-application-development/">http://www.appcelerator.com/products/titanium-mobile-application-development/</a></li>
<li><a href="http://developer.appcelerator.com/apidoc/mobile/latest">http://developer.appcelerator.com/apidoc/mobile/latest</a></li>
<li><a href="http://phonegap.com/about">http://phonegap.com/about</a></li>
<li><a href="https://build.phonegap.com/">https://build.phonegap.com/</a></li>
<li><a href="http://www.mono-project.com/Main_Page">http://www.mono-project.com/Main_Page</a></li>
<li><a href="http://xamarin.com/monotouch">http://xamarin.com/monotouch</a></li>
<li><a href="https://github.com/xamarin/monotouch-samples">https://github.com/xamarin/monotouch-samples</a></li>
<li><a href="http://blog.zhaojie.me/2010/09/develop-ios-app-with-monotouch-in-visual-studio-1.html">http://blog.zhaojie.me/2010/09/develop-ios-app-with-monotouch-in-visual-studio-1.html</a></li>
</ol>

<p>&#8211;EOF&#8211;</p>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/besides-objective-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用 node.js + nginx 建设网站</title>
		<link>http://xujiwei.com/blog/build-site-with-nodejs-and-nginx/</link>
		<comments>http://xujiwei.com/blog/build-site-with-nodejs-and-nginx/#comments</comments>
		<pubDate>Sun, 11 Dec 2011 09:41:49 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[cluster]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[nodejs]]></category>

		<guid isPermaLink="false">http://xujiwei.com/blog/?p=525</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/build-site-with-nodejs-and-nginx/" title="使用 node.js + nginx 建设网站"></a>昨天搞定了一个小网站的搭建，用了 node.js，另外为了能在一个 VPS 上搭建多个网站，用了 nginx 作为反向代理。 软件介绍 嗯，从维基上复制了一下～ node.js Node.js是一个事件驱动I/O伺服端JavaScript环境，基于V8。目的是为了提供撰写可扩充网络程式，如web服务。第一个版本由Ryan Dahl于2009年释出，后来，Joyent雇用了Dahl，并协助发展Node.js。 nginx nginx（发音同engine x）是一款由俄罗斯程序员Igor Sysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件（IMAP/POP3）代理服务器。 cluster 在 node.js 0.6.0 之前，有一个第三方的 node.js 模块 cluster，用来进行多核服务器上运行 node.js，以及提供扩展的支持。但是在 node.js 0.6.0 之后，node.js 本身就提供了 cluster 的支持，另外，第三方的 cluster 也与 node.js 0.6 有兼容性问题。目前 node.js 的稳定版本是 0.6.5，因此需要使用原生的 cluster 来代替第三方的 &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/build-site-with-nodejs-and-nginx/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/build-site-with-nodejs-and-nginx/" title="使用 node.js + nginx 建设网站"></a><p>昨天搞定了一个小网站的搭建，用了 node.js，另外为了能在一个 VPS 上搭建多个网站，用了 nginx 作为反向代理。</p>

<h2>软件介绍</h2>

<p>嗯，从维基上复制了一下～</p>

<p><strong>node.js</strong></p>

<blockquote>
  <p>Node.js是一个事件驱动I/O伺服端JavaScript环境，基于V8。目的是为了提供撰写可扩充网络程式，如web服务。第一个版本由Ryan Dahl于2009年释出，后来，Joyent雇用了Dahl，并协助发展Node.js。</p>
</blockquote>

<p><strong>nginx</strong></p>

<blockquote>
  <p>nginx（发音同engine x）是一款由俄罗斯程序员Igor Sysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件（IMAP/POP3）代理服务器。</p>
</blockquote>

<h2>cluster</h2>

<p>在 node.js 0.6.0 之前，有一个第三方的 node.js 模块 cluster，用来进行多核服务器上运行 node.js，以及提供扩展的支持。但是在 node.js 0.6.0 之后，node.js 本身就提供了 cluster 的支持，另外，第三方的 cluster 也与 node.js 0.6 有兼容性问题。目前 node.js 的稳定版本是 0.6.5，因此需要使用原生的 cluster 来代替第三方的 cluster。</p>

<p>幸好内置的 cluster 也足够简单，如果只是为了多核负载均衡，以及支持即时服务重启的话，只需要写一点的代码就可以完成这些功能了。</p>

<p><strong>server.js</strong></p>

<pre><code>var path = require('path');
var http = require('http');
var cluster = require('cluster');

var NODE_ENV = process.env.NODE_ENV || 'production';
var appName = path.basename(__dirname);
var appPort = 9000;

var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    process.title = appName + ' master';
    console.log(process.title, 'started');

    // 根据 CPU 个数来启动相应数量的 worker
    for (var i = 0; i &lt; numCPUs; i++) {
        cluster.fork();
    }

    process.on('SIGHUP', function() {
        // master 进程忽略 SIGHUP 信号
    });

    cluster.on('death', function(worker) {
        console.log(appName, 'worker', '#' + worker.pid, 'died');
        cluster.fork();
    });

} else {
    process.title = appName + ' worker ' + process.env.NODE_WORKER_ID;
    console.log(process.title, '#' + process.pid, 'started');

    process.on('SIGHUP', function() {
        // 接收到 SIGHUP 信号时，关闭 worker
        process.exit(0);
    });

    http.Server(function(req, res) {
        res.writeHead(200);
        res.end('Worker ' + process.env.NODE_WORKER_ID);
    }).listen(8000);
}
</code></pre>

<p>运行服务 <code>node server.js</code> ：</p>

<pre><code>nodejs master started
nodejs worker 1 #38928 started
nodejs worker 3 #38930 started
nodejs worker 2 #38929 started
nodejs worker 4 #38931 started
</code></pre>

<p>如果直接 kill 掉某一个 worker，<code>kill 38928</code>：</p>

<pre><code>nodejs worker #38931 died
nodejs worker 5 #38934 started
</code></pre>

<p>可以看到一个新的 worker 会马上启动，这就保证了服务的不间断性。</p>

<h2>Virtual Host 支持</h2>

<p>通常情况下，我们不会在一个 IP 上只部署一个网站。在使用 node.js 时，可以使用 connect 提供的 vhost 支持 Virtual Host，但是，这也限制了服务器只能用 node.js，而不能同时使用其他的服务，例如再安装一个 PHP 服务之类。</p>

<p>这时就可以使用 nginx 的反向代理来解决了，用户在访问网站时，请求先到 nginx 进行处理，如果是 node.js 站点的话，将请求转发到 node.js 的服务，然后再将 node.js 服务的结果返回给用户。</p>

<p>在 nginx 中设置反向代理很简单，一句 <code>proxy_pass</code> 就可以搞定：</p>

<pre><code>server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:9000;
    }
}
</code></pre>

<p>在添加了 Virtual Host 之后，就可以把一些静态资源，例如 CSS、JavaScript 之类的文件，直接交给 nginx 来处理，而不是什么请求都需要到 node.js 这一层去处理，也省去反向代理这一关的消耗。</p>

<h2>Session 支持</h2>

<p>使用 express 这个 node.js web framework 来创建网站时，可以配合 connect 这个中间件来实现 session 支持。</p>

<p>默认情况下，connect 的 session 是使用内置的内存存储来存放 session 信息，这时如果 node.js 服务一旦重启，所有的 session 信息都会丢失，这对于用户来说不是个好体验，那么我们可以用外部的存储来存放 session 信息，例如 redis。</p>

<p>要让 connect 使用 redis 作为 session 存储的话也是很方便的：</p>

<pre><code>var express = require('express');
var RedisStore = require('connect-redis')(express);

var app = express.createServer(
    express.session({ secret: 'keyboard cat', store : new RedisStore() })
);
</code></pre>

<h2>监测文件改动</h2>

<p>在调试的时候，经常需要重新启动 node.js 以便修改过的文件生效，原来第三方的 cluster 有一个配置项，可以很方便的配置监测时间间隔，文件改动后自动重新启动 worker，但是原生的 cluster 就没有这个功能了，需要自己来实现。</p>

<p>fs 模块提供了 watch 函数，可以方便的监测文件修改，使用这个就可以来实现文件修改后自动重启 woker 功能了。</p>

<pre><code>if (cluster.isMaster) {
    process.title = appName + ' master';
    console.log(process.title, 'started');

    var workers = [];

    // 根据 CPU 个数来启动相应数量的 worker
    for (var i = 0; i &lt; numCPUs; i++) {
        var worker = cluster.fork();
        workers.push(worker.pid);
    }

    process.on('SIGHUP', function() {
        // master 进程忽略 SIGHUP 信号
    });

    // 监测文件改动，如果有修改，就将所有的 worker kill 掉
    fs.watch(__dirname, function(event, filename) {
        workers.forEach(function(pid) {
            process.kill(pid);
        });
    });

    cluster.on('death', function(worker) {
        var index = workers.indexOf(worker.pid);
        if (index != -1) {
            workers.splice(index, 1);
        }
        console.log(appName, 'worker', '#' + worker.pid, 'died');
        worker = cluster.fork();
        workers.push(worker.pid);
    });

}
</code></pre>

<p>这样，每次文件保存之后，node.js 都会自动重启，从而避免了每次保存文件要手动重启服务的麻烦。</p>

<p>当然，在使用监测文件自动重启的时候，最好加上 NODE_ENV 的判断，在 development 的时候才进行自动重启，而 production 的时候使用手动重启就够了。</p>

<h2>小结</h2>

<p>总的来说，使用 node.js 来构建网站还是很方便的，加上 nginx 反向代理之后，与使用 PHP 之前也没有很大的区别，又可以享受到 node.js 的高效。</p>

<p>嗯，就这样了，希望此文对你有所帮助。</p>

<h2>参考资料</h2>

<ol>
<li><a href="http://zh.wikipedia.org/wiki/Node.js">http://zh.wikipedia.org/wiki/Node.js</a></li>
<li><a href="http://nodejs.org/docs/v0.6.5/api/cluster.html">http://nodejs.org/docs/v0.6.5/api/cluster.html</a></li>
<li><a href="http://learnboost.github.com/cluster/">http://learnboost.github.com/cluster/</a></li>
<li><a href="http://expressjs.com/">http://expressjs.com/</a></li>
<li><a href="http://senchalabs.github.com/connect/">http://senchalabs.github.com/connect/</a></li>
<li><a href="http://redis.io/">http://redis.io/</a></li>
</ol>

<p>&#8211;EOF&#8211;</p>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/build-site-with-nodejs-and-nginx/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>慎用 script 节点的 src 属性来传递参数</title>
		<link>http://xujiwei.com/blog/avoid-pass-parameters-through-src/</link>
		<comments>http://xujiwei.com/blog/avoid-pass-parameters-through-src/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 14:49:45 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=63</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/avoid-pass-parameters-through-src/" title="慎用 script 节点的 src 属性来传递参数"></a>在有些使用 javascript 来渲染数据的时候，为了能动态获取不同的数据，并且保持 javascript 代码的可扩展性，会将 javascript 代码中获取数据的部分需要的参数提取出来，做为参数放在 script 节点的外部。 一般来说，传递参数到 javascript 文件内部的方法有两种，一种是将参数写在一个 script 节点中，写成全局变量的方式的传递给紧接着这个 script 节点的外部 javascript 中，Google Analytics 就是使用这样的方式： &#60;script type=&#34;text/javascript&#34;&#62; var p1 = &#34;v1&#34;, p2 = &#34;v2&#34;; &#60;/script&#62; &#60;script type=&#34;text/javascript&#34; src=&#34;foo.js&#34;&#62;&#60;/script&#62; 另外一种是将参数直接写在 script 节点的 src 属性中，相当于一个页面的查询字符串一样： &#60;script type=&#34;text/javascript&#34; src=&#34;foo.js?p1=v1&#38;p2=v2&#34;&#62;&#60;/script&#62; 不过，使用 script 节点的 src 属性来传递参数需要注意一个很重要的问题，那就是动态变化的 src 属性会导致缓存失效。 现在，为了网站性能的需要，一般都会将 javascript 文件放在独立的服务器上，并设置一个较长的过期时间，这样客户端只会在第一次访问网站时需要去下载这个 javascript 文件。但是，如果使用 src 来传递参数，就可能会使这种缓存策略失效。特别是 src 中存在动态参数的情况，例如统计脚本中如果有一个 ip 参数，那么访客每次连上线时，可能 ip 都会不同，就会导致 javascript 缓存失效了。 解决这个问题的方法也很简单，简单地的将 src 属性中的参数放到 script 节点的一个自定义属性中就可以了，例如 data-args，而 src 属性只需要保留一个时间戳就可以了。因为使用 src 属性来传递参数本来就需要定位 script 节点，所以改由 data-args 自定义属性来传递参数并不会增加额外的代码。只不过页面会通不过 w3c 的验证罢了 :) &#38;lt;script type=&#34;text/javascript&#34; src=&#34;foo.js&#34; data-args=&#34;p1=v1&#38;amp;p2=v2&#34;&#38;gt;&#38;lt;/script&#38;gt; 再次提醒，慎用 script 节点的 src 属性来传递参数 :)]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/avoid-pass-parameters-through-src/" title="慎用 script 节点的 src 属性来传递参数"></a><p>在有些使用 javascript 来渲染数据的时候，为了能动态获取不同的数据，并且保持 javascript 代码的可扩展性，会将 javascript 代码中获取数据的部分需要的参数提取出来，做为参数放在 script 节点的外部。</p>

<p>一般来说，传递参数到 javascript 文件内部的方法有两种，一种是将参数写在一个 script 节点中，写成全局变量的方式的传递给紧接着这个 script 节点的外部 javascript 中，Google Analytics 就是使用这样的方式：</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;script <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/javascript&quot;</span>&gt;</span>
var p1 = &quot;v1&quot;, p2 = &quot;v2&quot;;
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span>
<span style="color: #009900;">&lt;script <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/javascript&quot;</span> <span style="color: #000066;">src</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;foo.js&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span></pre></div></div>


<p>另外一种是将参数直接写在 script 节点的 src 属性中，相当于一个页面的查询字符串一样：</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;script <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/javascript&quot;</span> <span style="color: #000066;">src</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;foo.js?p1=v1&amp;p2=v2&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span></pre></div></div>


<p>不过，使用 script 节点的 src 属性来传递参数需要注意一个很重要的问题，那就是<strong>动态变化的 src 属性会导致缓存失效</strong>。</p>

<p>现在，为了网站性能的需要，一般都会将 javascript 文件放在独立的服务器上，并设置一个较长的过期时间，这样客户端只会在第一次访问网站时需要去下载这个 javascript 文件。但是，如果使用 src 来传递参数，就可能会使这种缓存策略失效。特别是 src 中存在动态参数的情况，例如统计脚本中如果有一个 ip 参数，那么访客每次连上线时，可能 ip 都会不同，就会导致 javascript 缓存失效了。</p>

<p>解决这个问题的方法也很简单，简单地的将 src 属性中的参数放到 script 节点的一个自定义属性中就可以了，例如 data-args，而 src 属性只需要保留一个时间戳就可以了。因为使用 src 属性来传递参数本来就需要定位 script 节点，所以改由 data-args 自定义属性来传递参数并不会增加额外的代码。只不过页面会通不过 w3c 的验证罢了 :)</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot; src=&quot;foo.js&quot; data-args=&quot;p1=v1<span style="color: #ddbb00;">&amp;amp;</span>p2=v2&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p>再次提醒，<strong>慎用</strong> script 节点的 src 属性来传递参数 :)</p>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/avoid-pass-parameters-through-src/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在 AIR 中突破同源策略访问 iframe 中的内容</title>
		<link>http://xujiwei.com/blog/access-iframe-in-air/</link>
		<comments>http://xujiwei.com/blog/access-iframe-in-air/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 12:20:15 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[AIR]]></category>
		<category><![CDATA[iframe]]></category>
		<category><![CDATA[Sandbox]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=139</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/access-iframe-in-air/" title="在 AIR 中突破同源策略访问 iframe 中的内容"></a>本篇所讲关于 AIR 的内容是基本 HTML+JS，而不是 Flex 或其他。 在 AIR 中，如果当前页面有一个 iframe，并且我们需要获取这个 iframe 的内容，但是如果 iframe 里包含的是远程页面，在默认情况下，浏览器策略并不允许这种情况，这就是同源策略的限制。 在 Titanium 这个类似于 AIR 的平台中，默认情况下是可以直接从应用的页面中去读取 iframe 中的内容而没有任何限制，但是在 AIR 中不可以。在网上搜索了一番，再看了好几遍 AIR 的文档之后，终于找到解决问题的方法了。 在 AIR 中，iframe 标签拥有额外的几个属性：sandboxRoot、documentRoot、allowCrossDomainXHR 及 ondominitialize。这里为了解决读取 iframe 内容所用到的属性就是 sandboxRoot 及 documentRoot。 通过 sandboxRoot 及 documentRoot，可以将本地的页面映射为远程页面，并在沙盒中运行，另外，在沙盒中运行的页面就会拥有映射域名的域。例如，我们可以将本地的 proxy.html 映射为 http://example.com/local/proxy.html，这样，在实际运行时，proxy.html 中如果用 document.domain 获取页面所在的域，就会得到 example.com，这个时候如果在 proxy.html 中添加一个 iframe，这个 iframe 指向我们实际需要获取内容的地址，因为域相同，就可以直接获取 iframe 的内容了。 例如，我们要在 AIR 中获取 http://example.com/member/login.html 的内容，或者操作这个页面的元素，就可以先用一个 proxy.html，用来实现将本地文件运行在沙盒中，再用一个 login.html 来对实际网页进行操作。 &#60;!&#8211; proxy.html &#8211;&#62; &#60;iframe src=&#8221;http://example.com/local/login.html&#8221; id=&#8221;frame&#8221; width=&#8221;100%&#8221; sandboxRoot=&#8221;http://example.com/local/&#8221; documentRoot=&#8221;app:/&#8221; name=&#8221;frame&#8221; height=&#8221;100%&#8221;&#62;&#60;/iframe&#62; &#60;!&#8211; login.html &#8211;&#62; &#60;iframe id=&#8217;f' frameborder=&#8221;0&#8243; scrolling=&#8221;no&#8221; src=&#8221;http://example.com/member/login.html&#8221; width=&#8221;100%&#8221; height=&#8221;100%&#8221;&#62;&#60;/iframe&#62; &#60;script type=&#8221;text/javascript&#8221;&#62; alert(document.domain); var f = document.getElementById(&#8216;f&#8217;); f.onload = function() { alert(f.contentWindow.document.documentElement.innerHTML); }; &#60;/script&#62; 在 proxy.html 中，虽然 iframe 的 src 属性是 http://example.com/locale/login.html，但是由于这个 iframe 具有 sandboxRoot 属性，所以实际请求会被重定向到相对于 sandboxRoot，并且使用 documentRoot 进行定位的实际地址 app://login.html，但是这个时候 login.html 中具有  document.domain=&#8221;example.com&#8221; 这个属性，所有可以直接使用 iframe.contentWindow 来访问 iframe 中的内容。 虽然解决方法有些麻烦，但终归可以实现突破同源策略的限制来读取 iframe 的内容了。 当然，如果你有更好的方法，请不吝赐教：）]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/access-iframe-in-air/" title="在 AIR 中突破同源策略访问 iframe 中的内容"></a><div class="post-content">

本篇所讲关于 AIR 的内容是基本 HTML+JS，而不是 Flex 或其他。

在 <a rel="external" href="get.adobe.com/air/">AIR</a> 中，如果当前页面有一个 iframe，并且我们需要获取这个 iframe 的内容，但是如果 iframe 里包含的是远程页面，在默认情况下，浏览器策略并不允许这种情况，这就是同源策略的限制。

在 <a rel="external" href="http://www.appcelerator.com">Titanium</a> 这个类似于 AIR 的平台中，默认情况下是可以直接从应用的页面中去读取 iframe 中的内容而没有任何限制，但是在 AIR 中不可以。在网上搜索了一番，再看了好几遍 AIR 的文档之后，终于找到解决问题的方法了。

在 AIR 中，<a rel="external" href="http://help.adobe.com/en_US/AIR/1.5/devappsflex/WS5b3ccc516d4fbf351e63e3d118666ade46-7eb2.html#WS5b3ccc516d4fbf351e63e3d118666ade46-7edb">iframe 标签拥有额外的几个属性</a>：sandboxRoot、documentRoot、allowCrossDomainXHR 及 ondominitialize。这里为了解决读取 iframe 内容所用到的属性就是 sandboxRoot 及 documentRoot。

通过 sandboxRoot 及 documentRoot，可以将本地的页面映射为远程页面，并在沙盒中运行，另外，在沙盒中运行的页面就会拥有映射域名的域。例如，我们可以将本地的 proxy.html 映射为 http://example.com/local/proxy.html，这样，在实际运行时，proxy.html 中如果用 document.domain 获取页面所在的域，就会得到 example.com，这个时候如果在 proxy.html 中添加一个 iframe，这个 iframe 指向我们实际需要获取内容的地址，因为域相同，就可以直接获取 iframe 的内容了。

例如，我们要在 AIR 中获取 http://example.com/member/login.html 的内容，或者操作这个页面的元素，就可以先用一个 proxy.html，用来实现将本地文件运行在沙盒中，再用一个 login.html 来对实际网页进行操作。
<blockquote>
<div id="CODE_6635" class="codeMain"><span style="font-style: normal;">&lt;!&#8211; proxy.html &#8211;&gt;
&lt;iframe src=&#8221;http://example.com/local/login.html&#8221; id=&#8221;frame&#8221; width=&#8221;100%&#8221;
sandboxRoot=&#8221;http://example.com/local/&#8221;
documentRoot=&#8221;app:/&#8221; name=&#8221;frame&#8221; height=&#8221;100%&#8221;&gt;&lt;/iframe&gt;</span></div></blockquote>
<blockquote>
<div id="CODE_4316" class="codeMain">
<ul></ul>
<em><span style="font-style: normal;">&lt;!&#8211; login.html &#8211;&gt;</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">&lt;iframe id=&#8217;f' frameborder=&#8221;0&#8243; scrolling=&#8221;no&#8221;</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;"> src=&#8221;http://example.com/member/login.html&#8221; </span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;"> width=&#8221;100%&#8221; height=&#8221;100%&#8221;&gt;&lt;/iframe&gt;</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">&lt;script type=&#8221;text/javascript&#8221;&gt;</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">alert(document.domain);</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">var f = document.getElementById(&#8216;f&#8217;);</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">f.onload = function() {</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;"> alert(f.contentWindow.document.documentElement.innerHTML);</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">};</span></em><span style="font-style: normal;">
</span><em><span style="font-style: normal;">&lt;/script&gt;</span></em></div></blockquote>
在 proxy.html 中，虽然 iframe 的 src 属性是 http://example.com/locale/login.html，但是由于这个 iframe 具有 sandboxRoot 属性，所以实际请求会被重定向到相对于 sandboxRoot，并且使用 documentRoot 进行定位的实际地址 app://login.html，但是这个时候 login.html 中具有  document.domain=&#8221;example.com&#8221; 这个属性，所有可以直接使用 iframe.contentWindow 来访问 iframe 中的内容。

虽然解决方法有些麻烦，但终归可以实现突破同源策略的限制来读取 iframe 的内容了。

当然，如果你有更好的方法，请不吝赐教：）

</div>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/access-iframe-in-air/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用 arguments.caller 实现自动回调</title>
		<link>http://xujiwei.com/blog/autocallback-by-arguments-caller/</link>
		<comments>http://xujiwei.com/blog/autocallback-by-arguments-caller/#comments</comments>
		<pubDate>Sun, 25 Oct 2009 01:36:31 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[Arguments]]></category>
		<category><![CDATA[Callback]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=141</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/autocallback-by-arguments-caller/" title="使用 arguments.caller 实现自动回调"></a>这是一篇介绍类似于 js hack 的文章，只是说明了一种可行的途径，并且可能会增加代码复杂程度，具体项目中是否可以使用还请自辨：） 在前端开发过程中，有许多业务流程可能是需要用户进行登录的，并且登录过程是放在弹出层中，这样就可以不用刷新页面，增强用户体验。在登录时，用户的操作就会被打断，为了进一步增强用户体验，我们可能需要在登录完成后自动继续进行用户在登录前想进行的操作。 假设有这样一个场景，用户需要发表一个留言，但是发表留言是需要登录的，而发表留言的输入框是一直显示的，这也就要求在用户点击了发表按钮时对用户登录状态进行验证，传统的做法是将在用户登录状态检查封装在一个函数之中，这个函数接收一个回调参数，如果登录验证通过，则执行回调函数。这样的逻辑可以用以下代码表示： 程序代码：[ 复制代码到剪贴板 ] function doAction() { checkLogin(function() { // 处理业务逻辑 }); } function checkLogin(callback) { if (isLogin) { callback(); } else { showLogin(callback); } } function showLogin(callback) { document.getElementById(&#8220;login-btn&#8221;).onclick = function() { isLogin = true; callback(); }; } 总觉得这样的方式会将业务逻辑放到一个匿名函数中，而不是放在了按钮的事件响应函数中，感觉上不是那么好。这对整个事件响应函数的改动比较大，主要的业务逻辑都放在了登录验证函数的回调中。而希望可以是下面这样： 程序代码：[ 复制代码到剪贴板 ] function doAction() { // 直接在函数开始进行登录验证，将业务逻辑独立出来 if (!doLogin()) { return; } // 处理业务逻辑 } function doLogin() { if (isLogin) { return true; } else { var callback = function() {}; // 自动组装 callback &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/autocallback-by-arguments-caller/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/autocallback-by-arguments-caller/" title="使用 arguments.caller 实现自动回调"></a><div class="post-content">

这是一篇介绍类似于 js hack 的文章，只是说明了一种可行的途径，并且可能会增加代码复杂程度，具体项目中是否可以使用还请自辨：）

在前端开发过程中，有许多业务流程可能是需要用户进行登录的，并且登录过程是放在弹出层中，这样就可以不用刷新页面，增强用户体验。在登录时，用户的操作就会被打断，为了进一步增强用户体验，我们可能需要在登录完成后自动继续进行用户在登录前想进行的操作。

假设有这样一个场景，用户需要发表一个留言，但是发表留言是需要登录的，而发表留言的输入框是一直显示的，这也就要求在用户点击了发表按钮时对用户登录状态进行验证，传统的做法是将在用户登录状态检查封装在一个函数之中，这个函数接收一个回调参数，如果登录验证通过，则执行回调函数。这样的逻辑可以用以下代码表示：
<div class="codeHead">程序代码：<a href="javascript:CopyText($('CODE_4316'));">[ 复制代码到剪贴板 ]</a></div>
<blockquote>
<div id="CODE_4316" class="codeMain"><span style="font-style: normal;">function doAction() {
checkLogin(function() {
// 处理业务逻辑
});
}
function checkLogin(callback) {
if (isLogin) {
callback();
} else {
showLogin(callback);
}
}
function showLogin(callback) {
document.getElementById(&#8220;login-btn&#8221;).onclick = function() {
isLogin = true;
callback();
};
}</span>
<ul></ul>
</div></blockquote>
总觉得这样的方式会将业务逻辑放到一个匿名函数中，而不是放在了按钮的事件响应函数中，感觉上不是那么好。这对整个事件响应函数的改动比较大，主要的业务逻辑都放在了登录验证函数的回调中。而希望可以是下面这样：
<div class="codeHead">程序代码：<a href="javascript:CopyText($('CODE_4415'));">[ 复制代码到剪贴板 ]</a></div>
<blockquote>
<div id="CODE_4415" class="codeMain"><span style="font-style: normal;">function doAction() {
// 直接在函数开始进行登录验证，将业务逻辑独立出来
if (!doLogin()) {
return;
}
// 处理业务逻辑
}
function doLogin() {
if (isLogin) {
return true;
} else {
var callback = function() {}; // 自动组装 callback
showLogin(callback);
return false;
}
}
function showLogin(callback) {
document.getElementById(&#8220;login-btn&#8221;).onclick = function() {
isLogin = true;
callback();
};
}</span>
<ul></ul>
</div></blockquote>
这里的关键在于怎么“自动组装 callback”，很幸运的是，JavaScript 的 Function 提供了一个属性 caller 可以用来获取调用当前函数的函数是什么。并且还可以用 caller 的 arguments 属性来获取 caller 执行时的参数是什么，这也就使我们自动组装 callback 并恢复 caller 的原参数成为可能。
<blockquote>
<p style="text-align: auto;"></p>

<span style="font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, Helvetica, Sans, FreeSans, Jamrul, Garuda, Kalimati; color: #000000;">
<div id="_mcePaste">
<div id="_mcePaste"><span style="font-style: normal;">&lt;div id=&#8221;result&#8221;&gt;&lt;/div&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">&lt;input type=&#8221;button&#8221; value=&#8221;Sumit&#8221; onclick=&#8221;sendPost()&#8221; id=&#8221;post&#8221; /&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">&lt;input type=&#8221;button&#8221; value=&#8221;Clear login&#8221; onclick=&#8221;isLogin=false&#8221; id=&#8221;reset&#8221; /&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">&lt;div id=&#8217;login&#8217; style=&#8221;border:1px solid #000;padding:10px;margin:10px;display:none&#8221;&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> &lt;strong&gt;Login&lt;/strong&gt;&lt;br /&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> &lt;input type=&#8221;button&#8221; id=&#8217;loginbtn&#8217; value=&#8221;Login&#8221; /&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">&lt;/div&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">&lt;script type=&#8221;text/javascript&#8221;&gt;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">// 是否登录</span></div>
<div id="_mcePaste"><span style="font-style: normal;">var isLogin = false;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">// 检查登录状态</span></div>
<div id="_mcePaste"><span style="font-style: normal;">var checkLogin = function(cfg) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> if (isLogin) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> return true;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> cfg = cfg || {};</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> var callback = null;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> // 自动执行回调</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> if (cfg.autoCallback &amp;&amp; arguments.callee.caller) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> try {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> var caller  = arguments.callee.caller,</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> args    = caller.arguments || [],</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> scope   = cfg.callbackScope || {},</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> newArgs = [];</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> for (var i = 0; i &lt; args.length; ++i) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> newArgs.push(args[i]);</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> callback = caller ? function() {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> try {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> caller &amp;&amp; caller.apply(scope, newArgs);</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> } catch (e) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> //alert(e.message);</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> } : null;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> } catch (e) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> callback = null;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> </span></div>
<div id="_mcePaste"><span style="font-style: normal;"> showLogin(callback);</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> </span></div>
<div id="_mcePaste"><span style="font-style: normal;"> return false;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">};</span></div>
<div id="_mcePaste"><span style="font-style: normal;">function sendPost() {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> if (!checkLogin({autoCallback:true})) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> return;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> </span></div>
<div id="_mcePaste"><span style="font-style: normal;"> document.getElementById(&#8216;result&#8217;).innerHTML += &#8216;Hello&lt;br /&gt;&#8217;;</span></div>
<div id="_mcePaste"><span style="font-style: normal;">}</span></div>
<div id="_mcePaste"><span style="font-style: normal;">function showLogin(callback) {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> document.getElementById(&#8216;login&#8217;).style.display = &#8216;;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> document.getElementById(&#8216;loginbtn&#8217;).onclick = function() {</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> document.getElementById(&#8216;login&#8217;).style.display = &#8216;none&#8217;;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> isLogin = true;</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> callback &amp;&amp; callback();</span></div>
<div id="_mcePaste"><span style="font-style: normal;"> }</span></div>
<div id="_mcePaste"><span style="font-style: normal;">}</span></div>
<div id="_mcePaste"><span style="font-style: normal;">&lt;/script&gt;</span></div>
</div>
</span></blockquote>
其实弄出这么个方法来实现登录自动回调也是为了方便写代码，不用每次去将一堆业务逻辑放到 callback 中，直接在函数开始回一个 if (!checkLogin({autoCallback:true})) 就行。

caller 属性在 MDC 中被标记为“非标准”的，但是在主流浏览器中，都是支持这个属性的，也就是说，在浏览器兼容性上使用 caller 是没有问题的。

不过，使用奇技淫巧容易伤身体，慎用：）

</div>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/autocallback-by-arguments-caller/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 Canvas 起步(3) &#8211; 颜色与渐变</title>
		<link>http://xujiwei.com/blog/canvas-starts-part3/</link>
		<comments>http://xujiwei.com/blog/canvas-starts-part3/#comments</comments>
		<pubDate>Sun, 07 Jun 2009 05:51:23 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[CanvasStarts]]></category>
		<category><![CDATA[HTML5]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=91</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/canvas-starts-part3/" title="HTML5 Canvas 起步(3) - 颜色与渐变"></a>上一篇介绍了 HTML5 中 Canvas 的路径，这篇将要介绍一下 Canvas 中的颜色及渐变。 Canvas 中的基本颜色系统 在 Canvas 中，颜色主要用途就是在绘制路径时，用来指定填充颜色和边框颜色。 Canvas 中的颜色参数值有两种格式： 如果透明度为 1.0，也就是不透明，颜色值的格式就与一般使用一样，为：#AABBCC，其中 AA、BB、CC 分别为 Red、Green、Blue 分量。 如果透明度不为 1.0，也就是带透明，颜色值格式可以使用 rgba(r, g, b, a)，其中 r、g、b、a 分别为 Red、Green、Blue 分量和透明度。透明度的值为 0 至 1.0 之间的一个数值。 颜色值还可以为已知的颜色名称，例如 red、blue、green 等。 总的说来，Canvas 中颜色值的格式与 CSS &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/canvas-starts-part3/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/canvas-starts-part3/" title="HTML5 Canvas 起步(3) - 颜色与渐变"></a><p><a title="HTML5 Canvas 起步(2) – 路径" rel="external" href="http://www.xujiwei.com/blog/2009/05/05/canvas-starts-part2/">上一篇</a>介绍了 HTML5 中 Canvas 的路径，这篇将要介绍一下 Canvas 中的颜色及渐变。</p>

<p><strong>Canvas 中的基本颜色系统</strong></p>

<p>在 Canvas 中，颜色主要用途就是在绘制路径时，用来指定填充颜色和边框颜色。</p>

<p>Canvas 中的颜色参数值有两种格式：</p>

<ol>
<li><p>如果透明度为 1.0，也就是不透明，颜色值的格式就与一般使用一样，为：#AABBCC，其中 AA、BB、CC 分别为 Red、Green、Blue 分量。</p></li>
<li><p>如果透明度不为 1.0，也就是带透明，颜色值格式可以使用 rgba(r, g, b, a)，其中 r、g、b、a 分别为 Red、Green、Blue 分量和透明度。透明度的值为 0 至 1.0 之间的一个数值。</p></li>
<li><p>颜色值还可以为已知的颜色名称，例如 red、blue、green 等。</p></li>
</ol>

<p>总的说来，Canvas 中颜色值的格式与 CSS 中一致，因此颜色值没有特别需要注意的地方。</p>

<p><a rel="external" href="http://picasaweb.google.com/lh/photo/uMh6xFXBiybwGLZui9KkSg?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img src="http://lh3.ggpht.com/_QJK_LPJoebg/SggmGCtKCBI/AAAAAAAAAyc/T0MvsXpi5i4/s800/3-colors.jpg" alt="图片附件" /></a></p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">div</span> <span style="color: #000066;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;_mcePaste&quot;</span>&gt;</span><span style="color: #ddbb00;">&amp;lt;</span>canvas id=&quot;canvas&quot; width=&quot;600&quot; height=&quot;500&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/canvas<span style="color: #ddbb00;">&amp;gt;</span>
<span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    // fill a rectangle whit sepecified fill style
    function fillRect(color) {
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, 150, 100);
        ctx.font = &quot;10pt Arial&quot;;
        ctx.fillStyle = &quot;white&quot;;
        ctx.fillText(color, 6, 20);
        ctx.translate(160, 0);
    }
    ctx.translate(0, 50);
    // fill rect with color name
    fillRect(&quot;green&quot;);
    // fill rect with 0xAABBCC
    fillRect(&quot;#AABBCC&quot;);
    // fill rect with rgba(50, 100, 150, 0.5)
    ctx.translate(0, -25);
    fillRect(&quot;rgba(50,100,150,0.5)&quot;);
&nbsp;
    // fill another rect with rgba(150, 100, 50, 0.5)
    ctx.translate(-110, 50);
    fillRect(&quot;rgba(150, 100, 50, 0.5)&quot;);
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span><span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">div</span>&gt;</span></pre></div></div>


<p><span style="color: #333333; font-family: Corbel, 微软雅黑, Verdana, Arial, sans-serif; line-height: 21px; font-size: 14px;"> </span></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">注意，以上代码需要使用 Firefox 3.5 来查看，在 Firefox 3.0.x 中，Canvas 的 Context 对象不支持 fillText 方法，而我安装的 Chrome 2.0.174.0 对 translate 方法的实现有误。</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><strong>2. Canvas 中的渐变</strong></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">WHATWG 的 Canvas 规范中规划了两种渐变模式，一种是<strong>线性渐变</strong>，另一种是<strong>径向渐变</strong>。如果需要在 Canvas 中使用渐变，首先要根据你所要创建的渐变模式来调用 Context 的相应方法来创建一个渐变对象，这个对象就是用来控制渐变的效果。</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><span id="more-91"></span></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><strong>2.1 线性渐变</strong></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">线性渐变使用 Canvas Context 的 createLinearGradient 方法创建，它的定义如下：</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><em>CanvasGradient createLinearGradient(in float x0, in float y0, in float x1, in float y1);</em></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">通过 (x0, y0) 与 (x1, y1) 指定渐变的开始位置与截止位置。当然，创建完线性渐变对象并不能完全开始使用渐变对象，你需要给它指定渐变的起始色以及终止色。CanvasGradient 对象有一个 addColorStop 方法用来添加颜色。</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><em>gradient.addColorStop(offset, color)</em></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">在线性渐变中，偏移量 offset 只能取 0 或 1，分别代表线性渐变的起始色和终止色。color 参数的值可以参考上面说明的颜色格式。</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">例如，我要创建一个从 (20, 20) 到 (150, 150) 的一个渐变，从绿色渐变到白色透明，就可以按照下面的代码来创建 CanvasGradient 对象：</p>

<p><span style="color: #333333; font-family: Corbel, 微软雅黑, Verdana, Arial, sans-serif; line-height: 21px; font-size: 14px;"> </span></p>

<ul style="padding: 0px; margin: 0px;">
<blockquote>
    <li style="list-style-type: none; list-style-position: initial; list-style-image: initial; background-color: transparent; padding: 0px; margin: 0px; border: initial none initial;">var gradient = ctx.createLinearGradient(20, 20, 150, 150);</li>
    <li style="list-style-type: none; list-style-position: initial; list-style-image: initial; background-color: transparent; padding: 0px; margin: 0px; border: initial none initial;">gradient.addColorStop(0, &#8217;green&#8217;);</li>
    <li style="list-style-type: none; list-style-position: initial; list-style-image: initial; background-color: transparent; padding: 0px; margin: 0px; border: initial none initial;">gradient.addColorStop(1, &#8217;rgba(255,255,255,0)&#8217;);</li>
</blockquote>
    <li style="list-style-type: none; list-style-position: initial; list-style-image: initial; background-color: transparent; padding: 0px; margin: 0px; border: initial none initial;">
<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">创建完渐变对象后，可以直接将它赋值给 CanvasContext 的 fillStyle 属性或 strokeStyle 属性，用来指定填充的渐变色或画边框的渐变色。也就是说，<strong>CanvasGradient 对象的用法等同于颜色参数值，通过赋值给 Context 对象来起作用。</strong></p>
<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><a style="color: #0066cc; text-decoration: none;" rel="external" href="http://picasaweb.google.com/lh/photo/DFrLdhJzET3jW9pZ3jyF3w?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img style="border: 0px initial initial;" src="http://lh3.ggpht.com/_QJK_LPJoebg/ShFRTZW4NLI/AAAAAAAAA5o/ZA3OA6a4xFc/s800/3-lineargradient.jpg" alt="图片附件" /></a></p>
<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"></p>
<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"></p>
</li>
</ul>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    // fill a rectangle whit sepecified fill style
    function fillRect(color, x, y, h, w) {
        ctx.fillStyle = color;
        ctx.fillRect(x || 0, y || 0, h || 150, w || 100);
        ctx.font = &quot;10pt Arial&quot;;
        ctx.fillStyle = &quot;white&quot;;
        ctx.fillText(color, 6, 20);
    }
    ctx.translate(0, 50);
    // fill rect with color name
    fillRect(&quot;green&quot;);
    // 从绿色渐变到白色完全透明，并指定给填充色
    var gradient = ctx.createLinearGradient(20, 20, 150, 150);
    gradient.addColorStop(0, 'green');
    gradient.addColorStop(1, 'rgba(255,255,255,0)');
    ctx.translate(250, 0);
    fillRect(gradient, -50, -50, 250, 200);
    fillRect(gradient);
&nbsp;
    // 从白色到黑色的渐变，并指定给边框色
    gradient = ctx.createLinearGradient(0, 0, 150, 100);
    gradient.addColorStop(0, 'white');
    gradient.addColorStop(1, 'black');
    ctx.strokeStyle = gradient;
    ctx.strokeRect(0, 0, 150, 100);
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p><span style="color: #333333; font-family: Corbel, 微软雅黑, Verdana, Arial, sans-serif; line-height: 21px; font-size: 14px;"> </span></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><strong>2.2 径向渐变</strong></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">与线性的用法类似，它的函数签名如下：</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><em>CanvasGradient createRadialGradient(in float x0, in float y0, in float r0, in float x1, in float y1, in float r1);</em></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">与线性渐变不同的是，径向渐变除了要指定起始位置和终止位置外，还需要指定起始半径和终止半径。使用不同中心点的径向渐变可以实现类似光照的效果，不过目前 Chrome 对不同中心点的径向渐变支持不好，在 Chrome 中只会使用第二个中心点进行径向渐变，测试发现在最新开发版 Chrome 3.0.184.0 (17842) 中仍是如此。</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;">在下图中可以看到，主流支持 Canvas 的浏览器都能正确渲染中心点不同的径向渐变，而 Chrome 则只能使用第二个中心点进行渲染。</p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"><a style="color: #0066cc; text-decoration: none;" rel="external" href="http://picasaweb.google.com/lh/photo/cI7CFzU3F7p-JTIPf38oxw?authkey=Gv1sRgCNKYm-3IovqotQE&amp;feat=embedwebsite"></a><a style="color: #0066cc; text-decoration: none;" title="点击在新窗口中打开图片" rel="external" href="http://lh4.ggpht.com/_QJK_LPJoebg/SitS2qj6AfI/AAAAAAAABC0/a4SGn23MbbI/s800/3-radialgradient.jpg"><img style="border: 0px initial initial;" src="http://lh4.ggpht.com/_QJK_LPJoebg/SitS2qj6AfI/AAAAAAAABC0/a4SGn23MbbI/s800/3-radialgradient.jpg" alt="图片附件" width="462" height="477" /></a></p>

<p style="margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.7em; margin-left: 0px; text-indent: 2em; line-height: 1.5em; padding: 0px;"></p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>canvas id=&quot;canvas&quot; width=&quot;600&quot; height=&quot;500&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/canvas<span style="color: #ddbb00;">&amp;gt;</span>
<span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = fixContext(canvas.getContext(&quot;2d&quot;));
    // fill a rectangle whit sepecified fill style
    function fillRect(color, x, y, w, h) {
        ctx.fillStyle = color;
        ctx.fillRect(x || 0, y || 0, w || 150, h || 100);
        ctx.font = &quot;10pt Arial&quot;;
        ctx.fillStyle = &quot;white&quot;;
        ctx.fillText(color, 6, 20);
    }
    ctx.translate(50, 50);
    // 同中心，从绿色渐变到白色完全透明，并指定给填充色
    var gradient = ctx.createRadialGradient(50, 50, 0, 50, 50, 100);
    gradient.addColorStop(0, 'white');
    gradient.addColorStop(1, 'green');
    fillRect(gradient, -50, -50, 300, 200);
    // 第二次填充，矩形区域颜色加深
    fillRect(gradient);
&nbsp;
    ctx.translate(250, 0);
    // 不同中心，从绿色渐变到白色完全透明，并指定给填充色
    var gradient = ctx.createRadialGradient(0, 0, 10, 50, 50, 100);
    gradient.addColorStop(0, 'white');
    gradient.addColorStop(1, 'green');
    fillRect(gradient, -50, -50, 200, 200);
    // 第二次填充，矩形区域颜色加深
    fillRect(gradient);
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p><strong>小结</strong></p>

<p>总的来说，Canvas 中的颜色使用与使用 CSS 进行颜色定义没有什么区别，但比较强大的是 Canvas 支持渐变，这样就可以通过 Canvas 来做一些比较炫的效果。</p>

<p>这篇拖的太久了，已经有些忘了，先结束掉这部分再继续写了……</p>

<p><strong>参考资料</strong></p>

<ol>
    <li><a style="color: #0066cc; text-decoration: none;" rel="external" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element">The Canvas Element, WHATWG</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/canvas-starts-part3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 Canvas 起步(2) &#8211; 路径</title>
		<link>http://xujiwei.com/blog/canvas-starts-part2/</link>
		<comments>http://xujiwei.com/blog/canvas-starts-part2/#comments</comments>
		<pubDate>Tue, 05 May 2009 04:56:03 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[CanvasStarts]]></category>
		<category><![CDATA[HTML5]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=80</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/canvas-starts-part2/" title="HTML5 Canvas 起步(2) - 路径"></a>上一篇介绍了 HTML5 中 Canvas 的基本概念，这篇将要介绍一下 Canvas 中的基本图形。 图形的基础 - 路径 在 Canvas 中，所有基本图形都是以路径为基础的，也就是说，我们在调用 2dContext 的 lineTo、rect 等方法时，其实就是往已经的 context 路径集合中再添加一些路径点，在最后使用 fill 或 stroke 方法进行绘制时，都是依据这些路径点来进行填充或画线。 在每次开始绘制路径前，都应该使用 context.beginPath() 方法来告诉 Context 对象开始绘制一个新的路径，否则接下来绘制的路径会与之前绘制的路径叠加，在填充或画边框时就会出现问题。在绘制完成路径后，可以直接使用 context.closePath() 方法来关闭路径，或者手动关闭路径。另外，如果在填充时路径没有关闭，那么 Context 会自动调用 closePath 方法将路径关闭。 基本路径方法 1. beginPath, closePath 这两个方法在前面已经介绍过，分别用来通知 Context 开始一个新的路径和关闭当前的路径。 在 Canvas 中使用路径时，应该要保持一个良好的习惯，每次开始绘制路径前都要调用一次 beginPath 方法，否则画出来的效果难看不说，还会严重影响性能。 在下面这张图中，左边的图形在每次绘制矩形前都调用了一次 beginPath 来清除之前的路径并重新开始绘制新的路径，而后面的图形则就只在绘制所有图形前调用了一次 beginPath 来清除路径，因此，虽然这里是使用的边框色是 #666，但是右边的图形颜色比左边的深一些，因为每次使用 stroke 绘制边框时，会把之前的路径再次绘制一遍，叠加起来颜色就比原来深一些。 &#60;div id=&#34;_mcePaste&#34;&#62;&#38;lt;canvas id=&#34;canvas&#34; width=&#34;500&#34; height=&#34;500&#34;&#38;gt;&#38;lt;/canvas&#38;gt; &#38;lt;script type=&#34;text/javascript&#34;&#38;gt;     var canvas = document.getElementById(&#34;canvas&#34;);     var ctx = canvas.getContext(&#34;2d&#34;);     ctx.strokeStyle = &#34;#666&#34;;     function useBeginPath() {         for (var i = 0; i &#38;lt; 5; ++i) {             ctx.beginPath();             ctx.rect(10 + i*20, 10 + i*20, 210 - i*40, 210 - i*40);             ctx.stroke();         }     }     function notUseBeginPath() {         ctx.beginPath();         for (var i = 0; i &#38;lt; 5; ++i) {             ctx.rect(240 + i*20, 10 + i*20, 210 - i*40, 210 - i*40);             ctx.stroke();         }     }     useBeginPath();     notUseBeginPath(); &#38;lt;/script&#38;gt;&#60;/div&#62; 在 Context 中路径数较少时，如果不考虑显示效果，性能上还可以接受，但是如果 Context 中的路径数很多时，在开始绘制新路径前不使用 beginPath 的话，因为每次绘制都要将之前的路径重新绘制一遍，这时性能会以指数下降。 因此，除非有特殊需要，每次开始绘制路径前都要调用 &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/canvas-starts-part2/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/canvas-starts-part2/" title="HTML5 Canvas 起步(2) - 路径"></a><p><a href="http://www.xujiwei.com/blog/2009/04/20/canvas-starts-part1/">上一篇</a>介绍了 HTML5 中 Canvas 的基本概念，这篇将要介绍一下 Canvas 中的基本图形。</p>

<p><strong>图形的基础 - 路径</strong></p>

<p>在 Canvas 中，所有基本图形都是以路径为基础的，也就是说，我们在调用 2dContext 的 lineTo、rect 等方法时，其实就是往已经的 context 路径集合中再添加一些路径点，在最后使用 fill 或 stroke 方法进行绘制时，都是依据这些路径点来进行填充或画线。</p>

<p>在每次开始绘制路径前，都应该使用 context.beginPath() 方法来告诉 Context 对象开始绘制一个新的路径，否则接下来绘制的路径会与之前绘制的路径叠加，在填充或画边框时就会出现问题。在绘制完成路径后，可以直接使用 context.closePath() 方法来关闭路径，或者手动关闭路径。另外，如果在填充时路径没有关闭，那么 Context 会自动调用 closePath 方法将路径关闭。</p>

<p><strong>基本路径方法</strong></p>

<p><strong>1. beginPath, closePath</strong></p>

<p>这两个方法在前面已经介绍过，分别用来通知 Context 开始一个新的路径和关闭当前的路径。</p>

<p>在 Canvas 中使用路径时，应该要保持一个良好的习惯，每次开始绘制路径前都要调用一次 beginPath 方法，否则画出来的效果难看不说，还会严重影响性能。</p>

<p>在下面这张图中，左边的图形在每次绘制矩形前都调用了一次 beginPath 来清除之前的路径并重新开始绘制新的路径，而后面的图形则就只在绘制所有图形前调用了一次 beginPath 来清除路径，因此，虽然这里是使用的边框色是 #666，但是右边的图形颜色比左边的深一些，因为每次使用 stroke 绘制边框时，会把之前的路径再次绘制一遍，叠加起来颜色就比原来深一些。</p>

<p><a rel="external" href="http://picasaweb.google.com/lh/photo/eGvTDyt9pbvbUK6L5xpoEg?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img src="http://lh3.ggpht.com/_QJK_LPJoebg/Sf-nMSM-FGI/AAAAAAAAAnU/kd_YrZZObuo/s800/2-use-beginpath.jpg" alt="图片附件" /></a></p>


<div class="wp_syntax"><div class="code"><pre class="html5strict" style="font-family:monospace;">&lt;div id=&quot;_mcePaste&quot;&gt;&amp;lt;canvas id=&quot;canvas&quot; width=&quot;500&quot; height=&quot;500&quot;&amp;gt;&amp;lt;/canvas&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    ctx.strokeStyle = &quot;#666&quot;;
    function useBeginPath() {
        for (var i = 0; i &amp;lt; 5; ++i) {
            ctx.beginPath();
            ctx.rect(10 + i*20, 10 + i*20, 210 - i*40, 210 - i*40);
            ctx.stroke();
        }
    }
    function notUseBeginPath() {
        ctx.beginPath();
        for (var i = 0; i &amp;lt; 5; ++i) {
            ctx.rect(240 + i*20, 10 + i*20, 210 - i*40, 210 - i*40);
            ctx.stroke();
        }
    }
    useBeginPath();
    notUseBeginPath();
&amp;lt;/script&amp;gt;&lt;/div&gt;</pre></div></div>


<p>在 Context 中路径数较少时，如果不考虑显示效果，性能上还可以接受，但是如果 Context 中的路径数很多时，在开始绘制新路径前不使用 beginPath 的话，因为每次绘制都要将之前的路径重新绘制一遍，这时性能会以指数下降。</p>

<p>因此，除非有特殊需要，每次开始绘制路径前都要调用 beginPath 来开始新路径。<span id="more-80"></span></p>

<p><strong>2. 移动与直线 moveTo, lineTo, rect</strong></p>

<p><a style="color: #0066cc; text-decoration: none;" rel="external" href="http://picasaweb.google.com/lh/photo/MsYuilHYYBv_yXthtSUbNQ?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img style="border: 0px initial initial;" src="http://lh3.ggpht.com/_QJK_LPJoebg/Sf-tclWhJOI/AAAAAAAAAn0/UJMWF0V5gBA/s800/2-lineto-and-rect.jpg" alt="图片附件" /></a></p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>canvas id=&quot;canvas&quot; width=&quot;500&quot; height=&quot;500&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/canvas<span style="color: #ddbb00;">&amp;gt;</span>
<span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    ctx.beginPath();
    ctx.moveTo(10, 10);
    ctx.lineTo(110,110);
    ctx.lineTo(10, 110);
    ctx.lineTo(10, 10);
    ctx.stroke();
    ctx.beginPath();
    ctx.rect(120, 10, 100, 100);
    ctx.stroke();
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p><em>void moveTo(in float x, in float y);</em></p>

<p>在 Canvas 中绘制路径，一般是不需要指定起点的，默认的起点就是上一次绘制路径的终点，因此，如果需要指定起点的话，就需要使用 moveTo 方法来指定要移动到的位置。</p>

<p><em>void lineTo(in float x, in float y);</em></p>

<p>lineTo 方法则是绘制一条直接路径到指定的位置。在调用完 lineTo 方法后，Context 内部的绘制起点会移动到直线的终点。</p>

<p><em>void rect(in float x, in float y, in float w, in float h);</em></p>

<p>rect 方法用来绘制一个矩形路径，通过参数指定左上角位置以及宽和高。在调用 rect 后，Context 的绘制起点会移动到 rect 绘制的矩形的左上角。</p>

<p>rect 方法与后面要介绍的 arc 方法与其他路径方法有一点不同，它们是使用参数指定起点的，而不是使用 Context 内部维护的起点。</p>

<p><strong>3. 曲线 arcTo, arc, quadraticCurveTo, bezierCurveTo</strong></p>

<p><em>void arcTo(in float x1, in float y1, in float x2, in float y2, in float radius);</em></p>

<p>按照 WHATWG 文档的说明，这个方法是画一个与两条射线相切的的圆弧，两条射线其中一条为穿过 Context 绘制起点，终点为 (x1, y1)，另外一条为穿过 (x2, y2)，终点为 (x1, y1)，这条圆弧为最小的与这两条射线相切的圆弧。在调用完 arcTo 方法后，将 圆弧与 射线 (x1, y1)-(x2, y2) 的切点添加到当前路径中，做为下次绘制的起点。</p>

<p>在测试中发现，Firefox 和 Opera 目前对这个方法的支持并不好，只有 Chrome 和 Safari 4 能绘制出正确的路径。</p>

<p><span style="color: #333333; font-family: Corbel, 微软雅黑, Verdana, Arial, sans-serif; line-height: 21px; font-size: 14px;"><a style="color: #1f3a87; text-decoration: underline;" rel="external" href="http://picasaweb.google.com/lh/photo/dY3xi0DiDK7SqVKMAJt4DQ?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img style="border: 0px initial initial;" src="http://lh3.ggpht.com/_QJK_LPJoebg/Sf-1GoLlzMI/AAAAAAAAAn8/WUOHiqTwOCY/s800/2-arcto.jpg" alt="图片附件" /></a></span></p>

<p>图中的的两条灰色直线是偏移 4 个像素后的两条射线所在的位置。</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>canvas id=&quot;canvas&quot; width=&quot;500&quot; height=&quot;500&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/canvas<span style="color: #ddbb00;">&amp;gt;</span>
<span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    ctx.beginPath();
    ctx.strokeStyle = &quot;#000&quot;;
    ctx.translate(200, 200);
    ctx.moveTo(10, 10);
    ctx.arcTo(110, 60, 10, 110, 30);
    ctx.stroke();
&nbsp;
    ctx.beginPath();
    ctx.strokeStyle = &quot;#999&quot;;
    ctx.moveTo(10, 6);
    ctx.lineTo(114, 60);
    ctx.lineTo(10, 114);
    ctx.stroke();
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p><em>void arc(in float x, in float y, in float radius, in float startAngle, in float endAngle, in boolean anticlockwise);</em></p>

<p>arc 方法用来绘制一段圆弧路径，通过圆心位置、起始弧度、终止弧度来指定圆弧的位置和大小，这个方法也不依赖于 Context 维护的绘制起点。而在画圆弧时的旋转方向则由最后一个参数 anticlockwise 来指定，如果为 true 就是逆时针，false 则为顺时针。</p>

<p><em>void quadraticCurveTo(in float cpx, in float cpy, in float x, in float y);</em></p>

<p>quadraticCurveTo 方法用来绘制二次样条曲线路径，参数中 cpx 与 cpy 指定控制点的位置，x 和 y 指定终点的位置，起点则是由 Context 维护的绘制起点。</p>

<p><em>void bezierCurveTo(in float cp1x, in float cp1y, in float cp2x, in float cp2y, in float x, in float y);</em></p>

<p>bezierCurveTo 方法用来绘制贝塞尔曲线路径，它与 quadraticCurveTo 相似，不过贝塞尔曲线有两个控制点，因此参数中的 cp1x, cp1y, cp2x, cp2y 用来指定两个控制点的位置，而 x 和 y 指定绺的位置。</p>

<p><span style="color: #333333; font-family: Corbel, 微软雅黑, Verdana, Arial, sans-serif; line-height: 21px; font-size: 14px;"><a style="color: #1f3a87; text-decoration: underline;" rel="external" href="http://picasaweb.google.com/lh/photo/pZwPp7mDBZo2lAtZwUZ57g?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img style="border: 0px initial initial;" src="http://lh3.ggpht.com/_QJK_LPJoebg/Sf-6u7OYoaI/AAAAAAAAAoc/M_jVRyAwvHg/s800/2-arc-and-curve.jpg" alt="图片附件" /></a></span></p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>canvas id=&quot;canvas&quot; width=&quot;500&quot; height=&quot;500&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/canvas<span style="color: #ddbb00;">&amp;gt;</span>
<span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    ctx.translate(10, 10);
    ctx.beginPath();
    ctx.arc(50, 50, 50, 0, Math.PI, true);
    ctx.stroke();
    // quadraticCurveTo
    ctx.beginPath();
    ctx.strokeStyle = &quot;#000&quot;;
    ctx.moveTo(110, 50);
    ctx.quadraticCurveTo(160, 0, 210, 50);
    ctx.stroke();
    ctx.beginPath();
    ctx.strokeStyle = &quot;red&quot;;
    ctx.moveTo(110, 50);
    ctx.lineTo(160, 0);
    ctx.lineTo(210, 50);
    ctx.stroke();
    // bezierCurveTo
    ctx.beginPath();
    ctx.strokeStyle = &quot;#000&quot;;
    ctx.moveTo(220, 50);
    ctx.bezierCurveTo(250, 0, 280, 10, 320, 50);
    ctx.stroke();
    ctx.beginPath();
    ctx.strokeStyle = &quot;red&quot;;
    ctx.moveTo(220, 50);
    ctx.lineTo(250, 0);
    ctx.lineTo(280, 10);
    ctx.lineTo(320, 50);
    ctx.stroke();
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p><strong>4. fill, stroke, clip</strong></p>

<p>fill 与 stroke 这两个方法很好理解，分别用来填充路径与绘制路径线条。</p>

<p>clip 方法用来给 Canvas 设置一个剪辑区域，在调用 clip 方法之后的代码只对这个设定的剪辑区域有效，不会影响其他地方，这个方法在要进行局部更新时很有用。默认情况下，剪辑区域是一个左上角在 (0, 0)，宽和高分别等于 Canvas 元素的宽和高的矩形。</p>

<p><span style="color: #333333; font-family: Corbel, 微软雅黑, Verdana, Arial, sans-serif; line-height: 21px; font-size: 14px;"><a style="color: #0066cc; text-decoration: none;" rel="external" href="http://picasaweb.google.com/lh/photo/Y2-uHyM7J9eRhqXOUFUo3A?authkey=Gv1sRgCNOep9eIh-7m8QE&amp;feat=embedwebsite"><img style="border: 0px initial initial;" src="http://lh4.ggpht.com/_QJK_LPJoebg/Sf_CkLgc9II/AAAAAAAAAo8/Z83QRc4uBgA/s800/2-clip.jpg" alt="图片附件" /></a></span></p>

<p>在画这个图时，虽然两次都是使用 fillRect(0, 0, 100, 100) 填充了一个 100&#215;100 大小矩形，但是显示的结果却是第二次填充的只是中间的一小块，这是因为在两次填充之间使用 clip 方法设定了剪辑区域，这样第二次填充时只会影响到所设定的中间那一小部分区域。</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #ddbb00;">&amp;lt;</span>canvas id=&quot;canvas&quot; width=&quot;500&quot; height=&quot;500&quot;<span style="color: #ddbb00;">&amp;gt;&amp;lt;</span>/canvas<span style="color: #ddbb00;">&amp;gt;</span>
<span style="color: #ddbb00;">&amp;lt;</span>script type=&quot;text/javascript&quot;<span style="color: #ddbb00;">&amp;gt;</span>
    var canvas = document.getElementById(&quot;canvas&quot;);
    var ctx = canvas.getContext(&quot;2d&quot;);
    ctx.translate(10, 10);
    // fill a green rectangle
    ctx.fillStyle = &quot;green&quot;;
    ctx.fillRect(0, 0, 100, 100);
    // set the clipping region
    ctx.beginPath();
    ctx.rect(30, 30, 40, 40);
    ctx.clip();
    ctx.stroke();
    // fill a yellow rectangle
    ctx.fillStyle = &quot;yellow&quot;;
    ctx.fillRect(0, 0, 100, 100);
<span style="color: #ddbb00;">&amp;lt;</span>/script<span style="color: #ddbb00;">&amp;gt;</span></pre></div></div>


<p><strong>5. clearRect, fillRect, strokeRect</strong></p>

<p>这三个方法并不是路径方法，而是用来直接处理 Canvas 上的内容，相当于 Canvas 的背景，调用这三个方法也不会影响 Context 绘图的起点。</p>

<p>要清除 Canvas 上的所有内容时，可以直接调用 context.clearRect(0, 0, width, height) 来直接清除，而不需要使用路径方法绘制一个与 Canvas 同等大小的矩形路径再使用 fill 方法去清除。</p>

<p><strong>结语</strong></p>

<p>通过 Canvas 的路径方法，可以使用 Canvas 处理一些简单的矢量图形，这样在缩放时也不会失真。不过 Canvas 的路径方法也不是很强大，至少连个椭圆的路径都没有……</p>

<p>这篇写得有点长了，Cnavas 中路径相关的内容就写这么多，后面再讲讲 Canvas 其他的东西。</p>

<p><strong>参考资料</strong></p>

<ol>
    <li><a rel="external" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element">The Canvas Element, WHATWG</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/canvas-starts-part2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 Canvas 起步(1) &#8211; 基本概念</title>
		<link>http://xujiwei.com/blog/canvas-starts-part1/</link>
		<comments>http://xujiwei.com/blog/canvas-starts-part1/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 15:13:26 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[CanvasStarts]]></category>
		<category><![CDATA[HTML5]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=41</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/canvas-starts-part1/" title="HTML5 Canvas 起步(1) - 基本概念"></a>什么是Canvas &#60;canvas&#62; 是一个新的 HTML 元素，这个元素在 HTML5 中被定义。这个元素通常可以被用来在 HTML 页面中通过 JavaScript 进行绘制图形、合成图像等等操作，也可以用来做一些动画。当然，目前 HTML5 规范还在草稿阶段，正式发布也许要等到2010年，不过现在已经有不少浏览器已经支持了部分 HTML5 规范。目前支持 canvas 元素的浏览器有 Firefox 3+、Safari 4、Chrome 2.0+ 等，因此，在运行本页中的例子时，请确保你使用的是上述浏览器之一。 尽管在 Mozilla 已经有不少关于 Canvas 的教程，我还是决定把自己的学习过程记录下来。如果觉得我写的不够明白，那么你可以在参考资料中找到 Mozilla 网站上 Canvas 教程的链接。 另外，可以在这里找到一些有趣的 Canvas 示例。 开始使用 Canvas 使用 Canvas 很简单，与使用其他 HTML 元素一样，只需要在页面中添加一个 &#60;canvas&#62; 标签即可： &#60;canvas id=&#34;screen&#34; width=&#34;400&#34; height=&#34;400&#34;&#62;&#60;/canvas&#62; 当然，这样只是简单的创建了一个 Canvas 对象而已，并没有对它进行任何操作，这个时候的 canvas 元素看上去与 div 元素是没什么区别的，在页面上什么都看不出来：） 另外，canvas 元素的大小可以通过 width 与 height 属性来指定，这与 img 元素有点相似。 Canvas 的核心：Context 前面说到可以通过 JavaScript 来操作 Canvas 对象来进行绘制图形、合成图像等操作，这些操作并不是通过 Canvas 对象本身来进行的，而是通过 Canvas 对象的一个方法 getContext 获取 Canvas 操作上下文来进行。也就是说，在后面我们使用 Canvas 对象的过程中，都是与 Canvas 对象的 Context 打交道，而 Canvas 对象本身可以用来获取 Canvas 对象的大小等信息。 要获取 Canvas 对象的 Context 很简单，直接调用 canvas 元素的 getContext 方法即可，在调用的时候需要传递一个 Context 类型参数，目前可以用的并且是唯一可以用的类型值就是 2d： &#60;script type=&#34;text/javascript&#34;&#62;&#60;!--mce:0--&#62;&#60;/script&#62; Firefox 3.0.x 的尴尬 Firefox 3.0.x 虽然支持了 canvas 元素，但是并没有完全按照规范来实现，规范中的 fillText、measureText 两个方法在 Firefox 3.0.x 中被几个 Firefox 特有的方法代替，因此在 Firefox 3.0.x 中使用 Canvas 时需要先 fix 这个几个方法在不同浏览器中的差别。 下面这代码取自 Mozilla Bespin 项目，它修正了 Firefox 3.0.x 中 Canvas 的 Context 对象与 HTML5 规范不一致的地方： function fixContext&#40;ctx&#41; &#123;     // * upgrade Firefox 3.0.x text rendering to HTML 5 standard     if &#40;!ctx.fillText &#38;amp;&#38;amp; ctx.mozDrawText&#41; &#123;         ctx.fillText = function&#40;textToDraw, x, y, maxWidth&#41; &#123; &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/canvas-starts-part1/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/canvas-starts-part1/" title="HTML5 Canvas 起步(1) - 基本概念"></a><p><strong>什么是Canvas</strong></p>

<p>&lt;canvas&gt; 是一个新的 HTML 元素，这个元素在 <a rel="external" href="http://www.whatwg.org/specs/web-apps/current-work/">HTML5</a> 中被定义。这个元素通常可以被用来在 HTML 页面中通过 JavaScript 进行绘制图形、合成图像等等操作，也可以用来做一些动画。当然，目前 HTML5 规范还在草稿阶段，正式发布也许要等到2010年，不过现在已经有不少浏览器已经支持了部分 HTML5 规范。目前支持 canvas 元素的浏览器有 Firefox 3+、Safari 4、Chrome 2.0+ 等，因此，在运行本页中的例子时，请确保你使用的是上述浏览器之一。</p>

<p>尽管在 <a rel="external" href="http://www.mozilla.org/">Mozilla</a> 已经有不少关于 Canvas 的教程，我还是决定把自己的学习过程记录下来。如果觉得我写的不够明白，那么你可以在参考资料中找到 Mozilla 网站上 Canvas 教程的链接。</p>

<p>另外，可以在<a rel="external" href="http://hred.javaeye.com/blog/146568">这里</a>找到一些有趣的 Canvas 示例。</p>

<p><strong>开始使用 Canvas</strong></p>

<p>使用 Canvas 很简单，与使用其他 HTML 元素一样，只需要在页面中添加一个 &lt;canvas&gt; 标签即可：</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;canvas <span style="color: #000066;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;screen&quot;</span> <span style="color: #000066;">width</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;400&quot;</span> <span style="color: #000066;">height</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;400&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span>canvas&gt;</span></pre></div></div>


<p>当然，这样只是简单的创建了一个 Canvas 对象而已，并没有对它进行任何操作，这个时候的 canvas 元素看上去与 div 元素是没什么区别的，在页面上什么都看不出来：）</p>

<p>另外，canvas 元素的大小可以通过 width 与 height 属性来指定，这与 img 元素有点相似。</p>

<p><strong>Canvas 的核心：Context</strong></p>

<p>前面说到可以通过 JavaScript 来操作 Canvas 对象来进行绘制图形、合成图像等操作，这些操作并不是通过 Canvas 对象本身来进行的，而是通过 Canvas 对象的一个方法 getContext 获取 Canvas 操作上下文来进行。也就是说，在后面我们使用 Canvas 对象的过程中，都是与 Canvas 对象的 Context 打交道，而 Canvas 对象本身可以用来获取 Canvas 对象的大小等信息。</p>

<p>要获取 Canvas 对象的 Context 很简单，直接调用 canvas 元素的 getContext 方法即可，在调用的时候需要传递一个 Context 类型参数，目前可以用的并且是唯一可以用的类型值就是 2d：</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/javascript&quot;</span>&gt;</span><span style="color: #808080; font-style: italic;">&lt;!--mce:0--&gt;</span><span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span></pre></div></div>


<p><strong>Firefox 3.0.x 的尴尬</strong></p>

<p>Firefox 3.0.x 虽然支持了 canvas 元素，但是并没有完全按照规范来实现，规范中的 fillText、measureText 两个方法在 Firefox 3.0.x 中被几个 Firefox 特有的方法代替，因此在 Firefox 3.0.x 中使用 Canvas 时需要先 fix 这个几个方法在不同浏览器中的差别。</p>

<p><span id="more-41"></span></p>

<p>下面这代码取自 <a rel="external" href="http://labs.mozilla.com/projects/bespin/">Mozilla Bespin</a> 项目，它修正了 Firefox 3.0.x 中 Canvas 的 Context 对象与 HTML5 规范不一致的地方：</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> fixContext<span style="color: #009900;">&#40;</span>ctx<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">// * upgrade Firefox 3.0.x text rendering to HTML 5 standard</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>ctx.<span style="color: #660066;">fillText</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> ctx.<span style="color: #660066;">mozDrawText</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        ctx.<span style="color: #660066;">fillText</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>textToDraw<span style="color: #339933;">,</span> x<span style="color: #339933;">,</span> y<span style="color: #339933;">,</span> maxWidth<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            ctx.<span style="color: #660066;">translate</span><span style="color: #009900;">&#40;</span>x<span style="color: #339933;">,</span> y<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            ctx.<span style="color: #660066;">mozTextStyle</span> <span style="color: #339933;">=</span> ctx.<span style="color: #660066;">font</span><span style="color: #339933;">;</span>
            ctx.<span style="color: #660066;">mozDrawText</span><span style="color: #009900;">&#40;</span>textToDraw<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            ctx.<span style="color: #660066;">translate</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>x<span style="color: #339933;">,</span> <span style="color: #339933;">-</span>y<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #006600; font-style: italic;">// * Setup measureText</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>ctx.<span style="color: #660066;">measureText</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> ctx.<span style="color: #660066;">mozMeasureText</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        ctx.<span style="color: #660066;">measureText</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>text<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>ctx.<span style="color: #660066;">font</span><span style="color: #009900;">&#41;</span> ctx.<span style="color: #660066;">mozTextStyle</span> <span style="color: #339933;">=</span> ctx.<span style="color: #660066;">font</span><span style="color: #339933;">;</span>
            <span style="color: #003366; font-weight: bold;">var</span> width <span style="color: #339933;">=</span> ctx.<span style="color: #660066;">mozMeasureText</span><span style="color: #009900;">&#40;</span>text<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #009900;">&#123;</span> width<span style="color: #339933;">:</span> width <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #006600; font-style: italic;">// * Setup html5MeasureText</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>ctx.<span style="color: #660066;">measureText</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #339933;">!</span>ctx.<span style="color: #660066;">html5MeasureText</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        ctx.<span style="color: #660066;">html5MeasureText</span> <span style="color: #339933;">=</span> ctx.<span style="color: #660066;">measureText</span><span style="color: #339933;">;</span>
        ctx.<span style="color: #660066;">measureText</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>text<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #003366; font-weight: bold;">var</span> textMetrics <span style="color: #339933;">=</span> ctx.<span style="color: #660066;">html5MeasureText</span><span style="color: #009900;">&#40;</span>text<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #006600; font-style: italic;">// fake it 'til you make it</span>
            textMetrics.<span style="color: #660066;">ascent</span> <span style="color: #339933;">=</span> ctx.<span style="color: #660066;">html5MeasureText</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;m&quot;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">width</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">return</span> textMetrics<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #006600; font-style: italic;">// * for other browsers, no-op away</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>ctx.<span style="color: #660066;">fillText</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        ctx.<span style="color: #660066;">fillText</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>ctx.<span style="color: #660066;">measureText</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        ctx.<span style="color: #660066;">measureText</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #CC0000;">10</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000066; font-weight: bold;">return</span> ctx<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>


<p><em>注意：到 Opera 9.5 为止，Opera 还不支持 HTML5 规范中 Canvas 对象的 fillText 以及其相关方法和属性。</em></p>

<p><strong>Hello, Canvas!</strong></p>

<p>在对 Canvas 进行了一些初步了解后，开始来写我们的第一个 Canvas 程序，闻名的 HelloWorld 的又一个分支“Hello, Canvas”：</p>


<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;canvas  <span style="color: #000066;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;screen&quot;</span> <span style="color: #000066;">width</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;400&quot;</span> <span style="color: #000066;">height</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;400&quot;</span>&gt;&lt;<span style="color: #66cc66;">/</span>canvas&gt;</span>
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">script</span>  <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/javascript&quot;</span>&gt;</span>
(function() {
    var canvas = document.getElementById(&quot;screen&quot;);
    var ctx = fixContext(canvas.getContext(&quot;2d&quot;));
    ctx.font = &quot;20pt Arial&quot;;
    ctx.fillText(&quot;Hello, Canvas!&quot;, 20, 20);
    ctx.fillText(&quot;www.xujiwei.com&quot;, 20, 50);
    function fixContext(ctx) {
        // * upgrade Firefox 3.0.x text rendering to HTML 5 standard
        if (!ctx.fillText <span style="color: #ddbb00;">&amp;&amp; ctx.mozDrawText) {</span>
<span style="color: #ddbb00;">            ctx.fillText = function(textToDraw, x, y, maxWidth) {</span>
<span style="color: #ddbb00;">                ctx.translate(x, y);</span>
                ctx.mozTextStyle = ctx.font;
                ctx.mozDrawText(textToDraw);
                ctx.translate(-x, -y);
            };
        }
        // * Setup measureText
        if (!ctx.measureText <span style="color: #ddbb00;">&amp;&amp; ctx.mozMeasureText) {</span>
<span style="color: #ddbb00;">            ctx.measureText = function(text) {</span>
<span style="color: #ddbb00;">                if (ctx.font) ctx.mozTextStyle = ctx.font;</span>
                var width = ctx.mozMeasureText(text);
                return { width: width };
            };
        }
        // * Setup html5MeasureText
        if (ctx.measureText <span style="color: #ddbb00;">&amp;&amp; !ctx.html5MeasureText) {</span>
<span style="color: #ddbb00;">            ctx.html5MeasureText = ctx.measureText;</span>
            ctx.measureText = function(text) {
                var textMetrics = ctx.html5MeasureText(text);
                // fake it 'til you make it
                textMetrics.ascent = ctx.html5MeasureText(&quot;m&quot;).width;
                return textMetrics;
            };
        }
        // * for other browsers, no-op away
        if (!ctx.fillText) {
            ctx.fillText = function() {};
        }
        if (!ctx.measureText) {
            ctx.measureText = function() { return 10; };
        }
        return ctx;
    }
})();
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">script</span>&gt;</span></pre></div></div>


<p>运行示例，Canvas 对象所在区域显示出“Hello, World!”，这正是代码中 ctx.fillText(&#8220;Hello, World!&#8221;, 20, 20); 的作用。</p>

<p><strong>fillText 以及相关属性</strong></p>

<p>fillText 方法用来在 Canvas 中显示文字，它可以接受四个参数，其中最后一个是可选的：</p>

<p>void fillText(in DOMString text, in float x, in float y, [Optional] in float maxWidth);</p>

<p>其中 maxWidth 表示显示文字时最大的宽度，可以防止文字溢出，不过我在测试中发现在 Firefox 与 Chomre 中指定了 maxWidth 时也没有任何效果。</p>

<p>在使用 fillText 方法之前，可以通过设置 Context 的 font 属性来调整显示文字的字体，在上面的示例中我使用了“20pt Arial”来作为显示文字的字体，你可以自己设置不同的值来看具体的效果。</p>

<p><strong>结束</strong></p>

<p>暂时就到这里了，我会一边看规范一边写这个系列：）</p>

<p><strong>参考资料</strong></p>

<ol>
    <li><a rel="external" href="http://hred.javaeye.com/blog/146568">HTML5的Canvas，脚本语言的新舞台, hred</a></li>
    <li><a rel="external" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element">The Canvas Element, WHATWG</a></li>
    <li><a rel="external" href="https://developer.mozilla.org/cn/Canvas_tutorial">Canvas Tutorial 中文, Mozilla</a></li>
    <li><a rel="external" href="https://developer.mozilla.org/en/Canvas_tutorial">Canvas Tutorial 英文, Mozilla</a></li>
    <li><a rel="external" href="http://www.opera.com/docs/specs/opera95/canvas/">Canvas support in Opera, Opera</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/canvas-starts-part1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>“云端的编辑器”，10 步安装 Bespin Development Server（Python版）</title>
		<link>http://xujiwei.com/blog/setup-bespin-server-python-edition/</link>
		<comments>http://xujiwei.com/blog/setup-bespin-server-python-edition/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 06:33:53 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[Bespin]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[Editor]]></category>
		<category><![CDATA[Mozilla]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=52</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/setup-bespin-server-python-edition/" title="“云端的编辑器”，10 步安装 Bespin Development Server（Python版）"></a>Mozilla Labs 在今年情人节那天发布了一个叫 Bespin 的编辑器，这是一个基于网络的可扩展文本编辑器，按照现在流行的说法，就是“云编辑”了。并且，这个编辑器是开源的。 Bespin 是基于 Canvas 的，因此目前它只支持少数浏览器，其中包括 Firefox 3+、Safari 4 以及 Chrome 2 开发版，因此，如果要使用 Bespin 的话，你必须使用这几款浏览器中的一种。 要体验 Bespin，可以直接在 bespin.mozilla.com 注册一个帐号并且登录，不过目前 bespin.mozilla.com 所用的代码并不是最新版本的，线上使用的代码有许多不完善之处。因此，除了使用 Mozilla Labs 官方的 Bespin 站点，我们也可以从 Mozilla Labs 下载 Bespin 源代码，并在本地搭建 Bespin 服务，从而可以体验 Mozilla Labs 最新的开发成果。 注意，这篇文章并不介绍怎么去使用 Bespin，而是介绍怎么样去配置一个可以在本地运行的 Bespin 服务器，因此，如果你需要了解怎么去使用这个编辑器，可以参阅 Mozilla Labs 上的文档，或者等我再写一篇使用 Bespin 的文章：） 准备工作 首先需要说明的是，这里介绍的配置 Bespin 本地服务器的环境是 Windows Vista(or WinXP) + Python。另外，如果是在 Vista 中安装 Bespin Server，你使用管理员权限来运行 cmd。 第 1 步：在这里把 Bespin 的源代码下载下来，然后解压到任意目录，我这里解压到的是“D:\Source\Bespin\bespin-8b89188c5066”。 第 2 步：这篇文章介绍的是 Python 版 Bespin 服务器的配置，因此还需要安装 Python 解释器，可以在这里下载到 Python 2.5.4，使用默认设置安装即可。 第 3 步：在 bespin 源代码目录中新建一个目录 Scripts，这个目录与 backend 和 frontend 两个目录是平级的。然后到 Python 的安装目录下，将安装目录下的 msvcr71.dll、python.exe、python25.dll、pythonw.exe 四个文件拷贝到前面建立的 Script 目录中。 第 4 步：将 Python 安装目录中的 libs 目录拷贝到 bespin 源代码目录中。 第 5 步：Bespin Python 服务器所用的有些组件是使用 C 写的，在 Windows 上要编译与 Python 兼容的 C 扩展，需要使用 MinGW，下载以下这些压缩包，解压到 D:\Tools\MinGW，这里解压的位置可以自己选，但是后面要用到，所以请记住你解压的路径： http://nchc.dl.sourceforge.net/sourceforge/mingw/binutils-2.19.1-mingw32-bin.tar.gz http://nchc.dl.sourceforge.net/sourceforge/mingw/gcc-g++-3.4.5-20060117-3.tar.gz http://nchc.dl.sourceforge.net/sourceforge/mingw/gcc-core-3.4.5-20060117-3.tar.gz http://nchc.dl.sourceforge.net/sourceforge/mingw/w32api-3.13-mingw32-dev.tar.gz http://nchc.dl.sourceforge.net/sourceforge/mingw/mingwrt-3.15.2-mingw32-dev.tar.gz 开始安装 第 1 步：打开 cmd，进入 bespin 源代码目录，运行： bootstrap.py &#8211;no-site-packages 这个命令会下载必须的模块源代码，并且进行安装。在运行到最后时会报一个错： IOError: [Errno 2] No such file or directory: &#8216;/d:\\source\\bespin\\bespin-8b89188c5066\\ext\\paste-1.7.3dev-r7791.tar.gz&#8217; 这时需要用文本编辑器打开 Bespin 源代码目录中的“Lib\site-packages\pip-0.3.1-py25.egg\pip.py”，注意，这里所用的文本编辑器必须支持 UNIX &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/setup-bespin-server-python-edition/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/setup-bespin-server-python-edition/" title="“云端的编辑器”，10 步安装 Bespin Development Server（Python版）"></a><p>Mozilla Labs 在<a rel="external" href="http://labs.mozilla.com/2009/02/introducing-bespin/">今年情人节那天发布了一个叫 Bespin 的编辑器</a>，这是一个基于网络的可扩展文本编辑器，按照现在流行的说法，就是“云编辑”了。并且，这个编辑器是开源的。</p>

<p><a rel="external" href="http://labs.mozilla.com/uploads/2009/02/webkit-editor-medium.png"><img src="http://labs.mozilla.com/uploads/2009/02/webkit-editor-medium.png" alt="图片附件" /></a></p>

<p>Bespin 是基于 Canvas 的，因此目前它只支持少数浏览器，其中包括 Firefox 3+、Safari 4 以及 Chrome 2 开发版，因此，如果要使用 Bespin 的话，你必须使用这几款浏览器中的一种。</p>

<p>要体验 Bespin，可以直接在 <a rel="external" href="http://bespin.mozilla.com/">bespin.mozilla.com</a> 注册一个帐号并且登录，不过目前 bespin.mozilla.com 所用的代码并不是最新版本的，线上使用的代码有许多不完善之处。因此，除了使用 Mozilla Labs 官方的 Bespin 站点，我们也可以从 Mozilla Labs 下载 Bespin 源代码，并在本地搭建 Bespin 服务，从而可以体验 Mozilla Labs 最新的开发成果。</p>

<p>注意，这篇文章并不介绍怎么去使用 Bespin，而是介绍怎么样去配置一个可以在本地运行的 Bespin 服务器，因此，如果你需要了解怎么去使用这个编辑器，可以参阅 <a rel="external" href="https://wiki.mozilla.org/Labs/Bespin">Mozilla Labs 上的文档</a>，或者等我再写一篇使用 Bespin 的文章：）</p>

<p><strong>准备工作</strong></p>

<p>首先需要说明的是，这里介绍的配置 Bespin 本地服务器的环境是 Windows Vista(or WinXP) + Python。另外，如果是在 Vista 中安装 Bespin Server，你使用管理员权限来运行 cmd。</p>

<p>第 1 步：在<a rel="external" href="http://hg.mozilla.org/labs/bespin/">这里</a>把 Bespin 的源代码下载下来，然后解压到任意目录，我这里解压到的是“D:\Source\Bespin\bespin-8b89188c5066”。</p>

<p>第 2 步：这篇文章介绍的是 Python 版 Bespin 服务器的配置，因此还需要安装 Python 解释器，可以在<a rel="external" href="http://www.python.org/download/releases/2.5.4/">这里</a>下载到 Python 2.5.4，使用默认设置安装即可。</p>

<p>第 3 步：在 bespin 源代码目录中新建一个目录 Scripts，这个目录与 backend 和 frontend 两个目录是平级的。然后到 Python 的安装目录下，将安装目录下的 msvcr71.dll、python.exe、python25.dll、pythonw.exe 四个文件拷贝到前面建立的 Script 目录中。</p>

<p>第 4 步：将 Python 安装目录中的 libs 目录拷贝到 bespin 源代码目录中。</p>

<p>第 5 步：Bespin Python 服务器所用的有些组件是使用 C 写的，在 Windows 上要编译与 Python 兼容的 C 扩展，需要使用 MinGW，下载以下这些压缩包，解压到 D:\Tools\MinGW，这里解压的位置可以自己选，但是后面要用到，所以请记住你解压的路径：</p>

<blockquote>http://nchc.dl.sourceforge.net/sourceforge/mingw/binutils-2.19.1-mingw32-bin.tar.gz

http://nchc.dl.sourceforge.net/sourceforge/mingw/gcc-g++-3.4.5-20060117-3.tar.gz


http://nchc.dl.sourceforge.net/sourceforge/mingw/gcc-core-3.4.5-20060117-3.tar.gz


http://nchc.dl.sourceforge.net/sourceforge/mingw/w32api-3.13-mingw32-dev.tar.gz


http://nchc.dl.sourceforge.net/sourceforge/mingw/mingwrt-3.15.2-mingw32-dev.tar.gz</blockquote>

<p><span id="more-52"></span></p>

<p><strong>开始安装</strong></p>

<p>第 1 步：打开 cmd，进入 bespin 源代码目录，运行：</p>

<p><em>bootstrap.py &#8211;no-site-packages</em></p>

<p>这个命令会下载必须的模块源代码，并且进行安装。在运行到最后时会报一个错：</p>

<blockquote>IOError: [Errno 2] No such file or directory: &#8216;/d:\\source\\bespin\\bespin-8b89188c5066\\ext\\paste-1.7.3dev-r7791.tar.gz&#8217;</blockquote>

<p>这时需要用文本编辑器打开 Bespin 源代码目录中的“Lib\site-packages\pip-0.3.1-py25.egg\pip.py”，注意，这里所用的文本编辑器必须支持 UNIX 换行模式，也就意味着不要使用 Windows 记事本来编辑 pip.py，而是使用诸如 EditPlus、UltraEdit 等编辑器来编辑。</p>

<p>打开 pip.py 后使用搜索功能定位到 unpack_file 方法，搜索“def unpack_file”即可。找到这个方法后，在方法定义的下一行添加下面这样一句代码：</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">filename = filename.<span style="color: black;">lstrip</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span></pre></div></div>


<p>第 2 步：再在 pip.py 搜索“Running setup.py install for %s”定位到模块安装代码：</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">   <span style="color: #ff7700;font-weight:bold;">try</span>:
            call_subprocess<span style="color: black;">&#40;</span>
                <span style="color: black;">&#91;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">executable</span>, <span style="color: #483d8b;">'-c'</span>,
                 <span style="color: #483d8b;">&quot;import setuptools; __file__=%r; execfile(%r)&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">setup_py</span>, <span style="color: #008000;">self</span>.<span style="color: black;">setup_py</span><span style="color: black;">&#41;</span>,
                 <span style="color: #483d8b;">'install'</span>, <span style="color: #483d8b;">'--single-version-externally-managed'</span>, <span style="color: #483d8b;">'--record'</span>, record_filename,
                 <span style="color: #483d8b;">'--install-headers'</span>, header_dir<span style="color: black;">&#93;</span> + install_options,
                cwd=<span style="color: #008000;">self</span>.<span style="color: black;">source_dir</span>, filter_stdout=<span style="color: #008000;">self</span>._filter_install, show_stdout=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">finally</span>:
            logger.<span style="color: black;">indent</span> -= <span style="color: #ff4500;">2</span></pre></div></div>


<p>在 try 的后面添加下面 CODE TO ADD START 与 CODE TO ADD END 之间的代码，用来设置默认编译器为 mingw32：</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">     <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #808080; font-style: italic;">## CODE TO ADD START ##</span>
        call_subprocess<span style="color: black;">&#40;</span>
            <span style="color: black;">&#91;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">executable</span>, <span style="color: #483d8b;">'-c'</span>,
             <span style="color: #483d8b;">&quot;import setuptools; __file__=%r; execfile(%r)&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">setup_py</span>, <span style="color: #008000;">self</span>.<span style="color: black;">setup_py</span><span style="color: black;">&#41;</span>,
             <span style="color: #483d8b;">'setopt'</span>, <span style="color: #483d8b;">'--command=build'</span>, <span style="color: #483d8b;">'--option=compiler'</span>, <span style="color: #483d8b;">'--set-value=mingw32'</span><span style="color: black;">&#93;</span>,
            cwd=<span style="color: #008000;">self</span>.<span style="color: black;">source_dir</span>, filter_stdout=<span style="color: #008000;">self</span>._filter_install, show_stdout=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;">## CODE TO ADD END ##</span>
            call_subprocess<span style="color: black;">&#40;</span>
                <span style="color: black;">&#91;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">executable</span>, <span style="color: #483d8b;">'-c'</span>,
                 <span style="color: #483d8b;">&quot;import setuptools; __file__=%r; execfile(%r)&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">setup_py</span>, <span style="color: #008000;">self</span>.<span style="color: black;">setup_py</span><span style="color: black;">&#41;</span>,
                 <span style="color: #483d8b;">'install'</span>, <span style="color: #483d8b;">'--single-version-externally-managed'</span>, <span style="color: #483d8b;">'--record'</span>, record_filename,
                 <span style="color: #483d8b;">'--install-headers'</span>, header_dir<span style="color: black;">&#93;</span> + install_options,
                cwd=<span style="color: #008000;">self</span>.<span style="color: black;">source_dir</span>, filter_stdout=<span style="color: #008000;">self</span>._filter_install, show_stdout=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">finally</span>:
            logger.<span style="color: black;">indent</span> -= <span style="color: #ff4500;">2</span></pre></div></div>


<p>在修改的时候注意缩进，因为 Python 就靠缩进来判断代码结构了。修改完成后保存 pip.py。</p>

<p>第 3 步：回到 cmd 中，先设置一下环境变量，将 MniGW 的路径添加到 PATH 环境变量中，注意，这里的路径要与你解压 MinGW 时的路径一致：</p>

<p><em>set path=D:\Tools\MinGW\bin;%PATH%</em></p>

<p>然后再次运行：</p>

<p><em>bootstrap.py &#8211;no-site-packages</em></p>

<p>这次就可以正确安装了，最后会提示安装完成：</p>

<blockquote>Installed d:\source\bespin\bespin-8b89188c5066\backend\python
Processing dependencies for BespinServer==tip
Finished processing dependencies for BespinServer==tip</blockquote>

<p>第 4 步：在 cmd 中运行 Scripts\activate.bat，这个命令会设置当前的环境变量。</p>

<p>第 5 步：在 cmd 中运行</p>

<p><em>paver dojo create_db</em></p>

<p>这个命令会安装 dojo 库，然后运行：</p>

<p><em>paver start</em></p>

<p>cmd 中会提示：</p>

<blockquote>&#8212;> pavement.start
serving on http://127.0.0.1:8080</blockquote>

<p>恭喜，Bespin Server 已经成功启动！</p>

<p><strong>体验 Bespin</strong></p>

<p>打开浏览器，在地址栏中输入 http://localhost:8080/ 马上开始体验 Bespin：）</p>

<p>by Xu Jiwei</p>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/setup-bespin-server-python-edition/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>YUI学习笔记（4）</title>
		<link>http://xujiwei.com/blog/yui-study-notes-4/</link>
		<comments>http://xujiwei.com/blog/yui-study-notes-4/#comments</comments>
		<pubDate>Sat, 10 Jan 2009 15:50:52 +0000</pubDate>
		<dc:creator>Xu Jiwei</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[YUI]]></category>

		<guid isPermaLink="false">http://tmp.xujiwei.com/blog/?p=145</guid>
		<description><![CDATA[<a href="http://xujiwei.com/blog/yui-study-notes-4/" title="YUI学习笔记（4）"></a>YUI学习笔记（4） by xujiwei (http://www.xujiwei.com/) YAHOO.util.Subscriber 与 YAHOO.util.CustomEvent。 1. YAHOO.util.Subscriber (event.js) 这 应该算是设计模式中的观察者模式了，Subscriber 订阅一个事件，在 Publisher 触发那个事件后，会逐个通知 Subscriber。 对 于一般开发者来说，并不需要去关心 Subscriber 的实现，因为 Subscriber 主要是 CustomEvent 用来分发动作以及删 除 Subscriber 的。 Subscriber 类只定义了 3 个属性：fn、obj 以及 override，3 个方 法：getScope、contains、toString，其中 fn 为订阅者的回调函数，obj 为要传递给回调函数的一个额外参 数，override 如果是布尔型的 true 值，那么表示使用 obj 属性为回调函数执行时的上下文，或者直接使用一个对象来作为回调函数执行的 上下文。 Subscriber 的 3 个方法中比较有用的是 getScope 和 contains，toString 只是简单的 将 Subscriber 对象转换成一个字符串。getScope 会根据 Subscriber 对象的 override 属性来获取回调函数执行 的上下文，contains 用来判断 Subscriber 对象与指定的回调函数和 obj 是否一致。 2. YAHOO.util.CustomEvent (event.js) CustomEvent 的 作用相当在观察者模式中发布者的身份，可以通过它来实现一个自己的事件发布者。 CustomEvent 构造函数的定义如下： CustomEvent = function(type, oScope, silent, signature) 在 创建 CustomEvent 对象时，几个参数的用途如下： type 是自定义事件的名称，在使用回调函数的参数格式 为 YAHOO.util.Event.LIST 时，回调函数的第一个参数就是 CustomEvent 对象的名称； oScope 是 执行回调函数时的上下文对象，也就是在回调函数中可以用 this 来引用这个对象； silent 参数是用指示是否 在 YUI 为 debug 版本时禁用调试信息； signature 用来指示回调函数参数的格式，可以为 YAHOO.util.Event.FLAT 或 YAHOO.util.Event.LIST， 默认是 YAHOO.util.Event.LIST。 在使用 CustomEvent 之前，先要了解一下 CustomEvent 中 回调函数参数的格式，CustomEvent 的回调函数可以有两种格式，一种为 YAHOO.util.Event.LIST，这种格式的回调函数具有 三个参数，分别是事件名称、参数数组和附加对象参数；另外一种回调函数参数格式为 YAHOO.util.Event.FLAT，这个时候回调函数只有两 个参数，一个为 CustomEvent 对象调用 fire 方法时的第一个参数，另外一个是订阅时的额外对象参数。 在创 建 CustomEvent 对象时，CustomEvent 构造函数还会首先创建一个内部的自定义事件，用来处理该自定义事件被订阅的事件，这 在 EventProvider 中用到，这里暂且不提。 CustomEvent 对象使用一个名为 subscribers 的数组来保 &#8230;<p class="read-more"><a href="http://xujiwei.com/blog/yui-study-notes-4/">Read more &#187;</a>]]></description>
			<content:encoded><![CDATA[<a href="http://xujiwei.com/blog/yui-study-notes-4/" title="YUI学习笔记（4）"></a><p><strong>YUI学习笔记（4）</strong></p>

<p>by xujiwei (<a rel="external" href="http://www.xujiwei.com/">http://www.xujiwei.com/</a>)</p>

<p>YAHOO.util.Subscriber 与 YAHOO.util.CustomEvent。</p>

<p><strong>1. YAHOO.util.Subscriber (event.js)</strong></p>

<p>这 应该算是设计模式中的观察者模式了，Subscriber 订阅一个事件，在 Publisher 触发那个事件后，会逐个通知 Subscriber。</p>

<p>对 于一般开发者来说，并不需要去关心 Subscriber 的实现，因为 Subscriber 主要是 CustomEvent 用来分发动作以及删 除 Subscriber 的。</p>

<p>Subscriber 类只定义了 3 个属性：fn、obj 以及 override，3 个方 法：getScope、contains、toString，其中 fn 为订阅者的回调函数，obj 为要传递给回调函数的一个额外参 数，override 如果是布尔型的 true 值，那么表示使用 obj 属性为回调函数执行时的上下文，或者直接使用一个对象来作为回调函数执行的 上下文。</p>

<p>Subscriber 的 3 个方法中比较有用的是 getScope 和 contains，toString 只是简单的 将 Subscriber 对象转换成一个字符串。getScope 会根据 Subscriber 对象的 override 属性来获取回调函数执行 的上下文，contains 用来判断 Subscriber 对象与指定的回调函数和 obj 是否一致。</p>

<p><strong>2. YAHOO.util.CustomEvent (event.js)</strong></p>

<p>CustomEvent 的 作用相当在观察者模式中发布者的身份，可以通过它来实现一个自己的事件发布者。</p>

<p>CustomEvent 构造函数的定义如下：</p>

<p><em>CustomEvent = function(type, oScope, silent, signature)</em></p>

<p>在 创建 CustomEvent 对象时，几个参数的用途如下：</p>

<p>type 是自定义事件的名称，在使用回调函数的参数格式 为 YAHOO.util.Event.LIST 时，回调函数的第一个参数就是 CustomEvent 对象的名称；</p>

<p>oScope 是 执行回调函数时的上下文对象，也就是在回调函数中可以用 this 来引用这个对象；</p>

<p>silent 参数是用指示是否 在 YUI 为 debug 版本时禁用调试信息；</p>

<p>signature 用来指示回调函数参数的格式，可以为 <em>YAHOO.util.Event.FLAT</em> 或 <em>YAHOO.util.Event.LIST</em>， 默认是 YAHOO.util.Event.LIST。</p>

<p>在使用 CustomEvent 之前，先要了解一下 CustomEvent 中 回调函数参数的格式，CustomEvent 的回调函数可以有两种格式，一种为 YAHOO.util.Event.LIST，这种格式的回调函数具有 三个参数，分别是事件名称、参数数组和附加对象参数；另外一种回调函数参数格式为 YAHOO.util.Event.FLAT，这个时候回调函数只有两 个参数，一个为 CustomEvent 对象调用 fire 方法时的第一个参数，另外一个是订阅时的额外对象参数。</p>

<p>在创 建 CustomEvent 对象时，CustomEvent 构造函数还会首先创建一个内部的自定义事件，用来处理该自定义事件被订阅的事件，这 在 EventProvider 中用到，这里暂且不提。</p>

<p>CustomEvent 对象使用一个名为 subscribers 的数组来保 存所有订阅者的列表，并且通过维护这个列表来维护该自定义事件的订阅者。</p>

<p>CustomEvent 对象提供了 subscribe、 unsubscribe、unsubscribeAll、fire 这几个方法来处理自定义事件的订阅、退订以及触发等动作，而这几个就是观察者模式中的 主要动作了。</p>

<p>subscribe 的签名为 <em>subscribe: function(fn, obj, override)</em>， 三个参数分别对应了 Subscriber 类构造函数的三个参数，分别对应了回调函数、额外对象参数以及是否使用额外对象参数作为执行上下文。 subscribe 只是简单的判断参数 fn 是否有定义，然后会触发自定义事件订阅事件，最后使用这三个参数创建一个 Subscriber 对象添 加到 CustomEvent 对象的 subscribers 属性中。</p>

<p>unsubscribe 方法用来取消事件的订阅，它的函数签名 为 <em>unsubscribe: function(fn, obj)</em>，两个参数分别是回调函数和额外对象参数，如果使用无参数调 用 unsubscribe 方法，那么会直接调用 unsubscribeAll 来删除所有订阅者，否则会逐一判断 subscribers 中的每 个对象，通过使用 Subscriber 对象的 contains 方法来判断给定的 fn 和 obj 与其是否一致，如果一致，就使用一个私有方 法 _delete 来删除这个 Subscriber。</p>

<p>unsubscribeAll 方法没有参数，它只是简单的直接删除自定义事件的 所有订阅者，最后直接给 subscribers 赋值一个空数组来避免有可能出现漏删订阅者的情况。</p>

<p>内部方法 _delete 的参数 是 Subscriber 对象在 subscribers 数组中的索引，它会先删除 Subscriber 对象的 fn 和 obj 属性，最后使 用 splice 方法将 Subscriber 对象从数组中删除。</p>

<blockquote>
<div id="CODE_7643"><span style="font-style: normal;">var s = this.subscribers[index];
if (s) {
delete s.fn;
delete s.obj;
}
this.subscribers.splice(index, 1);</span></div></blockquote>

<p>使用 delete 删除 Subscriber 对象的 fn 和 obj 属性是为了去除 回调函数及额外对象参数的引用，以免引起不必要的内 存泄露。</p>

<p>CustomEvent 对象最重要的方法就是 fire 了，就是通过这个方法来通知所有了订阅者这个自定义事件被触发了。 fire 方法先使用 Array 的 slice 方法将调用 fire 方法时的参数转化成数组，这样就可以在调用 Subscriber 的回调函 数时可以传递参数给它们。</p>

<p>在遍历 subscribers 中的 Subscriber 前，fire 方法先使用 了 subscribers 的 slice 方法来创建一个 subscribers 的副本，这样避免在执行 fire 的过程中 有 Subscirber 取消订阅了这个自定义事件会导致错误。</p>

<p>在执行 Subscriber 的回调函数前，先使 用 Subscriber 对象的 getScope 方法来获取执行回调函数时的上下文对象，再根据 CustomEvent 对象 的 signature 属性来决定怎么去调用 Subscriber 的回调函数。</p>

<p>如 果 signature 为 YAHOO.util.CustomEvent.FLAT，那么就把调用 fire 方法时的第一个参数做为回调函数的第一 个参数，再把 Subscriber 对象的 obj 属性做为第二个参数：</p>

<p><em>s.fn.call(scope, param, s.obj)</em></p>

<p>如 果 signature 为 YAHOO.util.CustomEvent.LIST，那么就除了把整个 fire 方法的参数列表传递给回调函数外， 还要传递当前 CustomEvent 的名称给回调函数：</p>

<p><em>s.fn.call(scope, this.type, args, s.obj)</em></p>

<p>Subscriber 的 回调函数如果在执行过程中出现了错误，那么 CustomEvent 的 lastError 属性就是指向错误对象的引用，另外，如 果 YAOO.util.Event.throwErrors 为 true，那么会把这个错误再次抛出。</p>

<p>另外，Subscriber 对 象也可以控制事件通知是否继续，如果 Subscriber 对象的回调函数执行后的返回一个 false，那么在 fire 方法中就会停止通知剩下 的 Subscriber 对象，通常情况下，先订阅自定义事件的 Subscriber 可以阻止后订阅的 Subscriber 接收到通知。</p>

<p>使 用 YUI 的自定义事件（CustomEvent）可以很方便地实现观察者模式，更好地组织 JavaScript 程序的结构。</p>
]]></content:encoded>
			<wfw:commentRss>http://xujiwei.com/blog/yui-study-notes-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

