叶落为重生每片落下的叶子都是为了下一次的涅槃...^_^

置顶随笔 #

[置顶]canvas保存为data:image扩展功能的实现

摘要: 【已知】canvas提供了toDataURL的接口,可以方便的将canvas画布转化成base64编码的image。目前支持的最好的是png格式,jpeg格式的现代浏览器基本也支持,但是支持的不是很好。【想要的】往往这么简单直接的接口通常都满足不了需求。我想要的不仅是简单的通过画布生成一个png,我不想新开一个tab,然后还要右键另存为...我还需要更方便的自由的配置生成的图片的大小,比例等。另外如果我还要别的图片格式,比如位图bmp,gif等怎么办...【解决办法】a)想直接把图片生成后download到本地,其实办法也很简单。直接改图片的mimeType,强制改成steam流类型的。比如‘阅读全文

posted @ 2012-01-14 20:27 岑安 阅读(1138) 评论(0) 编辑

[置顶]关于base64编码的原理及实现

摘要: 我们的图片大部分都是可以转换成base64编码的data:image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外,大部分现代浏览器都已经支持原生的基于base64的encode和decode,例如btoa和atob。(将canvas画布保存成img并强制改变mimetype进行下载,会在下一篇记录)但是处于好奇心,还是驱使我去了解下base64编码的原理。以便也在不支持原生base64编码的ie下可以得以实现。【Base64】-base64的编码都是按字符串长度,以每3个8bit的字符为一组,-然后针对每组,首先获取每个字符的ASCII编码,-然后将ASCII编码转换成8b阅读全文

posted @ 2012-01-14 11:47 岑安 阅读(2132) 评论(4) 编辑

[置顶]追踪子弹-初中简单的物理和数学

摘要: 飞行射击类游戏很常用的追踪子弹,或者塔防里面固定炮台打 怪物的时候,为了保证子弹不会打空,追踪是必要的。然而,这是极其简单的事情。在每一帧里判断当前子弹和目标位置的距离和方向,不断修正 速度方向即可。// this.x, this.y 表示当前子弹的位置// this.tar.x, this.tar.y 表示当前目标的位置var dis = Math.sqrt(Math.pow((this.tar.x-this.x), 2) + Math.pow((this.tar.y - this.y), 2));var angleX = (this.tar.x - this.x)/dis;var angl阅读全文

posted @ 2012-01-10 15:33 岑安 阅读(215) 评论(7) 编辑

[置顶]【备忘】指定为同名callback的jsonp && IE下script loaded状态标记

