云标签,关键字图排版 html5 canvas版(一)
在做的过程当中,查阅了一些资料, 发现自己有点out了,在国外已经在wordle.net这样的网站.
也有一个叫做信息视觉化(Information Visualization)的概念.于是顺着这个概念再googling了一些相关的知识.把一些知识点做一下笔记.
Information Visualization
漂亮,惊艳.与传统的云标签的表现力对比,原来的太普通了.给人全新的一种视觉感受.
而后我的想法则是为什么这么有表现力的技术不能用在我们现在的项目上?所以我有了去尝试做这种排版的想法.
现在把一些过程记录,给自己的一种笔记,给其他后来者留下一些资料.
而云标签只是信息视觉化的其中一个很小的应用.有兴趣的同学可以参考wiki或我文后给出的链接查看详情.
这里不展开描述,不是本文的重点。
过程
以上图为例,一句话简单的描述起来就是,不停的尝试把当前的文字放到能够放到的位置,直到没有文字为止.
稍微画个流程图的话,大致如下:
这里面需要特别说明的几点, 各位可以先试想一下这几个问题:
1.如何进行字符碰撞检测?
2.如果是在脚本里去做的话,如何能拿到文字显示到画布上的像素点?
3.文字大小如何设定?
4.文字是垂直还是水平如何设定?
5.如果我要显示特殊图形如何扩展?
---------分隔线--------
1.碰撞检测是很耗CPU的.最笨的方法是每个像素点都去遍历尝试,直到能放到区域内为止.也可以用空间换时间,例如使用hash cache降低放在canvas中的io操作.再进一步可以把目前所有文字的范围最小x,y坐标,最大的x,y坐标记录下来,以便于下次检测碰撞可以直接限制坐标范围.
2.canvas目前高版本里可以用canvas.getContext('2d').measureText(word).width(注:目前也仅支持这一个属性,其他属性还不支持)
3.文字大小设置最大值, 设: 最大值==出现最多字的count,那么比例ratio=MaxFontsize/maxWordcount,随之过滤掉过小的fontsize字.
4.文字和水平对于排版影响不大,可在随机,也可以通过一个函数是做符合设定规则的转换,如查用随机显示文字是垂直还是水平简单一句:isVertical = Math.random > 0.3;
5.特殊图形==图形显示范围.我个人理解应该至少有两种方案,一种是用函数计算出来,另一种方案是先手工把边界制定出来,而后再填充.(本质上是一样去约束范围)
关键代码
1.文字count
如果是中文的话,需要处理分词,分好词后再count. 关于中文分词已经算是一个单独领域了, 我不专业,就此跳过. 而英文的话很好办.
words = text.split(/\\b/);
直接通过边界分,然后转成map,count后用filter过滤即可.
比较简单,代码略过.
2.范围
如何能让文字随机显示在界面上呢?
var arr = []; for(var i=0; i return Math.floor(arr.reduce(function (i, j) {return i + j}, 0) / iter * (max - min)) + min; }
以Math.random()随机范围在(0~1),如果随机次数趋向正无穷,那么理论上随机的平均值是趋向于0.5.
例: normalInt(0,100,10000),结果接近于50,有可能出现的结果是49.12334
所以,如果代码是
y = normalInt(0, canvas.height, 100);
3.如何能得到显示文字边缘
canvas里面能获得imagedata, 获取CanvasPixelArray接口为canvas.getContext('2d').getImageData(x, y, width, height);
数据类型为CanvasPixelArray. 它是像素矩阵的扁平表示方法:
CanvasPixelArray包括 宽*高*4 个字节的数据, 索引范围从0到 (高*宽*4)-1.
要取得图片里一个[x,y]坐标的red颜色信息可以用以下方法
var redValueForPixel = ((y - 1) * (width * 4)) + ((x - 1) * 4);
另:在处理图像的时候,如果有API可用的话,基本都是按这样的索引结构存储.
点击图片的rgba信息并将body background显示为该色
load 外部图权限需要提升
onload = function() {
var cx, canvas;
var WIDTH = 280, HEIGHT = 140;
var image = new Image();
void function createCanvas() {
canvas = document.createElement('canvas');
canvas.width = WIDTH;
canvas.height = HEIGHT;
canvas.style.border = '1px solid #000';
document.body.appendChild(canvas);
cx = canvas.getContext('2d');
}();
void function loadImage() {
image.src = "http://www.baidu.com/img/baidu_sylogo1.gif";
$(image).load(function() {
cx.drawImage(image, 0, 0);
});
}();
$(canvas).click(function(e) {
var canvasOffset = $(canvas).offset();
var canvasX = Math.floor(e.pageX-canvasOffset.left);
var canvasY = Math.floor(e.pageY-canvasOffset.top);
try {
var imageData = cx.getImageData(0, 0, canvas.width, canvas.height);
} catch(ex) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
var imageData = cx.getImageData(0, 0, canvas.width, canvas.height);
}
var pixels = imageData.data;
var pixelRedIndex = ((canvasY - 1) * (imageData.width * 4)) + ((canvasX - 1) * 4);
var pixelcolor = "rgba("+pixels[pixelRedIndex]+", "+pixels[pixelRedIndex+1]+", "+pixels[pixelRedIndex+2]+", "+pixels[pixelRedIndex+3]+")";
$("body").css("backgroundColor", pixelcolor);
});
}
4.碰撞
碰撞的算法比较简单,但是如何能提高碰撞检测的效率是关键之处.
这里用的方法是通过基于绘制文本的x,y为基础点去做判断.复杂度为循环要绘制文本的像素点:
O(((y - 1) * (width * 4)) + ((x - 1) * 4));
近似等于O( (Math.max(width, height)^2) ).
* collusion 检测碰撞
* @param {CanvasPixelArray} imageData 画布的imageData
* @param {CanvasPixelArray} wordImageData 文字的imageData
* @param {Number} x 绘制文字在画布的x坐标
* @param {Number} y 绘制文字在画布的y坐标
* @return {Boolean} 返回是否碰撞
*/
function collision(imageData, wordImageData, x, y) {
var wdata, fdata, wy, widx, widxend, fidx, wv, fv;
wdata = wordImageData.data;
fdata = imageData.data;
for (wy = wordImageData.height - 1; wy >= 0; --wy) {
//逐行扫描
widx = wy * wordimg.width * 4;//当前行rgba起始值
widxend = widx + (wordimg.width * 4);//当前行末rgba结束值
fidx = ((y + wy) * imageData.width + x) * 4;//大画布中当前行的像素起始值
for (; widx < widxend; widx += 4, fidx += 4) {//根据wordimg+y坐标定位到行,开始逐列扫描,并进行比较
//因为rgba的rgb任意像素都可能为空,所以只用判断opacity
wv = wdata[widx + 3];
fv = fdata[fidx + 3];
//需要放置的文字在某个坐标透明度不为零
//及在已存在画布上坐标上透明度也不为零,即为碰撞
if (wv && fv) return true;
}
}
return false;
}
一些资料
CanvasPixelArray参考MDC
https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas
wordle.net的作者写的一些资料:
http://static.mrfeinberg.com/bv_ch03.pdf
信息可视化wiki:
http://en.wikipedia.org/wiki/Information_visualization
stackoverflow上的一些信息:
http://stackoverflow.com/questions/342687/algorithm-to-implement-a-word-cloud-like-wordle
建议继续学习:
- CSS排版:例子和工具 (阅读:4083)
- Canvas学习教程 : Canvas介绍 (阅读:3168)
- 使用canvas绘制时钟 (阅读:2866)
- 使用JavaScript和Canvas开发游戏 (阅读:2709)
- HTML5 Canvas(画布)教程 (阅读:2622)
- 中文段首不需要空两格 (阅读:2479)
- CSS排版:技术与最佳实践 (阅读:2439)
- 使用JavaScript和Canvas开发游戏(一) (阅读:2302)
- CSS排版: 基础 (阅读:2304)
- Canvas高级特性 (阅读:2200)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:Rank <null@null.com> 来源: rank's technical notes
- 标签: canvas 排版
- 发布时间:2012-06-10 21:52:12
- [46] 界面设计速成
- [40] 视觉调整-设计师 vs. 逻辑
- [40] Oracle MTS模式下 进程地址与会话信
- [38] IOS安全–浅谈关于IOS加固的几种方法
- [37] android 开发入门
- [36] 如何拿下简短的域名
- [36] 程序员技术练级攻略
- [35] 图书馆的世界纪录
- [35] 【社会化设计】自我(self)部分――欢迎区
- [32] 读书笔记-壹百度:百度十年千倍的29条法则