-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpixelated.js
115 lines (102 loc) · 3.93 KB
/
pixelated.js
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
/**
* Pixelated.js
*
* Automatically scale img tags using nearest-neighbor.
* Just add the "pixelated" attribute to your img.
*
* @author Maximillian Laumeister
* @link https://github.com/MaxLaumeister/pixelated-polyfill
* @license http://opensource.org/licenses/MIT MIT License
* @copyright 2020 Maximillian Laumeister
*/
(function() {
class PixelatedImage {
constructor(img, algo) {
this.img = img;
this.algo = algo;
this.originalImg = img.cloneNode(true);
if (img.complete) {
this.init();
} else {
img.onload = () => {
img.onload = null;
this.init();
};
}
}
init() {
const onready = () => {
// First try native
if (this.algo !== "xbr") this.img.style.imageRendering = "pixelated";
// Then, polyfill time
if (!CSS.supports("image-rendering", "pixelated") || this.algo === "xbr") {
this.img.crossOrigin = "anonymous";
// Lock dimensions
if (!this.img.getAttribute("width") && !this.img.getAttribute("height")) {
this.img.width = this.img.naturalWidth;
this.img.height = this.img.naturalHeight;
}
// Set up canvas
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext("2d");
// Size the canvas and draw
this.updateCanvas();
// Listen for size changes
new ResizeObserver(this.updateCanvas.bind(this)).observe(this.img);
}
}
if (this.algo === "xbr") {
XbrWasm.ready.then(() => {
this.xbr = new XbrWasm(this.img, 4);
onready();
});
} else {
onready();
}
}
async updateCanvas() {
// Debounce
if (!this.canvasDebounceTimeout) {
// Update canvas size
const dpr = window.devicePixelRatio || 1;
const w = this.img.clientWidth * dpr;
const h = this.img.clientHeight * dpr;
this.canvas.width = w;
this.canvas.height = h;
// Redraw
this.ctx.save();
this.ctx.imageSmoothingEnabled = false;
if (this.algo === "xbr") {
await this.xbr.draw();
this.ctx.drawImage(this.xbr.destCanvas, 0, 0, this.canvas.width, this.canvas.height);
}
else {
this.ctx.drawImage(this.originalImg, 0, 0, this.canvas.width, this.canvas.height);
}
this.ctx.restore();
// Draw canvas to img element
this.img.src = this.canvas.toDataURL();
// Set debounce
this.canvasDebounceTimeout = setTimeout(() => {
this.canvasDebounceTimeout = null;
}, 500);
// Set final timeout
clearTimeout(this.canvasFinalTimeout);
this.canvasFinalTimeout = setTimeout(this.updateCanvas.bind(this), 500);
}
}
}
function processImage(img) {
img.style.imageRendering = "pixelated";
new PixelatedImage(img, img.getAttribute("pixelated-algo"));
}
document.addEventListener("DOMContentLoaded", function() {
// Process existing images
const imgs = document.querySelectorAll("img[pixelated]");
imgs.forEach( img => {
if (img instanceof HTMLImageElement) {
processImage(img);
}
});
});
})();