文章目录

该效果来自于:Funny JS

作者是因为知乎上的一个问题而写了这篇文章和这个程序,使用HTML5+JS进行开发,可以将任意图片转换成WWDC2014海报的样式:用色调如彩虹般绚丽的圆角矩形来拼成原图。

苹果 WWDC 2014 海报

我只想说,效果超乎想像。Canvas确实可以大有作为。

看官们就自行体验吧。

以下贴上所有代码:

HTML部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="toolbar">
<input type="button" value="苹果" id="apple">
<input type="button" value="微软" id="windows">
<input type="button" value="Twitter" id="twitter">
<input type="button" value="知乎" id="zhihu">
<input type="button" value="刘看山" id="liukanshan">
<input type="button" value="喵" id="cat">
<input type="button" value="苏莉安" id="sulian">
<span id="uploadspan">上传图片:<input type="file" id="localimg"/>
</span>
</div>
<div id="container">
<canvas id="image">
</canvas>
</div>

CSS部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
body {
margin: 0px;
}

#container {
width: 100%;
}

#toolbar {
background-color: #CCC;
width: 100%;
height: 32px;
font-size: 16px;
line-height: 32px;
margin: 0px;
}

#toolbar input {
font-size: 16px;
}

#toolbar input[type="button"] {
background-color: #99CCFF;
border: 1px solid #6699CC;
width: 64px;
height: 24px;
margin: 0px;
}

Javascript部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
var canvas; //画布
var ctx; //绘图内容
var blocksize = 14//单个图块的尺寸
var row, col//按图块尺寸算出的行列数
var drawing = false;

//页面加载
$(function () {
init();
});

//初始化
function init() {
canvas = document.getElementById("image");
if (!canvas.getContext) self.location = "/nohtml5.html"
else {
//初始化容器尺寸
var container = $("#container");
container.height($(window).height() - $("#toolbar").height() - 2);
canvas.width = container.width();
canvas.height = container.height();
ctx = canvas.getContext("2d");

var rainbowfill = ctx.createLinearGradient(0, 0, canvas.width, 0);//彩虹色填充
rainbowfill.addColorStop(0, "#92ea0e");
rainbowfill.addColorStop(0.25, "#03d9f5");
rainbowfill.addColorStop(0.5, "#8e49f7");
rainbowfill.addColorStop(0.75, "#fb9343");
rainbowfill.addColorStop(1, "#fdda06");
ctx.fillStyle = rainbowfill;

row = Math.floor(canvas.height / blocksize)
col = Math.floor(canvas.width / blocksize)

$("#toolbar :button").click(function () {
if (drawing) return;
drawing = true;
loadServerImg("wwdc2014/" + $(this).attr("id") + ".jpg");
})

if(typeof FileReader==='undefined'){
$("#uploadspan").hide();//不能使用本地文件接口则禁用
}else{
$("#localimg").change(function () {
var file = this.files[0];
if(!/image\/\w+/.test(file.type)){
alert("你选择的不是图片文件!");
return false;
}
var imgreader = new FileReader();
imgreader.readAsDataURL(file);
imgreader.onload = function(e){
loadServerImg(this.result);
}
});
}

loadServerImg("wwdc2014/apple.jpg");
}
}

//根据左上角坐标xy和尺寸size绘制单个圆角矩形
function roundRectanglePath(x, y, size, radius) {
var radius = Math.ceil(size / 4);
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + size - radius, y);
ctx.arc(x + size - radius, y + radius, radius, 3 * Math.PI / 2, 2 * Math.PI, false);
ctx.lineTo(x + size, y + size - radius);
ctx.arc(x + size - radius, y + size - radius, radius, 0, Math.PI / 2, false);
ctx.lineTo(x + radius, y + size);
ctx.arc(x + radius, y + size - radius, radius, Math.PI / 2, Math.PI, false);
ctx.lineTo(x, y + radius);
ctx.arc(x + radius, y + radius, radius, Math.PI, 3 * Math.PI / 2, false);
ctx.closePath();
ctx.fill();
}