摘要: 【已知】不知道大家有木有了解过jQuery1.0到2.0时候针对jsonp那一块的修改。v1.0的时候还在使用iframe作为请求数据的临时暂居地。以便让过往数据有据可查。保证了jsonp请求的时候即便用了同样的全局callback 也只至于先返回的数据丢失,造成数据污染的问题。但是自从jq2.0之后,就不再采用iframe来记录jsonp获得的数据了。可是依然,不得不支持同一个callback名。那么,这样,问题就来了...【问题】jsonp的原理其实就是我们把请求地址当作一个js地址以script tag 的方式插入到页面,把服务端返回的数据当作script 脚本来运行以获得所需的数据(通阅读全文

posted @ 2011-12-27 11:56 岑安 阅读(1231) 评论(0) 编辑

[置顶]【备忘】bounce ease

摘要: 偶尔会用到弹簧类似的缓动效果,就是不是直接从 A点缓动到B点, 而是 从A点出发,但是到最终停在B点之前,会以阻尼的方式在B点来回若干次。类似弹簧一样。 其实已有 bounce ease 的算法公式,比如:Bounce: { easeIn: function(t,b,c,d){ return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b; }, easeOut: function(t,b,c,d){ if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.阅读全文

posted @ 2011-12-22 21:58 岑安 阅读(158) 评论(6) 编辑

[置顶]逐帧动画 and 有限状态机(fsm)

摘要: 【逐帧动画】其实就canvas而言,和flash有太多相似的地方。最直接的一点:比如把一个object从a点移动到b点。并不是直接去改变object的位置,而是把a点的object擦掉,在b点重新画一个。这其实和我们儿时喜爱的动画原理一致的。电视机里播放的好看的动画,早期都是由我们的动画制作者一帧一帧画出来的。一秒30帧或者其他数。快速的转变欺骗我们的眼睛。如今的web动画,尤其是web Game一块,因为大量复杂绚丽的原因,简单依靠底层矢量的绘制基本已经不能满足需求。更多的,或者说更实际可用的是怎么把一张张图片资源连接到一起变成动画的方法。dom里面background 本身支持backgr阅读全文

posted @ 2011-12-04 19:28 岑安 阅读(1498) 评论(5) 编辑

[置顶]组内分享slide 【about 3D】

摘要: 请用webkit内核的浏览器查看以下slide,左右拖拽即可。(_blank的demo若有问题,请刷新下)阅读全文

posted @ 2011-11-09 12:30 岑安 阅读(338) 评论(3) 编辑

[置顶]Chaikin Curve(球面插值)

摘要: 在两条折线间完成平滑的过渡是 用画布做UI 或者做类似地图编辑器一类的工作的 很常见的任务。怎么样化方为圆是决定工作效率的很重要的因素。(当需要编辑的曲线多起来, 复杂起来的时候,这会是件相当繁重的工作)最容易想到的莫非是 贝塞尔曲线,而且时下几乎所有主流的数学算法库或者画布api 都已经很好的支持了贝塞尔曲线的绘制。 并能提供很便利的接口,通常只需知道 开始位置, 结束位置 ,以及贝塞尔控制点 就可生成一条贝塞尔曲线。例如:context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY); 贝塞尔曲线的阅读全文

posted @ 2011-10-27 19:04 岑安 阅读(1154) 评论(6) 编辑

[置顶]【Director pattern】迪米特法则+观察者模式

摘要: 如何构建大型web app?在web pages时代,用脚本来做的事情少的可怜,校验用户输入,表单提交,简单的滚动 等。后来ajax的出现,允许异步的去获取服务器数据,局部改变html文档内容或样式。于是以前要用两个或多个页面跳转才能表达的内容或交互逐渐被放到一个web page里面来。但由于网络速度,服务器性能,客户端性能,web技术瓶颈等原因。脚本能干的事情依旧有很大的局限。随着近几年web技术的发展,特别是前端技术的发展,用户在浏览器里面浏览html文档时,不仅仅停留在“浏览”的层面了,开始要求更好的体验。 于是各大浏览器厂商也争先推出更快的脚本引擎,更快的渲染速度。脚本能做的事情越来越阅读全文

posted @ 2011-10-11 22:31 岑安 阅读(1376) 评论(4) 编辑

[置顶]webkit-box & translate 的组合--流畅的滑动体验

摘要: 【注:本文所有的代码和实例仅在chrome和safari等webkit内核的浏览器测试通过】如果说从web Pages 能够转到web app时代,那么css3和html5其他相关技术一定是巨大的功臣。唯一的遗憾就是pc端浏览器的泛滥导致了我们不得不走所谓的优雅降级,而且这种降级是降到新技术几乎木有多大的用武之地。于是,客户端还算统一的移动端开始成了一个大的试验田。能够让众人大肆的在上面舒展拳脚。诸如众多新起的ui库或者框架(jquery-mobile, sencha, phoneGap ...),可见在移动终端上确实还有不小的田地。纵使如此,效率仍旧成为一个最大的瓶颈。之前有一种尝试是用CS阅读全文

posted @ 2011-10-10 14:53 岑安 阅读(2030) 评论(9) 编辑

[置顶]rotate 3D 篇二

摘要: 接上一篇,基于rotate的方式模拟三维空间。一开始定一个旋转原点,让物体随着这个原点旋转。(一般定为画布中心)vpx = canvas.width/2;vpy = canvas.height/2;然后为了让旋转效果和用户有交互,让旋转速度和角度和鼠标位置挂上关系stage.addEventListener('mousemove', function (x, y) { angleY = (x - vpx) * .001; angleX = (y - vpy) * .001;});接下来,如果我们有个三维空间里的小球ball,它有三向坐标(xpos, ypos, zpos)。下面阅读全文

posted @ 2011-09-16 14:56 岑安 阅读(1413) 评论(4) 编辑

[置顶]rotate 3D [初识篇]

摘要: 随着前端技术发展,尤其是html5中canvas和svg的应用,开始让web也可以轻易的渲染出各种绚丽的效果。本篇讨论的是基于rotate(旋转)的3d效果的初识。在canvas的getContext('2d')下利用一些变换来模拟。webGL是后话,本篇暂不讨论。【简要原理】由于仍是在2d下模拟,所以所谓的3d最终还是要降到2d的层面来。在坐标上的表现就是,3d的界面应该是有x,y,z三向坐标,2d的就只有x,y二向。那么怎么把3d的z向坐标降到和2d的x,y相关联起来,就是关键。要在2d的界面上展现3d的z方向的层次感,需要一个视井。相信学过绘画的同学应该很清楚,要画透视或阅读全文

posted @ 2011-09-12 20:10 岑安 阅读(1613) 评论(8) 编辑

[置顶]关于wrapper和chain

摘要: 随手而记...在某些有规律的对特定形式的元素或者对象操作时。wrap对于api的调用有很意想不到的作用。简易包装:/** * $wrap {给一对象打包装} * @author horizon(岑安) */var $wrap = function (obj) { //private temp object var _obj = obj, toString = Object.prototype.toString, slice = Array.prototype.slice, unshift = Array.prototype.unshift, // defin...阅读全文

posted @ 2011-09-10 14:30 岑安 阅读(1035) 评论(1) 编辑

[置顶]【share】第六期html5沙龙讲稿 (深圳)

摘要: 临时被邀请去深圳区html5沙龙第六期分享些html5相关的内容。 结果不出意外,没有脱离canvas。 并且由于时间关系,准备不够充分,很多demo甚至就直接从本博客拉下去的。(鄙人表示汗颜...) 但是重心内容是没在本博客发表过的: -- 关于canvas编程模型的封装尝试 -- 基于二维矢量的简单运动学模拟 -- 简单粒子系统的collection管理 部分demo代码思路取自Milo同学,表示感谢! 详细内容不做赘述,在线讲稿 PPT_Canvas(键盘左右键控制翻页) github:https://github.com/hongru/JCanvas(a...阅读全文

posted @ 2011-08-28 13:06 岑安 阅读(1362) 评论(5) 编辑

[置顶]各种纪念-好久没更新了

摘要: 快两个月没更新博客了。这一阵各种事情,奔波劳累,也是换了公司,换了城市。刚刚才算安顿了下来。一切重新开始,慢慢步入正轨吧。 算算在博客园第一篇博客至今也差不多一年了。一年的时间说长不长,只能说稍稍有些沉淀,这两个月也是因为换了工作的原因,平时工作比较忙,没来得及给自己充电。在今后的日子里,还是应该就着自己感兴趣的地方,继续挖一挖,做一点记录,一方面给自己作积累,另一方面也是和园友们共同交流吧。 我自己的轮子也是在造,但进度比较慢,基本每天能拿出一两个小时来做自己的东西就算不错了。另外有一个关于canvas的绘图api的二次封装的东东也在着手做。此外还有自已一直想做的javascript pa.阅读全文

posted @ 2011-08-21 10:44 岑安 阅读(318) 评论(4) 编辑

[置顶]【svg+vml】部分尝试

摘要: 半年前看过国外一个大牛的一个demo,然后自己用他的思路写了一个类似的东西。加了点额外的功能。 当时主要是为了学习svg的部分api,做着玩。<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Rag Doll</title> <meta name="Author" content="hongru.chen" /> <style _mce_bogus="1"阅读全文

posted @ 2011-06-18 17:16 岑安 阅读(1921) 评论(7) 编辑

[置顶]【记】几个我并没有想好的问题

摘要: 开门见山,记录三个问题,恰逢有人问我,仔细想想,发现原来真的没有想好,故重新思考后稍作记录。 【1.关于作用域和作用域链】 作用域的特性:自封闭,词法分区,调用对象冒泡,预声明。 我记得我曾在以前的一篇文章中提到了变量作用域一个预声明(hoisting)的特性,其中有个经典案例,也是好多公司用做面试的考题。var a = 'global';void function () {alert(a);var a = 'local';}()弹出的结果是undefined,即变量声明期和赋值期不宜样,这一点很多人都知道,我也不细说了。全局变量是全局对象的一个属性,那么在上面阅读全文

posted @ 2011-06-06 18:18 岑安 阅读(1979) 评论(7) 编辑

[置顶]【告示】新博客开始使用

摘要: 博客园的确是个高手云集的地方,技术氛围也算不错,可惜毕竟是第三方博客,自己想捣腾点东西就不太方便了,所以最后还是自己弄了个空间,挂个wrodpress,除了发文章之外,还可以捣腾些更多的东西... 新的空间基本上部署完毕,开始使用了,我也会继续时不时更新一些自己的想法和小demo在上面,欢迎围观。地址:elseif.me ps:博客园的博客应该也不会废弃,有时间会一起更新的...阅读全文

posted @ 2011-05-04 09:36 岑安 阅读(498) 评论(1) 编辑

[置顶]JS Format 未完成,先上JS HighLight吧

摘要: 这一篇倒没什么好说的,该说的好像前一篇关于词法分析的困境中都说过了... 最开始本打算做代码格式化的,等熬过了所谓的词法分析,却发现还有座大山压着,格式化的话,缩进和空格是关键,可是缩进层级的确是个大问题...必须结和上下语境才能得出正确的缩进层级,貌似这已经不是词法分析了,改算语法分析了吧... 所以,还是先记录下简单一点的高亮吧,解决了我上一篇文章说的那几个难点,或者说能解决大部分难点,基本高亮就够了...http://www.cnblogs.com/hongru/archive/2011/04/12/2000049.html 没有用正则,因为我正则不好,另外个原因就是如果要接着做格式化.阅读全文

posted @ 2011-04-28 15:47 岑安 阅读(560) 评论(2) 编辑

[置顶]恕我简陋,恕我臃肿

摘要: 好吧,我承认为了赶赶时髦,做了点自讨苦吃的东西,现在还只做了一半。先记下吧,怕有些东东搞忘了。很惭愧,正则一直以来都是我的痛处,于是也想借着这个机会顺便练习下自己的正则,可惜做着做着我发现,潜意识里我为了逃避正则,最后却放弃了一心想提升的正则,把这些匹配做成了状态机...用了beautity.js的方式... token的判断由正则演变成了模式匹配,好吧,我承认我错了。 this.whitespace='\n\r\t'.split('');this.wordchar='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ阅读全文

posted @ 2011-04-12 20:44 岑安 阅读(1953) 评论(1) 编辑

2012年1月14日 #

canvas保存为data:image扩展功能的实现

【已知】
canvas提供了toDataURL的接口,可以方便的将canvas画布转化成base64编码的image。目前支持的最好的是png格式,jpeg格式的现代浏览器基本也支持,但是支持的不是很好。

【想要的】
往往这么简单直接的接口通常都满足不了需求。我想要的不仅是简单的通过画布生成一个png,我不想新开一个tab,然后还要右键另存为...

我还需要更方便的自由的配置生成的图片的大小,比例等。

另外如果我还要别的图片格式,比如位图bmp,gif等怎么办...

【解决办法】
a)想直接把图片生成后download到本地,其实办法也很简单。直接改图片的mimeType,强制改成steam流类型的。比如‘image/octet-stream’,浏览器就会自动帮我们另存为.. 

b)图片大小,及比例的可控倒也好办,我们新建一个我们想要大小的canvas,把之前的canvas画布重新按照所要的比例,及大小draw到新的canvas上,然后用新的canvas来toDataURL即可。

c)想要bmp位图会麻烦些... 没有直接的接口,需要我们自己来生成。生成图片的响应头和响应体有一定的规则,略显麻烦。不过还能接受。剩下的就是性能问题,按像素级别来操作,对于一个大图来说计算量很有压力。

