用canvas 实现个图片三角化(LOW POLY)效果


Posted in Javascript onFebruary 18, 2016

之前无意中看到Ovilia 用threejs做了个LOW POLY,也就是图片平面三角化的效果,觉得很惊艳,然后就自己花了点时间尝试了一下。

我是没怎么用过threejs,所以就直接用canvas的2d绘图API来做,因为感觉似乎这效果也用不上threejs。

直接上demo先:http://whxaxes.github.io/canvas-test/src/Funny-demo/lowpoly/index.html (也可以在移动端看,不过因为计算量比较大,移动设备计算起来会比PC要多花些时间。)

做这种效果主要需要把图片三角化,以及对图片进行边缘化检测。这两个,第一个用到的delaunay三角化算法,第二个用到的sobel边缘检测算法。听起来偷挺高大上的,索性两个算法都有相应的开源组件可以直接拿来用:ironwallaby的delaunay组件 以及 Miguel Mota的sobel组件。

这两个算法sobel还好一点,delaunay就有点复杂了,待日后可以研究一下。不过目前只为做出个效果的话,还是可以用这些组件的。

两个最重要的组件都有了,剩下的事就很简单了:

先将图片绘制到canvas上:

canvas.width = (img.width > 800) ? 800 : img.width;
canvas.height = img.height * canvas.width/img.width;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

然后获取到canvas的imgData,再通过sobel计算返回新的imgData

var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var newImgData = Sobel(imgData);

如果我们把newImgData放到canvas上,就会发现,彩色图片变成了这样的灰度图片:

用canvas 实现个图片三角化(LOW POLY)效果

由于上面说的那个Sobel组件不是很适合自己的用法,同时代码也有不恰当的地方,所以自己做了适当修改和优化,优化了循环方法,加快了运算速度,同时加入了回调函数。详见该项目github中的sobel.js文件

在Sobel方法中对imgData.data进行遍历的时候,会调用回调函数,在回调中把颜色值大于40(也就是灰度为rgb(40,40,40)以上的)的坐标点记录下来。然后随机获取一部分边缘点,再加入一些随机出来的坐标 以及 四个边角的坐标值。这样,我们就可以获取到我们需要的坐标点了

var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 收集色值大于40的边缘像素点
var collectors = [];
Sobel(imgData , function(value , x , y){
if(value > 40){collectors.push([x , y]);}
});
// 添加一些随机点
for(var i=0;i<300;i++){particles.push([Math.random()*canvas.width , Math.random()*canvas.height]);}
// 添加随机边缘点,数量为边缘点数量除于50
var length = ~~(collectors.length/50), random;
for(var l=0;l<length;l++){
random = (Math.random()*collectors.length)<<0;
particles.push(collectors[random]);
collectors.splice(random , 1);
}
// 添加四顶点坐标
particles.push([0,0] , [0,canvas.height] , [canvas.width,0] , [canvas.width,canvas.height]);

获取到坐标点后,就可以通过delaunay组件计算,获取到拍好次序的三角坐标数组,对这些数组里的点进行连线,就可以出现这样的效果:

用canvas 实现个图片三角化(LOW POLY)效果

当然,我们要的效果不是连线,而是对所有三角形进行颜色填充,也就是获取三角形的三个坐标,然后计算出中心点的坐标,再根据中心点坐标在imgData里获取到相应的rgb的颜色值,然后填充到三角区域就可以了:

// 使用delaunay三角化获取三角坐标
var triangles = Delaunay.triangulate(particles);
var x1,x2,x3,y1,y2,y3,cx,cy;
for(var i=0;i < triangles.length; i+=3) {
x1 = particles[triangles[i]][0];
x2 = particles[triangles[i+1]][0];
x3 = particles[triangles[i+2]][0];
y1 = particles[triangles[i]][1];
y2 = particles[triangles[i+1]][1];
y3 = particles[triangles[i+2]][1];
// 获取三角形中心点坐标
cx = ~~((x1 + x2 + x3) / 3);
cy = ~~((y1 + y2 + y3) / 3);
// 获取中心点坐标的颜色值
index = (cy*imgData.width + cx)*4;
var color_r = imgData.data[index];
var color_g = imgData.data[index+1];
var color_b = imgData.data[index+2];
// 绘制三角形
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.fillStyle = "rgba("+color_r+","+color_g+","+color_b+",1)";
ctx.fill();
ctx.restore();
}

上面有一点要注意,获取到的中心点坐标一定要取整,才能够获取到正确的颜色参数,如果想着不取整,而是在获取rgb索引的时候再取整,获取到的颜色值就是错的。因为这样获取到的那个像素点就不是我们要的中心像素点。

颜色也获取到后,就是简单的连线,然后填充操作了,最后出来的效果就是:

