分析需求

首先我们要实现的文字云效果如下:

由图可知,该文字云的效果是,一个大标签文字在区域中心,其它小标签文字围绕这个大标签文字。
其中,这些文字有随机的颜色。
除了大标签文字,其它标签文字大小也随机。
然后,围绕这个效果呢,想象一下火影忍者的轮回眼

其实就像一颗小石头扔向湖面,泛起阵阵涟漪(圆圈)向外扩散。

之后,文字之间不能交叉,也就是说不能碰撞,那就是说不能重叠。
好了,让我们撸起袖子

撸函数

由以上的分析,需要撸一个获取随机颜色的值函数。

function getRandomColor(){var arr = [0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'];var color = "#";for(i=0;i<6;i++){var c = parseInt(Math.random()*16);c = arr[c];color = color + c;}return color;
}

再撸一个获取随机的文字大小函数。

function getRandomFontSize(){var arr = [28,30,34,40];var res = [];for (var i = 0, len = arr.length; i < len; i++) {var j = Math.floor(Math.random() * arr.length);res[i] = arr[j];arr.splice(j, 1);}return res[0];
}

以上两个函数代码简单粗暴,不再说明。
既然最大的标签文字待在区域中心,那么获取区域的中心点坐标函数必不可少。

function getCenterPoint(DOMElement){var rect = DOMElement.getBoundingClientRect();var rectTop = rect.top;var rectLeft = rect.left;var ngx = Math.ceil(rect.width);var ngy = Math.ceil(rect.height);var center = [(ngx / 2) + rectLeft, (ngy / 2) + rectTop];return center;
}

一句话讲清,长宽取各一半再加上区域自己的坐标即可。
接下来就是围绕这个效果对应的函数,

function getPointsAtRadius(radius, center ,offsetY, multiple){var T = radius * 8;var t = T;var points = [];var offsetY = offsetY || 1;var multiple = multiple || 30;if (radius === 0) {points.push([center[0], center[1]]);}while (t) {points.push([center[0] + (radius*multiple) * Math.cos( (t * 2 * Math.PI )/ T ),center[1] + (radius*multiple) * Math.sin( (t * 2 * Math.PI )/ T ) * offsetY,]);t = t - 1;}return points;
}

初中学的三角函数派上用场,它用来获取圆上点的横坐标和纵坐标。
这里除了半径(radius)和圆中心坐标(center)两个必要参数,还加上了Y轴偏移(offsetY),和单位距离(multiple)参数。
其中Y轴偏移(offsetY)可用来缩小或扩大选取的Y坐标,从而改变生成的文字云形状。
单位距离(multiple)参数,是确定以多少像素作为一个半径单位。
这年代,屏幕分辨率都很大,不可能以单个像素进行画圈圈吧。
然后我们也没必要拿到圆上的每个点的坐标。
那我们拿圆上多少个坐标比较合适?
文字标签是矩形,一个矩形可被八个矩形直接包围。间接包围n8个矩形。
故取n
8个坐标即可。

接下来是判断两个矩形是否相交

function isCorssRect(array1, array2){var Xa1 = array1[0][0];var Ya1 = array1[0][1];var Xa2 = array1[1][0];var Ya2 = array1[1][1];var Xb1 = array2[0][0];var Yb1 = array2[0][1];var Xb2 = array2[1][0];var Yb2 = array2[1][1];var Xc1 = Math.max(Xa1,Xb1);var Yc1 = Math.max(Ya1,Yb1);var Xc2 = Math.min(Xa2,Xb2);var Yc2 = Math.min(Ya2,Yb2);return (Xc1 <= Xc2 && Yc1 <= Yc2);
}

首先一个矩形可由左上角坐标和右下角坐标来定义。
那么两个矩形相交,则表明两个矩形的左上角坐标最大值 要小于等于 两个矩形的右下角坐标最小值。
请看图想象。

撸业务

有了以上几个函数,我们就可以开始构思业务逻辑。
假设输入的数据是一个数组,比如["紅樓夢","賈寶玉","林黛玉","薛寶釵","王熙鳳","李紈","賈元春","賈迎春","賈探春","賈惜春","秦可卿","賈巧姐","史湘雲","妙玉","賈政","賈赦","賈璉","賈珍","賈環","賈母","王夫人","薛姨媽","尤氏","平兒","鴛鴦","襲人","晴雯","香菱","紫鵑","麝月","小紅","金釧","甄士隱","賈雨村"]

  1. 我们要依次取出一个词,并且计算这个词的宽高。
  2. 通过围绕函数获取将要放置的坐标。
  3. 通过词的宽高 和 将要放置的坐标,可以得到这个词的左上角坐标和右下角坐标信息。
  4. 然后跟已画上去的词云左上角坐标,右下角坐标进行比较,看两个矩形是否相交
  5. 不相交,则画上去,并把它的左上角坐标,右下角坐标信息进行存储。
  6. 相交,则回到第2步,获取下一个将要放置的坐标。

具体代码如下:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>草珊瑚的文字云</title><style>body{margin: 0;}#html-canvas{/*position: absolute;left:10px;top: 100px;*/width: 1415px;height: 796px;background-color: rgb(240, 240, 240);position: relative;}</style>
</head>
<body><div id="html-canvas"></div>
</body>
<script>
/*** 获取画布的中心点坐标@method getCenterPoint@param {HTMLDOMElement} DOMElement 通常是一个div@return {array} [x, y] 包含中心点坐标的数组*/
function getCenterPoint(DOMElement){var rect = DOMElement.getBoundingClientRect();var rectTop = rect.top;var rectLeft = rect.left;var ngx = Math.ceil(rectLeft + rect.width);var ngy = Math.ceil(rectTop + rect.height);var center = [(ngx / 2) + rectLeft, (ngy / 2) + rectTop];console.log('rect', rect);return center;
}
// var tagCanvas = document.getElementById('html-canvas');
// console.log("所选DOM的中心点为",getCenterPoint(tagCanvas));// 获取最大半径
function getMaxRadius(DOMElement, cellSpace){var cellSpace = cellSpace || 1;var rect = DOMElement.getBoundingClientRect();var ngx = Math.ceil(rect.width / cellSpace);var ngy = Math.ceil(rect.height / cellSpace);var maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy));return maxRadius;
}/*** 获取圆上每个点的坐标@method getCenterPoint@param {int} radius 半径@param {array} center 类似[11,22]中心点@param {int} offsetY y坐标的偏移位置,默认为1@return {array} [x, y] 圆上坐标的数组*/
function getPointsAtRadius(radius, center ,offsetY, multiple){// 因为像素是一个正方形,一个正方形的四周有8块正方形可包围。var T = radius * 8;var t = T;var points = [];var offsetY = offsetY || 1;var multiple = multiple || 30;if (radius === 0) {points.push([center[0], center[1]]);}while (t) {// 参考http://www.cnblogs.com/xieon1986/archive/2013/01/28/2880367.html// 圆上每个点的// Y坐标=圆心y坐标 + Math.sin(2*Math.PI / 360) * 半径// X坐标=圆心x坐标 + Math.cos(2*Math.PI / 360) * 半径 // 弧度=(2*PI/360)*角度// 基于圆心的x坐标 X坐标=圆心x坐标 + Math.sin((2*Math.PI / 360) * 1) * 半径 // 这里的T同360°的意义points.push([center[0] + (radius*multiple) * Math.cos( (t * 2 * Math.PI )/ T ),center[1] + (radius*multiple) * Math.sin( (t * 2 * Math.PI )/ T ) * offsetY,]);t = t - 1;}return points;
}
// console.log('圆上各点坐标', getPointsAtRadius(1,[731.5, 475]));/*** 获取文本的宽高@method getCenterPoint@param {HTMLDOMElement} DOMElement 通常是一个div@return {array} [x, y] 包含中心点坐标的数组*/
function getTextInfo(word, userCSS){var fontSize = getRandomFontSize();if(userCSS){fontSize = userCSS.fontSize;}// 通过canvas的API来获取文本的各种信息var fcanvas = document.createElement('canvas');var fctx = fcanvas.getContext('2d');fctx.font = 'normal ' + fontSize + 'px Hiragino Mincho Pro, serif';// 通过canvas的measureText方法获取文本 像素级别的宽度// http://www.w3school.com.cn/tags/canvas_measuretext.aspvar fw = fctx.measureText(word).width;// 通过字体大小获取高度var fh = fontSize;return {width: Math.ceil(fw),height: fh,word: word,fontSize: fontSize}
}
// console.log('你输入的文字长宽为', getTextInfo('你好'));/*** 判断两个矩形是否相交* 参考:http://www.cnblogs.com/avril/archive/2012/11/13/2767577.html* http://www.cnblogs.com/avril/archive/2013/04/01/2993875.html@method isCorssRect@param {array} @param {array} @return {bool} true表示有重叠,false表示没有重叠*/
function isCorssRect(array1, array2){var Xa1 = array1[0][0];var Ya1 = array1[0][1];var Xa2 = array1[1][0];var Ya2 = array1[1][1];var Xb1 = array2[0][0];var Yb1 = array2[0][1];var Xb2 = array2[1][0];var Yb2 = array2[1][1];var Xc1 = Math.max(Xa1,Xb1);var Yc1 = Math.max(Ya1,Yb1);var Xc2 = Math.min(Xa2,Xb2);var Yc2 = Math.min(Ya2,Yb2);return (Xc1 <= Xc2 && Yc1 <= Yc2);
}//获取随机颜色的值
function getRandomColor(){var arr = [0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'];var color = "#";for(i=0;i<6;i++){var c = parseInt(Math.random()*16);c = arr[c];color = color + c;}return color;
}// 获取随机文字的大小
function getRandomFontSize(){var arr = [28,30,34,40];var res = [];for (var i = 0, len = arr.length; i < len; i++) {var j = Math.floor(Math.random() * arr.length);res[i] = arr[j];arr.splice(j, 1);}return res[0];
}function drawText(position, textInfo, canvas ){var span = document.createElement('span');var styleRules = {'position': 'absolute','display': 'block','font': 'normal' + ' '+textInfo.fontSize+'px ' + 'Hiragino Mincho Pro, serif','left': position[0] + 'px','top': position[1] + 'px','width': textInfo.width + 'px','height': textInfo.height + 'px','lineHeight': 1,'color': getRandomColor(),};span.textContent = textInfo.word;for (var cssProp in styleRules) {span.style[cssProp] = styleRules[cssProp];}canvas.appendChild(span);
}/*** 获取文本的左上右下坐标,@param {array} topLeft 类似@return {object} textInfo 包含中心点坐标的数组*/
function getTopLeft(topLeft, textInfo){var left1 = topLeft[0];var top1 = topLeft[1];return [[left1, top1],[left1+ textInfo[0], top1 + textInfo[1]]];
}// 获取可安置的坐标
function getDrawPosition(textInfo, maxRadius, center, cellSpace ,drawArray){var textInfo_width = textInfo.width;var textInfo_height = textInfo.height;var cellSpace = cellSpace || 10;for(var i=0; i<maxRadius; i++){var points = getPointsAtRadius(i, center, 0.64, cellSpace);pointsLoop:for(var j=0; j<points.length; j++){var topLeft = getTopLeft(points[j], [textInfo_width, textInfo_height]);// 是否和已存的文字碰撞for(var z =0 ; z< drawArray.length; z++){if(isCorssRect(topLeft, drawArray[z])){continue pointsLoop;}}drawArray.push(topLeft);return points[j];}}return null;
}function start(){var tagCanvas = document.getElementById('html-canvas');var center = getCenterPoint(tagCanvas);var cellSpace = 20;var maxRadius = getMaxRadius(tagCanvas, cellSpace);var data = ["紅樓夢","賈寶玉","林黛玉","薛寶釵","王熙鳳","李紈","賈元春","賈迎春","賈探春","賈惜春","秦可卿","賈巧姐","史湘雲","妙玉","賈政","賈赦","賈璉","賈珍","賈環","賈母","王夫人","薛姨媽","尤氏","平兒","鴛鴦","襲人","晴雯","香菱","紫鵑","麝月","小紅","金釧","甄士隱","賈雨村"];var drawArray = [];var tempDrawPositionArray = [];for(var i =0; i< data.length; i++){var dataItem = data[i];var textInfo = getTextInfo(dataItem);var drawPosition = null;if(i != 0){drawPosition = getDrawPosition(textInfo, maxRadius, center, cellSpace, drawArray);}else{textInfo = getTextInfo(dataItem, {fontSize:60});drawPosition = getDrawPosition(textInfo, maxRadius,[center[0]-(textInfo.width/2), center[1]-(textInfo.height/2)], cellSpace, drawArray);}if(drawPosition){tempDrawPositionArray.push([drawPosition, textInfo, tagCanvas]);}      }var timer = setInterval(function(){var textItem = tempDrawPositionArray.shift();if(textItem){drawText(textItem[0], textItem[1], textItem[2]);}else{clearInterval(timer);}}, 0);}
start();
</script>
</html>

本文实现思路和实验数据参考了wordcloud2,
并重写其百分之九十的代码。
意在理解其思路。

实现一个简单的文字云相关推荐

  1. html特效 wpf,利用WPF实现一个简单的文字粒子闪烁动画特效

    利用WPF实现一个简单的文字粒子闪烁动画特效 发布时间:2020-11-06 16:04:19 来源:亿速云 阅读:124 作者:Leah 本篇文章给大家分享的是有关利用WPF实现一个简单的文字粒子闪 ...

  2. 利用DW制作一个简单的文字logo

    这是利用DW设计一个简单的文字logo方案 1 建立一个HTML5 2用strong标签写出文字Google 3在style标签下利用font size定义字号 4根据CSS设定指定参数 效果如下:

  3. 使用Dreamweaver/利用HTML5/CSS/制作一个简单的文字logo

    一.制作一个简单的logo 1. 结构与样式分析 首先我们根据logo的图片分析logo的效果,该logo由6个字母组成.在使用"数码测色计"测出logo的颜色,这里我们测出log ...

  4. python自动回复qq消息_基于python使用qqbot接入qq做一个简单的文字消息自动回复

    qqbot是一个免费开源的基于smartqq的python插件,如果默认安装有pip,则可以直接在命令行下执行:pip install qqbot安装qqbot,安装成功后可以在命令行输入qqbot ...

  5. 使用C#编写一个简单的文字小游戏

    如果在下文中有不清楚的属性字段,方法 可以选中它按住Ctrl然后俩下双击就可以跳转到他的原始位置.```csharp using System; using System.Collections.Ge ...

  6. 用python写一个简单的文字识别器GUI

    效果图 使用方法 提取文本的步骤: 先选择普通识图还是高精度识图, 默认是普通识图 然后 方法1: 点击选择图片,然后选中图片就会自动识别图片并提取文字(gif格式的不可以提取文字) 方法2: 手动粘 ...

  7. 如何使用python实现一个优雅的词云?(超详细)

     什么是词云 "词云"就是对网络文本中出现频率较高的"关键词"予以视觉上的突出,形成"关键词云层"或"关键词渲染". 从 ...

  8. Tableau必知必会之如何快速制作 词云(文字云)

    作品中,为了让电影投资商清楚的知道自己需要什么样的演员.作者把数据中的所有演员,从票房号召力和粉丝关注度两个维度进行分析. 人名的字体越大,代表他(她)的票房号召力越大: 人名的颜色越鲜艳,代表他(她 ...

  9. 利用Python做一个简单的对战小游戏

    利用Python做一个简单的文字对战小游戏 一.游戏介绍 1.大体介绍:文字版的对战小游戏,可以利用Python随机生成两个角色,角色带有各自的血量和攻击值两个指标.两人在对战时同时攻击对方,同时造成 ...

最新文章

  1. IDEA配置GitHub和Gitee
  2. Mysql笔记2-----重要小点
  3. Spring 3.1 Environment Profiles--转载
  4. Spring IOC实现
  5. 【安全漏洞】Easy代码审计
  6. mnist数据集彩色图像_使用MNIST数据集构建多类图像分类模型。
  7. py获取前端的参数_鹅厂技术说 | 深入理解前端性能监控
  8. 如何用Camtasia进行内容补充?
  9. 根据id查询数据(向前台返回json格式的数据)
  10. 啥?喝着阔落吃着西瓜就把Promise手写出来了???
  11. CentOS 7中添加一个新用户并授权(转载)
  12. 一步步学习SPD2010--第十二章节--理解可用性和可接入性(5)--测试可用性
  13. Udacity数据分析(入门)-TMDb电影数据集探索
  14. 本地调试微信接口花生壳等域名被限制拉黑
  15. HTML动画能在手机播放吗,如何使动画在手机中屏幕适配
  16. 教你如何拥有好看的CMD界面 如何美化Windows Terminal
  17. 【网络】TOE、RDMA、smartNIC 是什么和区别|DPU
  18. elasticsearch和elasticsearch-sql安装教程
  19. linux服务器校对和手动修改时间
  20. mockjs的使用方法

热门文章

  1. 基于web的圆通快递物流管理系统
  2. Abp vNext 拓展Ids4-Claims用户信息
  3. 掌握python和js_js与python哪个更强大 javascript和python哪个好入门
  4. 40万年才能遇到外星人,是怎么算出来的?
  5. 内网和外网的区别 连接不同设备上网
  6. 修改CSDN博客的昵称
  7. Java基础学习——第六章 面向对象编程(下)
  8. 魔兽亡灵序曲《The Dawn》
  9. python 神经网络原理_神经网络工作原理
  10. java面向对象1-面向对象概念