【实现】

/**
* covert canvas to image
* and save the image file
*/

var Canvas2Image = function () {

// check if support sth.
var $support = function () {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');

return {
canvas: !!ctx,
imageData: !!ctx.getImageData,
dataURL: !!canvas.toDataURL,
btoa: !!window.btoa
};
}();

var downloadMime = 'image/octet-stream';

function scaleCanvas (canvas, width, height) {
var w = canvas.width,
h = canvas.height;
if (width == undefined) {
width = w;
}
if (height == undefined) {
height = h;
}

var retCanvas = document.createElement('canvas');
var retCtx = retCanvas.getContext('2d');
retCanvas.width = width;
retCanvas.height = height;
retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
return retCanvas;
}

function getDataURL (canvas, type, width, height) {
canvas = scaleCanvas(canvas, width, height);
return canvas.toDataURL(type);
}

function saveFile (strData) {
document.location.href = strData;
}

function genImage(strData) {
var img = document.createElement('img');
img.src = strData;
return img;
}
function fixType (type) {
type = type.toLowerCase().replace(/jpg/i, 'jpeg');
var r = type.match(/png|jpeg|bmp|gif/)[0];
return 'image/' + r;
}
function encodeData (data) {
if (!window.btoa) { throw 'btoa undefined' }
var str = '';
if (typeof data == 'string') {
str = data;
} else {
for (var i = 0; i < data.length; i ++) {
str += String.fromCharCode(data[i]);
}
}

return btoa(str);
}
function getImageData (canvas) {
var w = canvas.width,
h = canvas.height;
return canvas.getContext('2d').getImageData(0, 0, w, h);
}
function makeURI (strData, type) {
return 'data:' + type + ';base64,' + strData;
}


/**
* create bitmap image
* 按照规则生成图片响应头和响应体
*/
var genBitmapImage = function (data) {
var imgHeader = [],
imgInfoHeader = [];

var width = data.width,
height = data.height;

imgHeader.push(0x42); // 66 -> B
imgHeader.push(0x4d); // 77 -> M

var fsize = width * height * 3 + 54; // header size:54 bytes
imgHeader.push(fsize % 256); // r
fsize = Math.floor(fsize / 256);
imgHeader.push(fsize % 256); // g
fsize = Math.floor(fsize / 256);
imgHeader.push(fsize % 256); // b
fsize = Math.floor(fsize / 256);
imgHeader.push(fsize % 256); // a

imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(0);

imgHeader.push(54); // offset -> 6
imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(0);

// info header
imgInfoHeader.push(40); // info header size
imgInfoHeader.push(0);
imgInfoHeader.push(0);
imgInfoHeader.push(0);

// 横向info
var _width = width;
imgInfoHeader.push(_width % 256);
_width = Math.floor(_width / 256);
imgInfoHeader.push(_width % 256);
_width = Math.floor(_width / 256);
imgInfoHeader.push(_width % 256);
_width = Math.floor(_width / 256);
imgInfoHeader.push(_width % 256);

// 纵向info
var _height = height;
imgInfoHeader.push(_height % 256);
_height = Math.floor(_height / 256);
imgInfoHeader.push(_height % 256);
_height = Math.floor(_height / 256);
imgInfoHeader.push(_height % 256);
_height = Math.floor(_height / 256);
imgInfoHeader.push(_height % 256);

imgInfoHeader.push(1);
imgInfoHeader.push(0);
imgInfoHeader.push(24); // 24位bitmap
imgInfoHeader.push(0);

// no compression
imgInfoHeader.push(0);
imgInfoHeader.push(0);
imgInfoHeader.push(0);
imgInfoHeader.push(0);

// pixel data
var dataSize = width * height * 3;
imgInfoHeader.push(dataSize % 256);
dataSize = Math.floor(dataSize / 256);
imgInfoHeader.push(dataSize % 256);
dataSize = Math.floor(dataSize / 256);
imgInfoHeader.push(dataSize % 256);
dataSize = Math.floor(dataSize / 256);
imgInfoHeader.push(dataSize % 256);

// blank space
for (var i = 0; i < 16; i ++) {
imgInfoHeader.push(0);
}

var padding = (4 - ((width * 3) % 4)) % 4;
var imgData = data.data;
var strPixelData = '';
var y = height;
do {
var offsetY = width * (y - 1) * 4;
var strPixelRow = '';
for (var x = 0; x < width; x ++) {
var offsetX = 4 * x;
strPixelRow += String.fromCharCode(imgData[offsetY + offsetX + 2]);
strPixelRow += String.fromCharCode(imgData[offsetY + offsetX + 1]);
strPixelRow += String.fromCharCode(imgData[offsetY + offsetX]);
}
for (var n = 0; n < padding; n ++) {
strPixelRow += String.fromCharCode(0);
}

strPixelData += strPixelRow;
} while(-- y);

return (encodeData(imgHeader.concat(imgInfoHeader)) + encodeData(strPixelData));

};

/**
* saveAsImage
* @param canvasElement
* @param {String} image type
* @param {Number} [optional] png width
* @param {Number} [optional] png height
*/
var saveAsImage = function (canvas, width, height, type) {
if ($support.canvas && $support.dataURL) {
if (type == undefined) { type = 'png'; }
type = fixType(type);
if (/bmp/.test(type)) {
var data = getImageData(scaleCanvas(canvas, width, height));
var strData = genBitmapImage(data);
saveFile(makeURI(strData, downloadMime));
} else {
var strData = getDataURL(canvas, type, width, height);
saveFile(strData.replace(type, downloadMime));
}

}
}

var convertToImage = function (canvas, width, height, type) {
if ($support.canvas && $support.dataURL) {
if (type == undefined) { type = 'png'; }
type = fixType(type);

if (/bmp/.test(type)) {
var data = getImageData(scaleCanvas(canvas, width, height));
var strData = genBitmapImage(data);
return genImage(makeURI(strData, 'image/bmp'));
} else {
var strData = getDataURL(canvas, type, width, height);
return genImage(strData);
}
}
}



return {
saveAsImage: saveAsImage,
saveAsPNG: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'png');
},
saveAsJPEG: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'jpeg');
},
saveAsGIF: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'gif')
},
saveAsBMP: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'bmp');
},

