Canvas怎么实现怀旧照片?

怀旧照片能唤起人们对于过去美好时光的回忆。无论是家庭聚会、旅行、还是成长过程中的点点滴滴,这些照片都能让我们重新体验那些珍贵的时刻。

画布上的一个超实用功能:toDataURL()方法。

你有没有想过将画布上的内容直接转换成图片?Canvas 提供了一个非常方便的方法叫做 toDataURL(),它可以把当前画布的内容转换成图片,可以实现怀旧照片的效果 。

这个方法有两个参数,一个是图片的格式,一个是图片的质量。默认的图片格式是 PNG,但如果想要 JPG、WebP、GIF 等其他格式,可以在这里指定。而图片质量,只有当图片格式是 JPG 或 WebP 时才需要,范围是 0 到 1。

toDataURL() 方法会返回一个 data URI,这是一个经过 base64 编码的字符串,可以直接在 HTML 中使用。比如,你可以把这个 data URI 赋值给 img 标签的 src 属性,网页上就会直接显示这个图像。

需要注意的是,toDataURL() 是画布的方法,不是画布上下文渲染对象的方法。另外,如果画布的高度或宽度是0,toDataURL() 会返回一个字符串 “data:”。最后,Chrome 浏览器还特别支持 “image/webp” 格式。

现在我们来通过一个例子实际操作一下。首先,我们需要一个已经绘制了内容的画布。然后,调用 toDataURL() 方法,将画布内容转换成图片。最后,将转换得到的 data URI 赋值给一个 img 标签的 src 属性,就可以在网页上看到这个图片了。

示例效果与源代码:

运行效果

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
<!DOCTYPE html>
<html>

<head>
<title>画布操作(toDataUrl)</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="图形系统开发实战:基础篇 示例">
<meta name="author" content="hjq">
<meta name="keywords" content="canvas,ladder,javascript">
<style>
img { margin-left: 10px }
</style>
</head>

<body style="overflow: hidden;">
<div style="margin:0 auto; width:900px;">
<div style="margin:0 auto; width:600px;">
<canvas id="canvas" width="800" height="600" style="border:solid 10px goldenrod;"></canvas>
</div>
<div id="toolbar" style="text-align:center;"></div>
</div>
</body>
<script>
// 从页面中获取画板对象
let canvas = document.getElementById('canvas');
// 从画板中获取“2D渲染上下文”对象
let ctx = canvas.getContext('2d');

// 加载并绘制图片
let image = new Image();
image.onload = function () {
canvas.width = image.width;
canvas.height = image.height;
canvas.parentElement.style.width = (canvas.width + 20)+ "px";
ctx.drawImage(image, 0, 0);
filterImage(sepia, "怀旧");
filterImage(grayscaleFilter, "灰度");
filterImage(brighten, "亮度调节");
filterImage(noise, "噪点调节");
filterImage(invert, "反色");
// 显示文字
drawText("原 图", ctx);
}
image.src = "/examples/canvas-qa/canvas_1b/images/girl.png";

/**
* 使用滤镜处理图像,并显示在html页面中
*/
function filterImage(filter, text) {
// 使用滤镜处理原canvas中的图像
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
filter(imageData)

// 创建临时画布,用于渲染经过滤镜处理后的图像
let tempCanvas = document.createElement("canvas");
tempCanvas.width = imageData.width;
tempCanvas.height = imageData.height;
let context = tempCanvas.getContext("2d");
context.putImageData(imageData, 0, 0);

// 显示文字
drawText(text, context);

// 将裁切的图像转换为图片
let base64url = tempCanvas.toDataURL();

// 在html中建立img对象,显示转换后的图片
let img = document.createElement("img");
img.width = 160;
img.src = base64url;
document.getElementById("toolbar").appendChild(img);

// 清除临时画布
tempCanvas.width = 0;
tempCanvas.height = 0;
tempCanvas = null;
}

/**
* 绘制文字
*/
function drawText(text, context) {
context.save();
context.font = "24px 黑体"
context.textAlign = "center";
context.strokeStyle="white";
context.lineWidth = 6;
context.strokeText(text, context.canvas.width/2, 30);
context.fillText(text, context.canvas.width/2, 30);
context.restore();
}

/**
* 灰度滤镜
*/
function grayscaleFilter(imageData) {
let data = imageData.data, brightness;
for (let i = 0; i < data.length; i += 4) {
brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
data[i] = brightness;
data[i + 1] = brightness;
data[i + 2] = brightness;
}
}

/**
* 亮度调节滤镜
*/
function brighten(imageData, options = {}) {
let data = imageData.data, brightness = (options.brightness || 0.2) * 255;
for (let i = 0; i < data.length; i += 4) {
data[i] += brightness;
data[i + 1] += brightness;
data[i + 2] += brightness;
}
};

/**
* 反色滤镜
*/
function invert(imageData) {
let data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i];
data[i + 1] = 255 - data[i + 1];
data[i + 2] = 255 - data[i + 2];
}
};

/**
* 怀旧滤镜
*/
function sepia(imageData) {
let data = imageData.data, i, r, g, b;
for (i = 0; i < data.length; i += 4) {
r = data[i + 0];
g = data[i + 1];
b = data[i + 2];
data[i + 0] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
}
}

/**
* 噪声调节滤镜
*/
function noise(imageData, options = {}) {
let noise = options.noise || 0.4;
let amount = noise * 255, data = imageData.data, half = amount / 2;
for (let i = 0; i < data.length; i += 4) {
data[i + 0] += half - 2 * half * Math.random();
data[i + 1] += half - 2 * half * Math.random();
data[i + 2] += half - 2 * half * Math.random();
}
}
</script>

</html>

尝试一下 »

在这个示例里,我们会看到如何使用 getImageData() 方法从画布中获取图像数据,并使用滤镜来处理这些数据。之后,我们用 putImageData() 方法将这些处理过的数据写回到一个新的画布中。最后,我们创建一个 img 对象来在网页上显示转换后的图像。