灵感来自微博@bangbang93
大佬写了个记仇生成器,作为表情包大王的我不禁想学习一番。
然而菜鸡如我,写的还是有些不尽如人意,比如canvas图片跨域问题还是没有解决明白,图片大的话保存速度会很慢,代码有些地方略显冗余等。希望有大佬指教一下。
<html>
<head>
<title>文件上传
</title>
<style>
#bigImg{
width:
300px;
height:
239.31px;
}
#write{
width:
300px;
border:
none;
overflow-y:
visible;
font-size:
15px;
margin-top:
10px;
}
#canvas{
width:
300px;
height:
300px;
display:
none;
}
#form{
display:
none;
}
#sub{
margin-top:
20px;
}
<
/style>
</head>
<body>
<div
id=
"container"
>
</div>
<script>
var
jc =
new
jichou({
container:
"container",
//容器
initImg:
"",
//初始图片,不能跨域
initText:
"",
//初始文字,没有默认“请输入”
method:
"/upload",
//提交位置
canvasWidth:
300,
canvasHeight:
300,
canvasFont:
"normal normal normal 15px Microsoft YaHei UI",
fontSize:
12,
lineHeight:
20
})
//构造函数
function
jichou(
jcObject){
let
that =
this;
//默认文字
if(!
jcObject.
initText){
jcObject.
initText =
"请输入"
}
//获取id
this.
getId=
function(
id){
return
document.
getElementById(
id)
}
//填充内容
var
innerHTML =
"<img id = 'bigImg' src="+
jcObject.
initImg+
"><br />";
innerHTML +=
"<div id='write' contenteditable='true'/>"+
jcObject.
initText+
"</div>";
innerHTML +=
"<form action="+
jcObject.
method+
" method='post' enctype='multipart/form-data' id = 'form'>";
innerHTML +=
"<input type='file' name='image' id='image'>";
innerHTML +=
"<br />";
innerHTML +=
"<input type='hidden' name='base' id='base'>";
innerHTML +=
"</form>";
innerHTML +=
"<input type='button' value='保存' id='sub' />";
innerHTML +=
"<br/>";
innerHTML +=
"<canvas id='canvas' width='"+
jcObject.
canvasWidth+
"' height='"+
jcObject.
canvasHeight+
"'>";
innerHTML +=
"</canvas>";
this.
getId(
jcObject.
container).
innerHTML =
innerHTML;
//上传文件的input
var
image =
this.
getId(
'image');
//显示文件的img
var
bigImg =
this.
getId(
'bigImg');
//显示文字的div
var
write =
this.
getId(
'write');
//表单
var
form =
this.
getId(
'form');
//保存base64的input
var
base =
this.
getId(
'base');
//画布
var
canvas =
this.
getId(
'canvas');
var
context =
canvas.
getContext(
'2d');
//填充颜色
this.
drawCanvas =
function(
canvas,
left,
top,
width,
height,
color)
{
context.
fillStyle=
color;
context.
fillRect(
left,
top,
width,
height);
}
//清空画布
this.
clearCanvas =
function(
canvas,
left,
top,
width,
height,
color)
{
context.
clearRect(
left,
top,
width,
height);
//再填充一下
that.
drawCanvas(
canvas,
left,
top,
width,
height,
color);
}
//初始化
if(
jcObject.
initImg||
jcObject.
initText){
//初始化canvas
window.
onload=
function(){
that.
saveImg(
jcObject.
initImg);
that.
saveText(
jcObject.
initText);
}
}
//保存图片
this.
saveImg =
function(
imgUrl){
//创建image对象
var
img =
new
Image;
img.
src =
imgUrl;
img.
crossOrigin =
'anonymous';
img.
onload =
function(){
//画图
canvas.
height =
bigImg.
clientHeight +
write.
clientHeight +
10;
canvas.
style.
height =
bigImg.
clientHeight +
write.
clientHeight +
10 +
"px";
context.
drawImage(
img,
0,
0,
jcObject.
canvasWidth,
bigImg.
height);
}
context.
stroke();
}
//先填充白色防止透明色块
this.
drawCanvas(
canvas,
0,
0,
canvas.
width,
canvas.
height,
"#ffffff");
//canvas文字换行
this.
canvasTextAutoLine =
function(
str,
canvas,
initX,
initY,
lineHeight,
context){
var
context =
canvas.
getContext(
'2d');
context.
fillStyle=
"#000000";
var
lineWidth =
0;
var
canvasWidth =
canvas.
width;
var
lastSubStrIndex=
0;
for(
let
i=
0;
i<
str.
length;
i++){
lineWidth+=
context.
measureText(
str[
i]).
width;
if(
lineWidth>
canvasWidth-
initX){
//减去initX,防止边界出现的问题
context.
fillText(
str.
substring(
lastSubStrIndex,
i),
initX,
initY);
initY+=
lineHeight;
lineWidth=
0;
lastSubStrIndex=
i;
}
if(
i==
str.
length-
1){
context.
fillText(
str.
substring(
lastSubStrIndex,
i+
1),
initX,
initY);
}
}
}
//换图时修改路径
image.
onchange =
function(){
var
imgUrl =
window.
URL.
createObjectURL(
this.
files[
0]);
bigImg.
src =
imgUrl;
bigImg.
style.
height =
"auto";
image.
onload =
that.
saveImg(
imgUrl)
}
//点击图片换图
bigImg.
onclick =
function(){
image.
click();
}
//点击保存
sub.
onclick =
function(){
var
text =
write.
innerHTML;
that.
saveText(
text);
//提交表单
window.
setTimeout(
function(){
var
strDataURI =
canvas.
toDataURL();
base.
value =
strDataURI;
form.
submit();
},
1000)
}
//存文字
this.
saveText =
function(
text){
//文字样式
context.
font=
jcObject.
canvasFont;
//清空文字
that.
clearCanvas(
canvas,
0,
bigImg.
clientHeight+
jcObject.
fontSize,
canvas.
width,
write.
clientHeight+
jcObject.
lineHeight,
"#ffffff");
//文字换行
that.
canvasTextAutoLine(
text,
canvas,
0,
bigImg.
clientHeight+
jcObject.
lineHeight,
jcObject.
lineHeight,
content)
context.
stroke();
}
}
<
/script>
</body>
</html>
upload.js
var
express =
require(
'express');
var
app =
express();
var
fs =
require(
'fs');
var
router =
express.
Router();
//用于解析数据
var
bodyParser =
require(
'body-parser');
//上传文件中间件
var
multer =
require(
'multer');
app.
use(
express.
static(
'static'));
app.
use(
bodyParser.
urlencoded({
extended:
false}));
app.
use(
multer({
dest:
'/tmp/'}).
array(
'image'));
app.
get(
'/upload.html',
function(
req,
res){
res.
sendFile(
__dirname +
'/' +
"upload.html")
})
//设置跨域访问
app.
all(
'*',
function(
req,
res,
next) {
res.
header(
"Access-Control-Allow-Origin",
"*");
res.
header(
"Access-Control-Allow-Headers",
"X-Requested-With");
res.
header(
"Access-Control-Allow-Methods",
"PUT,POST,GET,DELETE,OPTIONS");
res.
header(
"X-Powered-By",
' 3.2.1');
res.
header(
"Content-Type",
"application/json;charset=utf-8");
next();
});
app.
post(
'/upload',
function(
req,
res){
var
imgData =
req.
body.
base.
replace(
/
^
data:image
\/
\w+
;base64,/,
'');
var
imageName =
imgData.
substr(
0,
10) +
Math.
random().
toString(
16);
console.
log(
imageName)
var
dataBuffer =
new
Buffer(
imgData,
'base64');
//写入文件
fs.
writeFile(
'D:/my/node/static/images/'+
imageName+
'.png',
dataBuffer,
function(
err){
if(
err){
res.
end(
err);
}
else{
res.
end(
JSON.
stringify(
'保存成功'));
}
});
})
var
server =
app.
listen(
3300,
function(
req,
res){
var
host =
server.
address().
address;
var
port =
server.
address().
port;
console.
log(
'running at http://%s:%s',
host,
port)
})