convertToImage: convertToImage,
convertToPNG: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'png');
},
convertToJPEG: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'jpeg');
},
convertToGIF: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'gif');
},
convertToBMP: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'bmp');
}
};

}();

【Demo】
http://hongru.github.com/proj/canvas2image/index.html
可以试着在canvas上涂涂画画,然后保存看看。如果用bmp格式的话,需要支持 btoa 的base64编码,关于base64编码规则可看上一篇博文

【不完美的地方】
1)jpeg接口本身就不完善,当canvas没有填充颜色或图片时,保存的jpeg由于是直接由png的alpha通道强制转换过来的,所以在png的透明部分在jpeg里面就是黑色的。

2)gif的限制太多。且可用性不大,有png就够了

3)bmp位图生成,计算量稍显大了。

4)由于是强制改mimeType来实现的自动下载,所以下载的时候文件类型不会自动识别。
 

posted @ 2012-01-14 20:27 岑安 阅读(1138) 评论(0) 编辑

关于base64编码的原理及实现

我们的图片大部分都是可以转换成base64编码的data:image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外,大部分现代浏览器都已经支持原生的基于base64的encode和decode,例如btoa和atob。(将canvas画布保存成img并强制改变mimetype进行下载,会在下一篇记录)

但是处于好奇心,还是驱使我去了解下base64编码的原理。以便也在不支持原生base64编码的ie下可以得以实现。