用canvas 实现个图片三角化(LOW POLY)效果

用canvas 实现个图片三角化(LOW POLY)效果

以上内容给大家介绍了用canvas 实现个图片三角化(LOW POLY)效果 ,希望对大家有所帮助!

Javascript 相关文章推荐
判断多个元素(RADIO,CHECKBOX等)是否被选择的原理说明
Feb 18 Javascript
关于用Jquery的height()、width()计算动态插入的IMG标签的宽高的问题
Dec 08 Javascript
基于js disabled=&quot;false&quot;不起作用的解决办法
Jun 26 Javascript
JS delegate与live浅析
Dec 21 Javascript
js 获取时间间隔实现代码
May 12 Javascript
在JavaScript中操作时间之getUTCDate()方法的使用
Jun 10 Javascript
jquery左右全屏大尺寸多图滑动效果代码分享
Aug 28 Javascript
利用JavaScript实现拖拽改变元素大小
Dec 14 Javascript
微信小程序(六):列表上拉加载下拉刷新示例
Jan 13 Javascript
Bootstrap jquery.twbsPagination.js动态页码分页实例代码
Feb 20 Javascript
JavaScript requestAnimationFrame动画详解
Sep 14 Javascript
vue获取当前激活路由的方法
Mar 17 Javascript
Js实现简单的小球运动特效
Feb 18 #Javascript
JavaScript与jQuery实现的闪烁输入效果
Feb 18 #Javascript
js实现简单的省市县三级联动效果实例
Feb 18 #Javascript
XML、HTML、CSS与JS的区别整理
Feb 18 #Javascript
jQuery插件实现适用于移动端的地址选择器
Feb 18 #Javascript
AngularJS 2.0新特性有哪些
Feb 18 #Javascript
JavaScript+canvas实现七色板效果实例
Feb 18 #Javascript
You might like
PHP 输出缓存详解
2009/06/20 PHP
关于PHP结束标签的使用细节探讨及联想
2013/03/04 PHP
PHP解决URL中文GBK乱码问题的两种方法
2014/06/03 PHP
ecshop后台编辑器替换成ueditor编辑器
2015/03/03 PHP
PHP 开发者该知道的 5 个 Composer 小技巧
2016/02/03 PHP
浅谈PHP检查数组中是否存在某个值 in_array 函数
2016/06/13 PHP
PHP简单获取网站百度搜索和搜狗搜索收录量的方法
2016/08/23 PHP
微信公众平台开发教程⑥ 微信开发集成类的使用图文详解
2019/04/10 PHP
JavaScript的parseInt 进制问题
2009/05/07 Javascript
javascript获取xml节点的最大值(实现代码)
2013/12/11 Javascript
JQuery EasyUI 加载两次url的原因分析及解决方案
2014/08/18 Javascript
基于jquery固定于顶部的导航响应浏览器滚动条事件
2014/11/02 Javascript
Javascript控制input输入时间格式的方法
2015/01/28 Javascript
Bootstrap 手风琴菜单的实现代码
2017/01/20 Javascript
BackBone及其实例探究_动力节点Java学院整理
2017/07/14 Javascript
mui框架移动开发初体验详解
2017/10/11 Javascript
vue中实现图片和文件上传的示例代码
2018/03/16 Javascript
详解浏览器缓存和webpack缓存配置
2018/07/06 Javascript
angularjs手动识别字符串中的换行符方法
2018/10/02 Javascript
微信小程序生成分享海报方法(附带二维码生成)
2019/03/29 Javascript
Angular7中创建组件/自定义指令/管道的方法实例详解
2019/04/02 Javascript
基于Fixed定位的框选功能的实现代码
2019/05/13 Javascript
微信小程序实现点击按钮后修改颜色
2019/12/05 Javascript
JS实现音乐导航特效
2020/01/06 Javascript
[02:54]DOTA2英雄基础教程 暗影牧师戴泽
2013/12/05 DOTA
[01:09:01]完美世界DOTA2联赛循环赛 Magma vs PXG BO2第一场 10.28
2020/10/28 DOTA
pandas使用get_dummies进行one-hot编码的方法
2018/07/10 Python
Python3内置模块random随机方法小结
2019/07/13 Python
localstorage和sessionstorage使用记录(推荐)
2017/05/23 HTML / CSS
非常详细的C#面试题集
2016/07/13 面试题
PHP引擎php.ini参数优化深入讲解
2021/03/24 PHP
爱护公物标语
2014/06/24 职场文书
学生逃课检讨书
2015/02/17 职场文书
党员带头倡议书
2015/04/29 职场文书
文书工作总结(范文)
2019/07/11 职场文书
分享一个vue实现的记事本功能案例
2022/04/11 Vue.js