//绘制一斜行方块
function drawBlocks(imgdata, maxgray, mingray, cursor) {
if (cursor >= col + row) {
drawing = false;
return;
}
var r , c;//起始行列
if (cursor < col) {
r = 0;
c = cursor;
}
else {
r = cursor - col + 1;
c = col - 1;
}
while (r < row && c >= 0) {
var gray = (imgdata[c][r] - mingray) / (maxgray - mingray);//以最大和最小灰度为界,计算出相对灰度
if (gray > 0.25) {//过浅的灰度不绘制
var pointsize = 2 * (Math.ceil(gray * blocksize / 2) - 1);//灰度决定尺寸
if (pointsize >= blocksize) pointsize = blocksize - 2;//防止超限
var x = c * blocksize + (blocksize - pointsize) / 2
var y = r * blocksize + (blocksize - pointsize) / 2
roundRectanglePath(x, y, pointsize)
}
r++;
c--;
}
setTimeout(function () {
drawBlocks(imgdata, maxgray, mingray, cursor + 1);
}, 7);//延时绘制下一排
}

//读取图片
function loadServerImg(src) {
var imageObj = new Image();
imageObj.onload = function () {
//生成离屏画布
var offcanvas = document.createElement('canvas');
var imgw, imgh;//缩放后的图片尺寸
if (col / row > imageObj.width / imageObj.height) {//如果画布比图片宽,则以高度为准进行放缩
imgw = Math.floor(imageObj.width / imageObj.height * row);
imgh = row;
}
else {//否则以宽度为准进行放缩
imgw = col;
imgh = Math.floor(imageObj.height / imageObj.width * col);
}
offcanvas.width = imgw;
offcanvas.height = imgh;
var offctx = offcanvas.getContext("2d");
offctx.drawImage(this, 0, 0, imgw, imgh);
var imgdata = offctx.getImageData(0, 0, imgw, imgh)
//返回数组初始化为全黑
var returndata = new Array()
for (var i = 0; i < col; i++) {
returndata[i] = new Array()
for (var j = 0; j < row; j++)
returndata[i][j] = 255;
}
//然后为中心部分赋值
var startcol = Math.floor((col - imgw) / 2)
var startrow = Math.floor((row - imgh) / 2)
var maxgray = 0, mingray = 255;//最大最小灰度值
for (var i = 0; i < imgw; i++) {
for (var j = 0; j < imgh; j++) {
var cursor = (i + j * imgw) * 4;
var gray = imgdata.data[cursor] * .3 + imgdata.data[cursor + 1] * .59 + imgdata.data[cursor + 2] * .11;//取灰度
if (gray > maxgray) maxgray = gray;
if (gray < mingray) mingray = gray;
returndata[startcol + i][startrow + j] = gray;
}
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBlocks(returndata, maxgray, mingray, 0);
};
imageObj.src = src;
}

//建立动画对象
function createAnime(callback) {
if (!callback) return null;
var anime = new Object();
anime.starttime//动画开始时间
anime.fps;//上一秒帧数
anime.secondstarttime;//当前秒开始时间(用于统计每秒的帧数)
anime.framestarttime;
anime.nextfps;
anime.status = "idle";
anime.start = function () {
if (this.status == "running") return;
this.status = "running";
if (this.status == "idle") this.starttime = new Date();//初次启动才计时
this.secondstarttime = new Date();
this.framestarttime = new Date();
this.fps = 0;//上一秒帧数
this.nextfps = 0;

_animeFrame(this, callback);
};
anime.stop = function () {
this.status = "stopped";
};
return anime;
}

function _animeFrame(anime, callback) {
anime.nextfps++;//累加帧数
var currenttime = new Date()
if (currenttime - anime.secondstarttime >= 1000) {//满1秒则为帧数赋值,清空计数
anime.fps = anime.nextfps;
anime.nextfps = 0;
anime.secondstarttime = currenttime;
}
var framespan = currenttime - anime.framestarttime;//计算两帧间隔
anime.framestarttime = currenttime;

callback(framespan, anime.fps);
if (anime && anime.status == "running") {
requestAnimationFrame(function () {
_animeFrame(anime, callback);
}
);
}
}


//requestAnimationFrame动画方法的兼容性
(function () {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // Webkit中此取消方法的名字变了
window[vendors[x] + 'CancelRequestAnimationFrame'];
}

if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}
}());

♦ 本文固定连接:https://www.gsgundam.com/archive/2015-02-04-wwdc-2014-post-effect/

♦ 转载请注明:GSGundam 2015年02月04日发布于 GSGUNDAM砍柴工

♦ 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

♦ 原创不易,如果页面上有适合你的广告,不妨点击一下看看,支持作者。(广告来源:Google Adsense)

♦ 本文总阅读量