【Base64】
-base64的编码都是按字符串长度,以每3个8bit的字符为一组,
-然后针对每组,首先获取每个字符的ASCII编码,
-然后将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节
-然后再将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节
-然后将这4个8bit的字节转换成10进制,对照Base64编码表 (下表),得到对应编码后的字符。

(注:1. 要求被编码字符是8bit的,所以须在ASCII编码范围内,\u0000-\u00ff,中文就不行。
   2. 如果被编码字符长度不是3的倍数的时候,则都用0代替,对应的输出字符为=)

Base64 编码表
ValueChar   ValueChar   ValueChar   ValueChar
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 r 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /

比如举下面2个例子:
a) 字符长度为能被3整除时:比如“Tom” :

            T           o           m
ASCII: 84 111 109
8bit字节: 01010100 01101111 01101101
6bit字节: 010101 000110 111101 101101
十进制: 21 6 61 45
对应编码: V G 9 t

所以,btoa('Tom') = VG9t

b) 字符串长度不能被3整除时,比如“Lucy”:

            L           u           c           y
ASCII: 76 117 99 121
8bit字节: 01001100 01110101 01100011 01111001 00000000 00000000
6bit字节: 010011 000111 010101 100011 011110 010000 000000 000000
十进制: 19 7 21 35 30 16 (异常) (异常)
对应编码: T H V j e Q = =

由于Lucy只有4个字母,所以按3个一组的话,第二组还有两个空位,所以需要用0来补齐。这里就需要注意,因为是需要补齐而出现的0,所以转化成十进制的时候就不能按常规用base64编码表来对应,所以不是a, 可以理解成为一种特殊的“异常”,编码应该对应“=”。

有了上面的理论,那我们实现一个base64编码就容易了。
 

/**
* base64 encoding & decoding
* for fixing browsers which don't support Base64 | btoa |atob
*/

(function (win, undefined) {

var Base64 = function () {
var base64hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

// btoa method
function _btoa (s) {
if (/([^\u0000-\u00ff])/.test(s)) {
throw new Error('INVALID_CHARACTER_ERR');
}
var i = 0,
prev,
ascii,
mod,
result = [];

while (i < s.length) {
ascii = s.charCodeAt(i);
mod = i % 3;

switch(mod) {
// 第一个6位只需要让8位二进制右移两位
case 0:
result.push(base64hash.charAt(ascii >> 2));
break;
//第二个6位 = 第一个8位的后两位 + 第二个8位的前4位
case 1:
result.push(base64hash.charAt((prev & 3) << 4 | (ascii >> 4)));
break;
//第三个6位 = 第二个8位的后4位 + 第三个8位的前2位
//第4个6位 = 第三个8位的后6位
case 2:
result.push(base64hash.charAt((prev & 0x0f) << 2 | (ascii >> 6)));
result.push(base64hash.charAt(ascii & 0x3f));
break;
}

prev = ascii;
i ++;
}

// 循环结束后看mod, 为0 证明需补3个6位,第一个为最后一个8位的最后两位后面补4个0。另外两个6位对应的是异常的“=”;
// mod为1,证明还需补两个6位,一个是最后一个8位的后4位补两个0,另一个对应异常的“=”
if(mod == 0) {
result.push(base64hash.charAt((prev & 3) << 4));
result.push('==');
} else if (mod == 1) {
result.push(base64hash.charAt((prev & 0x0f) << 2));
result.push('=');
}

return result.join('');
}

// atob method
// 逆转encode的思路即可
function _atob (s) {
s = s.replace(/\s|=/g, '');
var cur,
prev,
mod,
i = 0,
result = [];

while (i < s.length) {
cur = base64hash.indexOf(s.charAt(i));
mod = i % 4;

switch (mod) {
case 0:
//TODO
break;
case 1:
result.push(String.fromCharCode(prev << 2 | cur >> 4));
break;
case 2:
result.push(String.fromCharCode((prev & 0x0f) << 4 | cur >> 2));
break;
case 3:
result.push(String.fromCharCode((prev & 3) << 6 | cur));
break;

}

prev = cur;
i ++;
}

return result.join('');
}

return {
btoa: _btoa,
atob: _atob,
encode: _btoa,
decode: _atob
};
}();

if (!win.Base64) { win.Base64 = Base64 }
if (!win.btoa) { win.btoa = Base64.btoa }
if (!win.atob) { win.atob = Base64.atob }

})(window)

 

