Libx

Generate an image by canvas

Word count: 1,442Reading time: 6 min
2017/06/19 Share

嗯,很久没写东西了,是的,我们考完了试,这个辣鸡小项目基本上也算是完成了,按照惯例,对于这个垃圾小项目来总结点东西。

项目的 Github 地址在这里 ,在线预览地址在这里

因为在学习Canvas就想做些有意思的东西

这是一个类似于网上流传的很多的那种输入汉字然后自动生成表情包的小东西,然后就想要来试一下

最终将实现的功能:

最终实现的功能大概有这些:

  • 用户可以自由上传图片,自由编辑文字内容,并选择插入文字的位置
  • 下载生成的图片
  • 原笔迹的书写
  • 图片下载寒酸的裁剪功能
  • 几种图片滤镜:黑白,负片以及更多

上传图片

上传图片在这里使用的是HTML5中的filereader来实现的,将图片以DataURL的形式读入页面,在上传前通过输入缩放倍数来选择大小 后期考虑引入滑块式的调整各种参数,直接输入的方法真的是。。

具体的代码实现如下:

function readAsDataURL(){
if (!document.getElementById('read_pic_but').className)
{document.getElementById('read_pic_but').className+='button';}
else{document.getElementById('read_pic_but').className=""}
console.log(document.getElementById('read_pic_but').className)
//检验是否为图像文件
var file = document.getElementById("uppic").files[0];
if(!/image\/\w+/.test(file.type)){
alert('仅支持上传图片');
return false;
}
var reader = new FileReader();
//将文件以Data URL形式读入页面
reader.readAsDataURL(file);
reader.onload=function(e){
var picture=this.result;
var pic=new Image();
pic.src=picture;
//调整图片的大小
var scal_control=document.getElementById('_scale').value;
if (scal_control!="")
{ ctx.scale(scal_control,scal_control);}
if (scal_control="")
{
scal_control= 1;
ctx.scale(scal_control,scal_control);
};
console.log(scal_control);
ctx.drawImage(pic,0,0)
return pic;
}
}

代码写的有点烂,以后再看一下有没有重构的价值,如果有时间的话还是希望把代码重构一下

下载图片

下载图片时是可以选择保存图片的格式的,但是考虑到在使用时的繁琐,就直接设置为了jpg格式。 在canvas中保存图片是使用的toDataURL来做的。在保存的时候比较关键的一个就是要将MIME类型改为image/object-stream。得到的图片data:image/png;base64需要更改一下

var fixtype = function (type) {
type = type.toLocaleLowerCase().replace(/jpg/i, 'jpeg');
var r = type.match(/png|jpeg|bmp|gif/)[0];
return 'image/' + r;
}
imgdata = imgdata.replace(fixtype(type), 'image/octet-stream')

图片处理

说是图片处理,我都不好意思了hhh

  • 添加文字:使用是canvas中自有的渲染文字的方法,可以选择字体颜色 取色器使用getImageData做,得到的数据是一个数组,每四位分别表示一个像素点的RGBA数据。取到该像素点的数据,简单的字符处理就可得到可用的颜色数据。 这里就只是把取色部分的代码写一下吧:
function pick(event) {
var x = event.offsetX;
var y = event.offsetY;
var pixel = ctx2.getImageData(x, y, 1, 1);
var data = pixel.data;
var rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + (data[3] / 255) + ')';
color.style.background = rgba;
color.textContent = rgba;
console.log(rgba,x,y)
ctx.fillStyle=rgba;
ctx.strokeStyle=rgba;
}
  • 原笔迹书写:监听鼠标的move up down 使用了line_X,line_Y,line_N三个数组分别储存的轨迹的X Y坐标,和鼠标是否按下的标志信息,当鼠标按下并移动时分别向数组X,Y ,N push该点坐标及标识位信息,然后整个遍历一次数组,标志位为1时lineTo(X,Y),标志位为0时移动坐标,进行下一笔画的渲染。突然发现这样遍历成本很高,回头改改.代码在这里:
var linex = new Array();
var liney = new Array();
var linen = new Array();
var lastX = -1;
var lastY = -1;
var flag = 0;
function line_drawer() {
canvas.addEventListener('mousemove', onMouseMove, false);
canvas.addEventListener('mousedown', onMouseDown, false);
canvas.addEventListener('mouseup', onMouseUp, false);
function onMouseMove(evt) {
if (flag == 1) {
linex.push(evt.layerX);
liney.push(evt.layerY);
linen.push(1);
ctx.beginPath();
ctx.lineWidth = 1 + Math.random() * 5;
for (var i=1;i<linex.length;i++) {
lastX = linex[i];
lastY = liney[i];
if (linen[i] == 0) {
ctx.moveTo(lastX,lastY);
} else {
ctx.lineTo(lastX,lastY);
}
}
ctx.stroke();
} }
function onMouseDown(evt) {
flag = 1;
linex.push(evt.layerX);
liney.push(evt.layerY);
linen.push(0);
}
function onMouseUp(evt) {
flag = 0;
linex.push(evt.layerX);
liney.push(evt.layerY);
linen.push(0);
}
}

几种辣鸡滤镜

很简单的滤镜功能:

  • 负片:取到图片的数据,循环一次数组,分别将R位 G位 B位数据被255减即可。代码:
function negative(){
for(var j=0;j<data_length;j+=4){
data0[j]=255-data0[j];
data0[j+1]=255-data0[j+1];
data0[j+2]=255-data0[j+2];
}
ctx.putImageData(imageData0, 0, 0);
console.log(imageData0);
}
  • 黑白:同样拿到数据,步阶为4,将r、g、b位求平均值,然后rgb均等该改平均值即可。 代码:

    function grayscale(id) {
    for (var i = 0; i < data_length; i += 4) {
    var avg = (data0[i] + data0[i + 1] + data0[i + 2]) / 3;
    data0[i] = avg; // red
    data0[i + 1] = avg; // green
    data0[i + 2] = avg; // blue
    }
    ctx.putImageData(imageData0, 0, 0);
    console.log(imageData0);
    };
  • 高斯模糊:这里引入了一个StackBlur.js,自己写有点应付不来。。 大概就是这些吧。

学到了什么?

技术方面的不再说,说一些软的东西吧。 一个完整项目中的一些命名规则,变量的处理,包括全局和私有变量选择,函数的处理,如何解决函数间的通信等 另外,在整个项目开始之前的大概的规划,就算是练手的小项目,大致要做成什么样子也要有一个底。不然在写的时候很容易越写越乱。 另外,代码规范!这个东西并未严格的按照家园的前端代码规范来写,因为觉得只是练手吧,直到现在开始接触Vue 工作室要求使用Eslint来规范代码,真的很难受。。这个东西还是从开始就养好习惯比较好。不要因为追求速度而忽视了这些‘软实力’。 就这些吧~

CATALOG
  1. 1. 最终将实现的功能:
    1. 1.1. 上传图片
    2. 1.2. 下载图片
    3. 1.3. 图片处理
    4. 1.4. 几种辣鸡滤镜
  • 学到了什么?