在线云html排版,云标签,关键字图排版 html5 canvas版

最近业余时间在做一个云标签相关的信息展现. 大概做成的情况能像微博关键字一样形成这样的图形:

0818b9ca8b590ca3270a3433284dd417.png

在做的过程当中,查阅了一些资料, 发现自己有点out了,在国外已经在wordle.net这样的网站.

也有一个叫做信息视觉化(Information Visualization)的概念.于是顺着这个概念再googling了一些相关的知识.把一些知识点做一下笔记.

Information Visualization

漂亮,惊艳.与传统的云标签的表现力对比,原来的太普通了.给人全新的一种视觉感受.

而后我的想法则是为什么这么有表现力的技术不能用在我们现在的项目上?所以我有了去尝试做这种排版的想法.

现在把一些过程记录,给自己的一种笔记,给其他后来者留下一些资料.

而云标签只是信息视觉化的其中一个很小的应用.有兴趣的同学可以参考wiki或我文后给出的链接查看详情.

这里不展开描述,不是本文的重点。

过程

以上图为例,一句话简单的描述起来就是,不停的尝试把当前的文字放到能够放到的位置,直到没有文字为止.

稍微画个流程图的话,大致如下:

0818b9ca8b590ca3270a3433284dd417.png

这里面需要特别说明的几点, 各位可以先试想一下这几个问题:

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.范围

如何能让文字随机显示在界面上呢?

function normalInt(min, max, iter) {

var arr = []; for(var i=0; i

return Math.floor(arr.reduce(function (i, j) {return i + j}, 0) / iter * (max - min)) + min;

}

iter值越大,得出的结果越接近于min, max的中间值.

以Math.random()随机范围在(0~1),如果随机次数趋向正无穷,那么理论上随机的平均值是趋向于0.5.

例: normalInt(0,100,10000),结果接近于50,有可能出现的结果是49.12334

所以,如果代码是

var x = normalInt(0, canvas.width, 100),

y = normalInt(0, canvas.height, 100);

理论上,将正态分布在画布的中心周围.

3.如何能得到显示文字边缘

canvas里面能获得imagedata, 获取CanvasPixelArray接口为canvas.getContext('2d').getImageData(x, y, width, height);

数据类型为CanvasPixelArray. 它是像素矩阵的扁平表示方法:

0818b9ca8b590ca3270a3433284dd417.png

CanvasPixelArray包括 宽*高*4 个字节的数据, 索引范围从0到 (高*宽*4)-1.

要取得图片里一个[x,y]坐标的red颜色信息可以用以下方法

var redValueForPixel = ((y - 1) * (width * 4)) + ((x - 1) * 4);

0818b9ca8b590ca3270a3433284dd417.png

另:在处理图像的时候,如果有API可用的话,基本都是按这样的索引结构存储.

点击图片的rgba信息并将body background显示为该色

load 外部图权限需要提升

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

wordle.net的作者写的一些资料:

信息可视化wiki:

stackoverflow上的一些信息:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值