Base64 example

 

posted @ 2012-01-14 11:47 岑安 阅读(2132) 评论(4) 编辑

2012年1月10日 #

追踪子弹-初中简单的物理和数学

飞行射击类游戏很常用的追踪子弹,或者塔防里面固定炮台打 怪物的时候,为了保证子弹不会打空,追踪是必要的。

然而,这是极其简单的事情。

在每一帧里判断当前子弹和目标位置的距离和方向,不断修正 速度方向即可。

// this.x, this.y 表示当前子弹的位置
//
this.tar.x, this.tar.y 表示当前目标的位置
var dis = Math.sqrt(Math.pow((this.tar.x-this.x), 2) + Math.pow((this.tar.y - this.y), 2));
var angleX = (this.tar.x - this.x)/dis;
var angleY = (this.tar.y - this.y)/dis;
this.speedX = speed * angleX;
this.speedY = speed * angleY;
this.x += this.speedX;
this.y += this.speedY;

算出速度方向,然后 速度*dt 叠加到 位移即可。

Trace Bullet



posted @ 2012-01-10 15:33 岑安 阅读(215) 评论(7) 编辑

2012年1月6日 #

【备忘】canvas下图片翻转-转自oldj.net(英杰兄)

  原文链接:http://oldj.net/article/flip-images-in-html5/

  貌似 HTML5 的 Canvas 只提供了图片的旋转、缩放功能,没有提供图片翻转(水平翻转或垂直翻转)的支持,搜索加试验之后,得到几种实现图片翻转的方法,记录一下。

  第一种最简单的是使用 CSS,代码片断如下:

1 <style>
2 .flip-x {
3     filter: FlipH; /* IE only */
4     -moz-transform: matrix(-1, 0, 0, 1, 0, 0);
5     -webkit-transform: matrix(-1, 0, 0, 1, 0, 0);
6 }
7 </style>
8 <img src="http://oldj.net/images/oldj.net.png" class="flip-x" />

  支持 IE 、Firefox 等各大浏览器。不过,如果想在 HTML5 的 Canvas 中翻转一个图片,CSS 就无能为力了。

  在 Canvas 中翻转图片大致有两种思路,一种是先“翻转”画布,在上面画好需要的图片后再将画布“翻转”回来;另一种是先在画布上正常画上原图,用 getImageData 方法取得图片的每一个象素的数据,再将数据镜像交换一下。

  先来看“翻转”画布的方法,代码大致类似于这样:

01 // 正常绘制:
02 // ctx.drawImage(img, px, py);
03  
04 // 水平“翻转”画布
05 ctx.translate(canvas_width, 0);
06 ctx.scale(-1, 1);
07 // 下面画的图片是水平翻转的
08 ctx.drawImage(img, canvas_width - img.width - px, py);
09 // 画布恢复正常
10 ctx.translate(canvas_width, 0);
11 ctx.scale(-1, 1);

  可以看到,主要用到了 translate 以及 scale 方法。先用 translate 方法将坐标原点设为画布右上角(默认为左上角),再用 scale(-1, 1) 的方式将画布水平翻转,在上面画好图之后再恢复即可。

  另一种象素级的操作原理上也非常简单,就不多解释了,可以直接看源码:

01 <!doctype html>
02 <html>
03 <head>
04 <meta charset="UTF-8" />
05 <title>test</title>
06 <style type="text/css">
07 #cv {
08 border: solid 1px #333;
09 }
10 </style>
11 </head>
12 <body>
13 <canvas id="cv" width="300" height="240"></canvas>
14 <script>
15 var canvas = document.getElementById("cv"),
16     ctx = canvas.getContext("2d"),
17     img = new Image();
18  
19 img.src = "/images/oldj_gmail.png";
20  
21 function show() {
22     // 正常图片
23     ctx.drawImage(img, 10, 10);
24  
25     // 水平翻转
26     ctx.translate(300, 0);
27     ctx.scale(-1, 1);
28     // 下面画的图片是水平翻转的
29     ctx.drawImage(img, 300 - img.width - 10, 60);
30     // 恢复正常
31     ctx.translate(300, 0);
32     ctx.scale(-1, 1);
33  
34     // 下面的图片是正常的
35     ctx.drawImage(img, 10, 110);
36  
37  
38     // 象素级水平翻转图片的方法
39     show2();
40 }
41  
42 function show2() {
43     // 象素级水平翻转的方法
44  
45     // 先画一个正常的画片
46     var px = 10,
47         py = 160;
48  
49     ctx.drawImage(img, px, py);
50  
51     // 取得这个图片的数据,图片与当前页面必须同域,否则会出错
52     var img_data = ctx.getImageData(px, py, img.width, img.height),
53  
54         x, y, p, i, i2, t,
55         h = img_data.height;
56         w = img_data.width,
57         w_2 = w / 2;
58  
59     // 将 img_data 的数据水平翻转
60     for (y = 0; y < h; y ++) {
61         for (x = 0; x < w_2; x ++) {
62             i = (y<<2) * w + (x<<2);
63             i2 = ((y + 1) << 2) * w - ((x + 1) << 2);
64             for (p = 0; p < 4; p ++) {
65                 t = img_data.data[i + p];
66                 img_data.data[i + p] = img_data.data[i2 + p];
67                 img_data.data[i2 + p] = t;
68             }
69         }
70     }
71  
72     // 重绘水平翻转后的图片
73     ctx.putImageData(img_data, px, py);
74 }
75  
76 img.onload = function () {
77     show();
78 };
79 </script>
80 </body>
81 </html>

posted @ 2012-01-06 16:44 岑安 阅读(112) 评论(0) 编辑

2011年12月27日 #

【备忘】指定为同名callback的jsonp && IE下script loaded状态标记

【已知】

不知道大家有木有了解过jQuery1.0到2.0时候针对jsonp那一块的修改。v1.0的时候还在使用iframe作为请求数据的临时暂居地。以便让过往数据有据可查。保证了jsonp请求的时候即便用了同样的全局callback 也只至于先返回的数据丢失,造成数据污染的问题。

但是自从jq2.0之后,就不再采用iframe来记录jsonp获得的数据了。可是依然,不得不支持同一个callback名。

那么,这样,问题就来了...

【问题】

jsonp的原理其实就是我们把请求地址当作一个js地址以script tag 的方式插入到页面,把服务端返回的数据当作script 脚本来运行以获得所需的数据(通常是第一个参数)。既然是以script脚本的方式载入,自然就绕过的跨域的问题。这自然需要服务端做一点配合。把我们约定传的callback参数以函数的形式返回,方便我们执行,获得数据。

那,如果我们有多个jsonp的请求,而且非要,死活要指定同样的callback名呢,肿么办?

<script src="omg.php?callback=myFunc&param=1"></script>
<script src="omg.php?callback=myFunc&param=2"></script>
<script src="omg.php?callback=myFunc&param=3"></script>

因为请求耗时的不确定性,无法确定每个回调执行的顺序,比如,一个简单的input suggest, 假设我们每次用户keyup都去请求一次数据,然后每次请求回调里面重新render我的们suggest list。实际上,这些请求是有“顺序”可言的,也就是说:我们原意是,后来的请求的回调也应该后执行才对, 这样才能保证 我输入 abc ,最后得到的suggest list 不是 a 的, 或者 ab的。(这是一个问题,但不是jsonp本身应该讨论的问题)

当然,你可以创建一个hash维护数据,当成一个自定义的cache来用,用一个唯一的且可以标识这个请求的key就行。每次要render的时候校验一次当前数据是否是你想要的请求得来的数据即可。

可是....

这里要讨论的并不是这个问题。

a) 如果是一个同名的全局callback,单单从这个函数来看的话,我们其实是不知道它被重写了多少次?结果和请求是否能对应起来?尤其是当其中有某些脚本发生错误的时候(当然,如果非要去hack,也并非不可)

b)不管在哪个浏览器里,一个脚本在执行之前,是无法对其干预的(removeNode之类的不算),只有当脚本loaded并且执行后,或者发生错误之后,才能对其进行操作。

c)正因为在execution 之前你不能对其进行操作,所以无法得知,这些脚本会以什么样的顺序进行loading和executing。

【通常】

为了避免所谓的callback污染:

a)用计数器对请求结果进行连接控制,jq v2.0+ 类似的方式

b)每次使用完返回数据后都重置,下一次使用前都检查下,防止二次污染使用。

对jsonp做一个简单通用的requestCallback, onload和onerror都能调

// some generic code
// common to all requests

var lastValue;

function genericCallback( value ) {
  lastValue = [ value ];
}

// then the request specific closure

function request( options ) {

  window[ options.callback ] = genericCallback;

  function requestCallback() {

    var tmp = lastValue;
    lastValue = undefined;

    if ( ! tmp ) {
      options.error();
    } else {
      options.success( tmp[ 0 ] );
    }
  }

  // create the script tag

  // "attach" requestCallback to the script tag

  // put the script tag in the DOM

}

..........
scriptTag.onload = scriptTag.onerror = requestCallback;

Opera并没提供不标准的onerror的钩子。但是无妨,onload亦可。

【IE下onreadystatechange 怪癖】

scriptTag.onreadystatechange = function() {
  if ( /loaded|complete/.test( scriptTag.readyState ) ) {
    requestCallback();
  }
};

ie下,onreadystatechange 的调用 并不是紧紧跟在脚本执行或者失败之后, 而是有一点点延迟,尤其是当有这个请求有缓存的时候。而有可能这一点点延迟的时候就有另一个脚本回调进来了,那就悲剧了。

如果这样:

<div id="divId" />

<script
  id="scriptId"
  for="divId"
  event="onclick"
  src="script.js">
</script>

奇淫技巧,尝试load脚本,如果成功,ie下会触发divId 的一个onclick,而且不管load是否成功,都会触发script的onreaderstatechange的loaded readyState,而且最重要的是这个发生在脚本load完成,excute之前。

进一步,div的onclick可以被触发,同样的方式对于script呢?

<script
  id="scriptId"
  for="scriptId"
  event="onclick"
  src="script.js">
</script>

答案是肯定的,所以我们也不用额外的创建一个dom了。

所以可以用 script的event和for属性 来fix ie的onreaderstatechange的毛病了:

scriptTag.event = "onclick";
scriptTag.id = scriptTag.htmlFor = generateNewId();

scriptTag.onreadystatechange = function() {
  if ( /loaded|complete/.test( scriptTag.readyState ) ) {
    try {
      scriptTag.onclick();
    } catch( e ) {}
    requestCallback();
  }
};

ie>=9 或许并不需要这个hack,但是无影响。

【所以】

// 代码其中一段... 

 var head = document.getElementsByTag('head')[0],
      uniqid = 0,
      lastValue;
      
  function generalCallback(data) {
    lastValue = data
  }

  function urlappend(url, s) {
    return url + (/\?/.test(url) ? '&' : '?') + s
  }

  function handleJsonp(o, fn, err, url) {
    var reqId = uniqid++,
        cbkey = o.jsonpCallback || 'callback', // the 'callback' key
        cbval = o.jsonpCallbackName || ('__myrequest__' + reqId), // the 'callback' value
        cbreg = new RegExp('(' + cbkey + ')=(.+)(&|$)'),
        match = url.match(cbreg),
        script = doc.createElement('script'),
        loaded = 0;

    if (match) {
      if (match[2] === '?') {
        url = url.replace(cbreg, '$1=' + cbval + '$3') // wildcard callback func name
      } else {
        cbval = match[2] // provided callback func name
      }
    } else {
      url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em
    }

    win[cbval] = generalCallback;

    script.type = 'text/javascript';
    script.src = url;
    script.async = true;
    if (typeof script.onreadystatechange !== 'undefined') {
        // need this for IE due to out-of-order onreadystatechange(), binding script
        // execution to an event listener gives us control over when the script
        // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
        script.event = 'onclick';
        script.htmlFor = script.id = '_myrequest_' + reqId;
    }

    script.onload = script.onreadystatechange = function () {
      if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) {
        return false
      }
      script.onload = script.onreadystatechange = null;
      script.onclick && script.onclick();
      // Call the user callback with the last value stored and clean up values and scripts.
      o.success && o.success(lastValue);
      lastValue = undefined;
      head.removeChild(script);
      loaded = 1;
    }

    // Add the script to the DOM head
    head.appendChild(script);
  }

  

更多阅读:http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html  

  

  

  

posted @ 2011-12-27 11:56 岑安 阅读(1231) 评论(0) 编辑

2011年12月22日 #

【备忘】bounce ease

偶尔会用到弹簧类似的缓动效果,就是不是直接从 A点缓动到B点, 而是 从A点出发,但是到最终停在B点之前,会以阻尼的方式在B点来回若干次。类似弹簧一样。 其实已有 bounce ease 的算法公式,比如:

Bounce: { easeIn: function(t,b,c,d){ return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b; }, easeOut: function(t,b,c,d){ if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOut: function(t,b,c,d){
if (t < d/2) return Tween.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
else return Tween.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
}
}
可惜通常这样的公式都满足不了需求。我们要的是简易的,又易于控制的代码。所以:
move: function () {
var disX = this.endX - this.x;
var disY = this.endY - this.y;
var dis = Math.sqrt(Math.pow(disX, 2) + Math.pow(disY, 2));
var force = dis * parseFloat(document.getElementById('force').value);
var angle = Math.atan2(disY, disX); // atan2(x, y) 表示 点(x,y)到x轴的弧度
 
this.vx += force * Math.cos(angle);
this.vy += force * Math.sin(angle);
 
this.vx *= 0.92;
this.vy *= 0.92;
 
//
this.x += this.vx;
this.y += this.vy;
},
上面简单的代码可以做到我们想要的, 关键的在于 Math.atan2 获取某个点到x轴的反正弦。注意 两个参数,x,y调了个位置。 这个是为了配合下面 vx 阻尼时 乘以的系数。 因为
通常更习惯的是 x方向乘以 cos 的系数, y方向乘以 sin的系数。

Demo 1

Demo 2

posted @ 2011-12-22 21:58 岑安 阅读(158) 评论(6) 编辑

2011年12月20日 #

Particles Text

摘要: testtest阅读全文

posted @ 2011-12-20 10:19 岑安 阅读(80) 评论(0) 编辑

2011年12月4日 #

逐帧动画 and 有限状态机(fsm)

摘要: 【逐帧动画】其实就canvas而言,和flash有太多相似的地方。最直接的一点:比如把一个object从a点移动到b点。并不是直接去改变object的位置,而是把a点的object擦掉,在b点重新画一个。这其实和我们儿时喜爱的动画原理一致的。电视机里播放的好看的动画,早期都是由我们的动画制作者一帧一帧画出来的。一秒30帧或者其他数。快速的转变欺骗我们的眼睛。如今的web动画,尤其是web Game一块,因为大量复杂绚丽的原因,简单依靠底层矢量的绘制基本已经不能满足需求。更多的,或者说更实际可用的是怎么把一张张图片资源连接到一起变成动画的方法。dom里面background 本身支持backgr阅读全文

posted @ 2011-12-04 19:28 岑安 阅读(1498) 评论(5) 编辑

2011年11月9日 #

组内分享slide 【about 3D】

摘要: 请用webkit内核的浏览器查看以下slide,左右拖拽即可。(_blank的demo若有问题,请刷新下)阅读全文

posted @ 2011-11-09 12:30 岑安 阅读(338) 评论(3) 编辑

2011年10月27日 #

Chaikin Curve(球面插值)

摘要: 在两条折线间完成平滑的过渡是 用画布做UI 或者做类似地图编辑器一类的工作的 很常见的任务。怎么样化方为圆是决定工作效率的很重要的因素。(当需要编辑的曲线多起来, 复杂起来的时候,这会是件相当繁重的工作)最容易想到的莫非是 贝塞尔曲线,而且时下几乎所有主流的数学算法库或者画布api 都已经很好的支持了贝塞尔曲线的绘制。 并能提供很便利的接口,通常只需知道 开始位置, 结束位置 ,以及贝塞尔控制点 就可生成一条贝塞尔曲线。例如:context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY); 贝塞尔曲线的阅读全文

posted @ 2011-10-27 19:04 岑安 阅读(1154) 评论(6) 编辑

导航

统计信息

News

搜索

 
 

常用链接

最新随笔

我的标签

随笔档案

积分与排名

最新评论

阅读排行榜

评论排行榜

推荐排行榜