next-blurhash-previews 0.0.3-beta7 → 0.0.3-beta70
Sign up to get free protection for your applications and to get access to all the features.
- package/.vscode/settings.json +10 -0
- package/bin/generateBlurhash.js +38 -7
- package/bin/remark-plugin.js +5 -3
- package/components/ImageWithPreview.tsx +20 -10
- package/components/imagePreviewBootstrap.tsx +13 -6
- package/components/workerScript.ts +17 -0
- package/imagePreviewBootstrap.js +302 -0
- package/index.js +1 -1
- package/package.json +3 -3
- package/util/setBootstrap.js +19 -20
- package/{vite.config.ts → vite.wc.config.ts} +4 -7
- package/vite.worker.config.ts +18 -0
- package/.prettierrc +0 -3
- package/img1.png +0 -0
- package/img5.png +0 -0
- package/junk.js +0 -1
- package/next-runner/.eslintrc.json +0 -3
- package/next-runner/README.md +0 -34
- package/next-runner/blog-posts/post1.md +0 -303
- package/next-runner/blog-posts/post2.md +0 -10
- package/next-runner/blog-posts/post3.md +0 -10
- package/next-runner/blog-posts/post4.md +0 -10
- package/next-runner/next.config.js +0 -6
- package/next-runner/package-lock.json +0 -5080
- package/next-runner/package.json +0 -23
- package/next-runner/pages/_app.js +0 -11
- package/next-runner/pages/_document.js +0 -17
- package/next-runner/pages/index.js +0 -47
- package/next-runner/public/css-modules/loadingError.png +0 -0
- package/next-runner/public/css-modules/styledComponent.png +0 -0
- package/next-runner/public/css-modules/styledSass.png +0 -0
- package/next-runner/public/css-modules/unstyledComp.png +0 -0
- package/next-runner/public/dynamo-introduction/img1.png +0 -0
- package/next-runner/public/dynamo-introduction/img10.png +0 -0
- package/next-runner/public/dynamo-introduction/img11.png +0 -0
- package/next-runner/public/dynamo-introduction/img12.png +0 -0
- package/next-runner/public/dynamo-introduction/img13.png +0 -0
- package/next-runner/public/dynamo-introduction/img14.png +0 -0
- package/next-runner/public/dynamo-introduction/img15.png +0 -0
- package/next-runner/public/dynamo-introduction/img2.png +0 -0
- package/next-runner/public/dynamo-introduction/img3.png +0 -0
- package/next-runner/public/dynamo-introduction/img4.png +0 -0
- package/next-runner/public/dynamo-introduction/img5.png +0 -0
- package/next-runner/public/dynamo-introduction/img5a.png +0 -0
- package/next-runner/public/dynamo-introduction/img6.png +0 -0
- package/next-runner/public/dynamo-introduction/img7.png +0 -0
- package/next-runner/public/dynamo-introduction/img8.png +0 -0
- package/next-runner/public/dynamo-introduction/img9.png +0 -0
- package/next-runner/public/favicon.ico +0 -0
- package/next-runner/public/img1.png +0 -0
- package/next-runner/public/img5.png +0 -0
- package/next-runner/public/vercel.svg +0 -4
- package/next-runner/styles/Home.module.css +0 -3
- package/next-runner/styles/globals.css +0 -22
- package/next-runner/yalc.lock +0 -9
- package/next-runner/yarn.lock +0 -1650
- package/tsconfig.json +0 -21
- package/tsconfig.node.json +0 -8
- package/yalc.lock +0 -9
package/bin/generateBlurhash.js
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
import fs from "fs";
|
2
|
+
import path from "path";
|
3
|
+
import colors from "colors";
|
4
|
+
|
1
5
|
import sharp from "sharp";
|
2
6
|
import fetch from "node-fetch";
|
3
7
|
import { encode, isBlurhashValid } from "blurhash";
|
4
8
|
|
5
|
-
import path from "path";
|
6
9
|
const __dirname = process.cwd();
|
7
10
|
|
8
|
-
|
11
|
+
async function getSharpImage(imgPath) {
|
9
12
|
if (/^http/.test(imgPath)) {
|
10
13
|
const buffer = await fetch(imgPath)
|
11
14
|
.then(fetchResponse => fetchResponse.arrayBuffer())
|
@@ -13,18 +16,40 @@ export async function getSharpImage(imgPath) {
|
|
13
16
|
|
14
17
|
return sharp(buffer);
|
15
18
|
} else {
|
16
|
-
|
19
|
+
const ext = path.extname(imgPath);
|
20
|
+
const dir = path.dirname(imgPath);
|
21
|
+
const basename = path.basename(imgPath, ext);
|
22
|
+
|
23
|
+
const realImage = sharp(imgPath);
|
24
|
+
|
25
|
+
const previewOption = path.join(dir, basename + "-preview" + ext);
|
26
|
+
console.log(colors.blue("Trying preview", previewOption));
|
27
|
+
|
28
|
+
if (fs.existsSync(previewOption)) {
|
29
|
+
console.log(colors.green("Preview found"));
|
30
|
+
|
31
|
+
return [realImage, sharp(previewOption)];
|
32
|
+
}
|
33
|
+
|
34
|
+
return [realImage];
|
17
35
|
}
|
18
36
|
}
|
19
37
|
|
20
38
|
export async function getBlurhash(path) {
|
21
|
-
const blurhashImage = await getSharpImage(path);
|
39
|
+
const [blurhashImage, previewImage] = await getSharpImage(path);
|
22
40
|
const dimensions = await blurhashImage.metadata();
|
23
41
|
|
24
|
-
const { width, height } = dimensions;
|
42
|
+
const { width: displayWidth, height: displayHeight } = dimensions;
|
43
|
+
let { width, height } = dimensions;
|
44
|
+
|
45
|
+
if (previewImage) {
|
46
|
+
const dimensions = await previewImage.metadata();
|
47
|
+
|
48
|
+
({ width, height } = dimensions);
|
49
|
+
}
|
25
50
|
|
26
51
|
return new Promise((res, rej) => {
|
27
|
-
blurhashImage
|
52
|
+
(previewImage ?? blurhashImage)
|
28
53
|
.raw()
|
29
54
|
.ensureAlpha()
|
30
55
|
.toBuffer((err, buffer) => {
|
@@ -40,7 +65,13 @@ export async function getBlurhash(path) {
|
|
40
65
|
4
|
41
66
|
);
|
42
67
|
if (isBlurhashValid(blurhash)) {
|
43
|
-
return res({
|
68
|
+
return res({
|
69
|
+
blurhash,
|
70
|
+
w: width,
|
71
|
+
h: height,
|
72
|
+
dw: displayWidth,
|
73
|
+
dh: displayHeight,
|
74
|
+
});
|
44
75
|
} else {
|
45
76
|
console.log("FAIL");
|
46
77
|
return rej("FAIL");
|
package/bin/remark-plugin.js
CHANGED
@@ -10,7 +10,7 @@ export const blurhashPlugin = publicPath => () => {
|
|
10
10
|
let outstanding = 0;
|
11
11
|
|
12
12
|
visitParents(tree, "image", async (node, ancestors) => {
|
13
|
-
let { url: imagePath, alt } = node;
|
13
|
+
let { url: imagePath, alt = "" } = node;
|
14
14
|
|
15
15
|
const originalImg = imagePath;
|
16
16
|
if (!/http/.test(imagePath)) {
|
@@ -28,6 +28,8 @@ export const blurhashPlugin = publicPath => () => {
|
|
28
28
|
|
29
29
|
const blurHash = await getBlurhash(imagePath);
|
30
30
|
|
31
|
+
const { w, h, dw, dh } = blurHash;
|
32
|
+
|
31
33
|
console.log(
|
32
34
|
colors.green(`Finished processing ${imagePath}\n\t`, blurHash)
|
33
35
|
);
|
@@ -37,8 +39,8 @@ export const blurhashPlugin = publicPath => () => {
|
|
37
39
|
|
38
40
|
const newNode = `
|
39
41
|
<blurhash-image url="${originalImg}" preview='${JSON.stringify(blurHash)}'>
|
40
|
-
<img alt="${alt
|
41
|
-
<canvas width="${
|
42
|
+
<img alt="${alt}" width="${w}" height="${h}" src="${originalImg}" slot="image" />
|
43
|
+
<canvas width="${w}" height="${h}" style="width: ${dw}px; height: auto;" slot="preview"></canvas>
|
42
44
|
</blurhash-image>`.trim();
|
43
45
|
|
44
46
|
parent.children[index] = {
|
@@ -1,5 +1,3 @@
|
|
1
|
-
import { decode } from "../node_modules/blurhash/dist/esm/index";
|
2
|
-
|
3
1
|
type blurhash = { w: number; h: number; blurhash: string };
|
4
2
|
|
5
3
|
class ImageWithPreview extends HTMLElement {
|
@@ -8,6 +6,7 @@ class ImageWithPreview extends HTMLElement {
|
|
8
6
|
|
9
7
|
static observedAttributes = ["preview"];
|
10
8
|
|
9
|
+
#connected = false;
|
11
10
|
get #imgEl(): any {
|
12
11
|
return this.querySelector("img");
|
13
12
|
}
|
@@ -33,11 +32,12 @@ class ImageWithPreview extends HTMLElement {
|
|
33
32
|
this.#imgEl.addEventListener("load", this.#imgLoad);
|
34
33
|
}
|
35
34
|
|
36
|
-
return
|
35
|
+
return 1;
|
37
36
|
}
|
38
37
|
};
|
39
38
|
|
40
39
|
connectedCallback() {
|
40
|
+
this.#connected = true;
|
41
41
|
if (!this.#checkReady()) {
|
42
42
|
this.mo = new MutationObserver(this.#checkReady);
|
43
43
|
this.mo.observe(this, {
|
@@ -59,6 +59,10 @@ class ImageWithPreview extends HTMLElement {
|
|
59
59
|
}
|
60
60
|
|
61
61
|
#updatePreview() {
|
62
|
+
if (!this.#connected || !this.getAttribute("preview")) {
|
63
|
+
return;
|
64
|
+
}
|
65
|
+
|
62
66
|
const previewObj = JSON.parse(this.getAttribute("preview")!);
|
63
67
|
updateBlurHashPreview(this.#canvasEl, previewObj);
|
64
68
|
}
|
@@ -68,15 +72,21 @@ if (!customElements.get("blurhash-image")) {
|
|
68
72
|
customElements.define("blurhash-image", ImageWithPreview);
|
69
73
|
}
|
70
74
|
|
71
|
-
|
72
|
-
|
75
|
+
const workerBlob = new Blob(
|
76
|
+
[document.querySelector("#next-blurhash-worker-script")!.textContent!],
|
77
|
+
{ type: "text/javascript" }
|
78
|
+
);
|
73
79
|
|
80
|
+
function updateBlurHashPreview(canvasEl: HTMLCanvasElement, preview: blurhash) {
|
81
|
+
const { w: width, h: height, blurhash } = preview;
|
74
82
|
canvasEl.width = width;
|
75
83
|
canvasEl.height = height;
|
76
84
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
if (true) {
|
86
|
+
const worker = new Worker(window.URL.createObjectURL(workerBlob));
|
87
|
+
const offscreen = (canvasEl as any).transferControlToOffscreen();
|
88
|
+
worker.postMessage({ canvas: offscreen, width, height, blurhash }, [
|
89
|
+
offscreen,
|
90
|
+
]);
|
91
|
+
}
|
82
92
|
}
|
@@ -1,7 +1,14 @@
|
|
1
|
-
import { createElement } from "react";
|
2
|
-
import Script from "next/script";
|
1
|
+
import { createElement, Fragment } from "react";
|
3
2
|
|
4
|
-
export const imagePreviewBootstrap = createElement(
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
export const imagePreviewBootstrap = createElement(
|
4
|
+
Fragment,
|
5
|
+
{},
|
6
|
+
createElement("script", {
|
7
|
+
id: "next-blurhash-worker-script",
|
8
|
+
type: "javascript/worker",
|
9
|
+
dangerouslySetInnerHTML: { __html: `(() => { /*WORKER*/ })();` },
|
10
|
+
}),
|
11
|
+
createElement("script", {
|
12
|
+
dangerouslySetInnerHTML: { __html: `(() => { /*WC*/ })();` },
|
13
|
+
})
|
14
|
+
);
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { decode } from "blurhash/dist/esm";
|
2
|
+
|
3
|
+
addEventListener("message", evt => {
|
4
|
+
console.log(evt);
|
5
|
+
|
6
|
+
const { canvas, width, height, blurhash } = evt.data;
|
7
|
+
|
8
|
+
console.log("Encoding", blurhash);
|
9
|
+
const start = +new Date();
|
10
|
+
const pixels = decode(blurhash, width, height);
|
11
|
+
const ctx = canvas.getContext("2d");
|
12
|
+
const imageData = ctx.createImageData(width, height);
|
13
|
+
imageData.data.set(pixels);
|
14
|
+
ctx.putImageData(imageData, 0, 0);
|
15
|
+
const end = +new Date();
|
16
|
+
console.log("Done Encoding", blurhash, end - start);
|
17
|
+
});
|
@@ -0,0 +1,302 @@
|
|
1
|
+
import { createElement, Fragment } from "react";
|
2
|
+
|
3
|
+
export const imagePreviewBootstrap = createElement(
|
4
|
+
Fragment,
|
5
|
+
{},
|
6
|
+
createElement("script", {
|
7
|
+
id: "next-blurhash-worker-script",
|
8
|
+
type: "javascript/worker",
|
9
|
+
dangerouslySetInnerHTML: { __html: `(() => { "use strict";
|
10
|
+
const digitCharacters = [
|
11
|
+
"0",
|
12
|
+
"1",
|
13
|
+
"2",
|
14
|
+
"3",
|
15
|
+
"4",
|
16
|
+
"5",
|
17
|
+
"6",
|
18
|
+
"7",
|
19
|
+
"8",
|
20
|
+
"9",
|
21
|
+
"A",
|
22
|
+
"B",
|
23
|
+
"C",
|
24
|
+
"D",
|
25
|
+
"E",
|
26
|
+
"F",
|
27
|
+
"G",
|
28
|
+
"H",
|
29
|
+
"I",
|
30
|
+
"J",
|
31
|
+
"K",
|
32
|
+
"L",
|
33
|
+
"M",
|
34
|
+
"N",
|
35
|
+
"O",
|
36
|
+
"P",
|
37
|
+
"Q",
|
38
|
+
"R",
|
39
|
+
"S",
|
40
|
+
"T",
|
41
|
+
"U",
|
42
|
+
"V",
|
43
|
+
"W",
|
44
|
+
"X",
|
45
|
+
"Y",
|
46
|
+
"Z",
|
47
|
+
"a",
|
48
|
+
"b",
|
49
|
+
"c",
|
50
|
+
"d",
|
51
|
+
"e",
|
52
|
+
"f",
|
53
|
+
"g",
|
54
|
+
"h",
|
55
|
+
"i",
|
56
|
+
"j",
|
57
|
+
"k",
|
58
|
+
"l",
|
59
|
+
"m",
|
60
|
+
"n",
|
61
|
+
"o",
|
62
|
+
"p",
|
63
|
+
"q",
|
64
|
+
"r",
|
65
|
+
"s",
|
66
|
+
"t",
|
67
|
+
"u",
|
68
|
+
"v",
|
69
|
+
"w",
|
70
|
+
"x",
|
71
|
+
"y",
|
72
|
+
"z",
|
73
|
+
"#",
|
74
|
+
"$",
|
75
|
+
"%",
|
76
|
+
"*",
|
77
|
+
"+",
|
78
|
+
",",
|
79
|
+
"-",
|
80
|
+
".",
|
81
|
+
":",
|
82
|
+
";",
|
83
|
+
"=",
|
84
|
+
"?",
|
85
|
+
"@",
|
86
|
+
"[",
|
87
|
+
"]",
|
88
|
+
"^",
|
89
|
+
"_",
|
90
|
+
"{",
|
91
|
+
"|",
|
92
|
+
"}",
|
93
|
+
"~"
|
94
|
+
];
|
95
|
+
const decode83 = (str) => {
|
96
|
+
let value = 0;
|
97
|
+
for (let i = 0; i < str.length; i++) {
|
98
|
+
const c = str[i];
|
99
|
+
const digit = digitCharacters.indexOf(c);
|
100
|
+
value = value * 83 + digit;
|
101
|
+
}
|
102
|
+
return value;
|
103
|
+
};
|
104
|
+
const sRGBToLinear = (value) => {
|
105
|
+
let v = value / 255;
|
106
|
+
if (v <= 0.04045) {
|
107
|
+
return v / 12.92;
|
108
|
+
} else {
|
109
|
+
return Math.pow((v + 0.055) / 1.055, 2.4);
|
110
|
+
}
|
111
|
+
};
|
112
|
+
const linearTosRGB = (value) => {
|
113
|
+
let v = Math.max(0, Math.min(1, value));
|
114
|
+
if (v <= 31308e-7) {
|
115
|
+
return Math.round(v * 12.92 * 255 + 0.5);
|
116
|
+
} else {
|
117
|
+
return Math.round((1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255 + 0.5);
|
118
|
+
}
|
119
|
+
};
|
120
|
+
const sign = (n) => n < 0 ? -1 : 1;
|
121
|
+
const signPow = (val, exp) => sign(val) * Math.pow(Math.abs(val), exp);
|
122
|
+
class ValidationError extends Error {
|
123
|
+
constructor(message) {
|
124
|
+
super(message);
|
125
|
+
this.name = "ValidationError";
|
126
|
+
this.message = message;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
const validateBlurhash = (blurhash) => {
|
130
|
+
if (!blurhash || blurhash.length < 6) {
|
131
|
+
throw new ValidationError("The blurhash string must be at least 6 characters");
|
132
|
+
}
|
133
|
+
const sizeFlag = decode83(blurhash[0]);
|
134
|
+
const numY = Math.floor(sizeFlag / 9) + 1;
|
135
|
+
const numX = sizeFlag % 9 + 1;
|
136
|
+
if (blurhash.length !== 4 + 2 * numX * numY) {
|
137
|
+
throw new ValidationError(\`blurhash length mismatch: length is \${blurhash.length} but it should be \${4 + 2 * numX * numY}\`);
|
138
|
+
}
|
139
|
+
};
|
140
|
+
const decodeDC = (value) => {
|
141
|
+
const intR = value >> 16;
|
142
|
+
const intG = value >> 8 & 255;
|
143
|
+
const intB = value & 255;
|
144
|
+
return [sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB)];
|
145
|
+
};
|
146
|
+
const decodeAC = (value, maximumValue) => {
|
147
|
+
const quantR = Math.floor(value / (19 * 19));
|
148
|
+
const quantG = Math.floor(value / 19) % 19;
|
149
|
+
const quantB = value % 19;
|
150
|
+
const rgb = [
|
151
|
+
signPow((quantR - 9) / 9, 2) * maximumValue,
|
152
|
+
signPow((quantG - 9) / 9, 2) * maximumValue,
|
153
|
+
signPow((quantB - 9) / 9, 2) * maximumValue
|
154
|
+
];
|
155
|
+
return rgb;
|
156
|
+
};
|
157
|
+
const decode = (blurhash, width, height, punch) => {
|
158
|
+
validateBlurhash(blurhash);
|
159
|
+
punch = punch | 1;
|
160
|
+
const sizeFlag = decode83(blurhash[0]);
|
161
|
+
const numY = Math.floor(sizeFlag / 9) + 1;
|
162
|
+
const numX = sizeFlag % 9 + 1;
|
163
|
+
const quantisedMaximumValue = decode83(blurhash[1]);
|
164
|
+
const maximumValue = (quantisedMaximumValue + 1) / 166;
|
165
|
+
const colors = new Array(numX * numY);
|
166
|
+
for (let i = 0; i < colors.length; i++) {
|
167
|
+
if (i === 0) {
|
168
|
+
const value = decode83(blurhash.substring(2, 6));
|
169
|
+
colors[i] = decodeDC(value);
|
170
|
+
} else {
|
171
|
+
const value = decode83(blurhash.substring(4 + i * 2, 6 + i * 2));
|
172
|
+
colors[i] = decodeAC(value, maximumValue * punch);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
const bytesPerRow = width * 4;
|
176
|
+
const pixels = new Uint8ClampedArray(bytesPerRow * height);
|
177
|
+
for (let y = 0; y < height; y++) {
|
178
|
+
for (let x = 0; x < width; x++) {
|
179
|
+
let r = 0;
|
180
|
+
let g = 0;
|
181
|
+
let b = 0;
|
182
|
+
for (let j = 0; j < numY; j++) {
|
183
|
+
for (let i = 0; i < numX; i++) {
|
184
|
+
const basis = Math.cos(Math.PI * x * i / width) * Math.cos(Math.PI * y * j / height);
|
185
|
+
let color = colors[i + j * numX];
|
186
|
+
r += color[0] * basis;
|
187
|
+
g += color[1] * basis;
|
188
|
+
b += color[2] * basis;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
let intR = linearTosRGB(r);
|
192
|
+
let intG = linearTosRGB(g);
|
193
|
+
let intB = linearTosRGB(b);
|
194
|
+
pixels[4 * x + 0 + y * bytesPerRow] = intR;
|
195
|
+
pixels[4 * x + 1 + y * bytesPerRow] = intG;
|
196
|
+
pixels[4 * x + 2 + y * bytesPerRow] = intB;
|
197
|
+
pixels[4 * x + 3 + y * bytesPerRow] = 255;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
return pixels;
|
201
|
+
};
|
202
|
+
var decode$1 = decode;
|
203
|
+
addEventListener("message", (evt) => {
|
204
|
+
console.log(evt);
|
205
|
+
const { canvas, width, height, blurhash } = evt.data;
|
206
|
+
console.log("Encoding", blurhash);
|
207
|
+
const start = +new Date();
|
208
|
+
const pixels = decode$1(blurhash, width, height);
|
209
|
+
const ctx = canvas.getContext("2d");
|
210
|
+
const imageData = ctx.createImageData(width, height);
|
211
|
+
imageData.data.set(pixels);
|
212
|
+
ctx.putImageData(imageData, 0, 0);
|
213
|
+
const end = +new Date();
|
214
|
+
console.log("Done Encoding", blurhash, end - start);
|
215
|
+
}); })();` },
|
216
|
+
}),
|
217
|
+
createElement("script", {
|
218
|
+
dangerouslySetInnerHTML: { __html: `(() => { "use strict";
|
219
|
+
class ImageWithPreview extends HTMLElement {
|
220
|
+
sd;
|
221
|
+
mo;
|
222
|
+
static observedAttributes = ["preview"];
|
223
|
+
#connected = false;
|
224
|
+
get #imgEl() {
|
225
|
+
return this.querySelector("img");
|
226
|
+
}
|
227
|
+
get #canvasEl() {
|
228
|
+
return this.querySelector("canvas");
|
229
|
+
}
|
230
|
+
constructor() {
|
231
|
+
super();
|
232
|
+
this.sd = this.attachShadow({
|
233
|
+
mode: "open"
|
234
|
+
});
|
235
|
+
this.sd.innerHTML = \`<slot name="preview"></slot>\`;
|
236
|
+
}
|
237
|
+
#checkReady = () => {
|
238
|
+
if (this.#imgEl && this.#canvasEl) {
|
239
|
+
this.mo?.disconnect();
|
240
|
+
if (this.#imgEl.complete) {
|
241
|
+
this.#imgLoad();
|
242
|
+
} else {
|
243
|
+
this.#updatePreview();
|
244
|
+
this.#imgEl.addEventListener("load", this.#imgLoad);
|
245
|
+
}
|
246
|
+
return 1;
|
247
|
+
}
|
248
|
+
};
|
249
|
+
connectedCallback() {
|
250
|
+
this.#connected = true;
|
251
|
+
if (!this.#checkReady()) {
|
252
|
+
this.mo = new MutationObserver(this.#checkReady);
|
253
|
+
this.mo.observe(this, {
|
254
|
+
subtree: true,
|
255
|
+
childList: true,
|
256
|
+
attributes: false
|
257
|
+
});
|
258
|
+
}
|
259
|
+
}
|
260
|
+
#imgLoad = () => {
|
261
|
+
this.sd.innerHTML = \`<slot name="image"></slot>\`;
|
262
|
+
};
|
263
|
+
attributeChangedCallback(name) {
|
264
|
+
if (this.#canvasEl && name === "preview") {
|
265
|
+
this.#updatePreview();
|
266
|
+
}
|
267
|
+
}
|
268
|
+
#updatePreview() {
|
269
|
+
if (!this.#connected || !this.getAttribute("preview")) {
|
270
|
+
return;
|
271
|
+
}
|
272
|
+
const previewObj = JSON.parse(this.getAttribute("preview"));
|
273
|
+
updateBlurHashPreview(this.#canvasEl, previewObj);
|
274
|
+
}
|
275
|
+
}
|
276
|
+
if (!customElements.get("blurhash-image")) {
|
277
|
+
customElements.define("blurhash-image", ImageWithPreview);
|
278
|
+
}
|
279
|
+
const workerBlob = new Blob([document.querySelector("#next-blurhash-worker-script").textContent], {
|
280
|
+
type: "text/javascript"
|
281
|
+
});
|
282
|
+
function updateBlurHashPreview(canvasEl, preview) {
|
283
|
+
const {
|
284
|
+
w: width,
|
285
|
+
h: height,
|
286
|
+
blurhash
|
287
|
+
} = preview;
|
288
|
+
canvasEl.width = width;
|
289
|
+
canvasEl.height = height;
|
290
|
+
{
|
291
|
+
const worker = new Worker(window.URL.createObjectURL(workerBlob));
|
292
|
+
const offscreen = canvasEl.transferControlToOffscreen();
|
293
|
+
worker.postMessage({
|
294
|
+
canvas: offscreen,
|
295
|
+
width,
|
296
|
+
height,
|
297
|
+
blurhash
|
298
|
+
}, [offscreen]);
|
299
|
+
}
|
300
|
+
} })();` },
|
301
|
+
})
|
302
|
+
);
|
package/index.js
CHANGED
package/package.json
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
{
|
2
2
|
"name": "next-blurhash-previews",
|
3
|
-
"version": "0.0.3-
|
3
|
+
"version": "0.0.3-beta70",
|
4
4
|
"description": "",
|
5
5
|
"main": "index.js",
|
6
|
+
"module": "index.js",
|
6
7
|
"bin": {
|
7
8
|
"blurhash-markdown": "./bin/markdown-sync.js"
|
8
9
|
},
|
9
10
|
"scripts": {
|
10
11
|
"test": "echo \"Error: no test specified\" && exit 1",
|
11
|
-
"build": "vite build",
|
12
|
+
"build": "rm -rf build && vite build --config vite.wc.config.ts && vite build --config vite.worker.config.ts && node util/setBootstrap.js",
|
12
13
|
"build-watch": "vite build -w",
|
13
14
|
"prepare": "npm run build"
|
14
15
|
},
|
@@ -31,7 +32,6 @@
|
|
31
32
|
"colors": "^1.4.0",
|
32
33
|
"glob": "^8.0.3",
|
33
34
|
"install": "^0.13.0",
|
34
|
-
"next": "^12.2.0",
|
35
35
|
"node-fetch": "^3.2.6",
|
36
36
|
"npm": "^8.15.1",
|
37
37
|
"remark": "^14.0.2",
|
package/util/setBootstrap.js
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
import fs from "fs";
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
}
|
3
|
+
let BootstrapModule = fs.readFileSync(
|
4
|
+
"./components/imagePreviewBootstrap.tsx",
|
5
|
+
"utf8"
|
6
|
+
);
|
7
|
+
|
8
|
+
const wcScript = fs.readFileSync("./build/imageWithPreview.js", "utf8");
|
9
|
+
const workerScript = fs.readFileSync("./build/workerScript.js", "utf8");
|
10
|
+
|
11
|
+
BootstrapModule = BootstrapModule.replace(
|
12
|
+
"/*WC*/",
|
13
|
+
wcScript.replace(/[\r\n]\s*$/, "").replace(/`/g, "\\`")
|
14
|
+
)
|
15
|
+
.replace(
|
16
|
+
"/*WORKER*/",
|
17
|
+
workerScript.replace(/[\r\n]\s*$/, "").replace(/`/g, "\\`")
|
18
|
+
)
|
19
|
+
.replace(/\${/g, "\\${");
|
20
|
+
|
21
|
+
fs.writeFileSync("./imagePreviewBootstrap.js", BootstrapModule);
|
@@ -1,21 +1,18 @@
|
|
1
1
|
import { defineConfig } from "vite";
|
2
2
|
import react from "@vitejs/plugin-react";
|
3
|
-
import writeBootstrapPlugin from "./util/setBootstrap";
|
4
3
|
|
5
4
|
export default defineConfig({
|
6
5
|
build: {
|
7
6
|
target: "es2022",
|
8
7
|
outDir: "./build",
|
8
|
+
emptyOutDir: false,
|
9
9
|
lib: {
|
10
|
-
entry: "components/
|
10
|
+
entry: "components/ImageWithPreview.tsx",
|
11
11
|
formats: ["cjs"],
|
12
12
|
fileName: () => "imageWithPreview.js",
|
13
13
|
name: "imageWithPreview",
|
14
14
|
},
|
15
|
-
|
16
|
-
rollupOptions: {
|
17
|
-
external: ["react", "react-dom", "next", "next/script"],
|
18
|
-
},
|
15
|
+
minify: false,
|
19
16
|
},
|
20
|
-
plugins: [react()
|
17
|
+
plugins: [react()],
|
21
18
|
});
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { defineConfig } from "vite";
|
2
|
+
import react from "@vitejs/plugin-react";
|
3
|
+
|
4
|
+
export default defineConfig({
|
5
|
+
build: {
|
6
|
+
target: "es2022",
|
7
|
+
outDir: "./build",
|
8
|
+
emptyOutDir: false,
|
9
|
+
lib: {
|
10
|
+
entry: "components/workerScript.ts",
|
11
|
+
formats: ["cjs"],
|
12
|
+
fileName: () => "workerScript.js",
|
13
|
+
name: "workerScript",
|
14
|
+
},
|
15
|
+
minify: false,
|
16
|
+
},
|
17
|
+
plugins: [react()],
|
18
|
+
});
|
package/.prettierrc
DELETED
package/img1.png
DELETED
Binary file
|
package/img5.png
DELETED
Binary file
|
package/junk.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
"use strict";var B=Object.defineProperty;var T=(t,e,s)=>e in t?B(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s;var a=(t,e,s)=>(T(t,typeof e!="symbol"?e+"":e,s),s);const k=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","#","$","%","*","+",",","-",".",":",";","=","?","@","[","]","^","_","{","|","}","~"],h=t=>{let e=0;for(let s=0;s<t.length;s++){const n=t[s],o=k.indexOf(n);e=e*83+o}return e},p=t=>{let e=t/255;return e<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)},b=t=>{let e=Math.max(0,Math.min(1,t));return e<=.0031308?Math.round(e*12.92*255+.5):Math.round((1.055*Math.pow(e,.4166666666666667)-.055)*255+.5)},A=t=>t<0?-1:1,v=(t,e)=>A(t)*Math.pow(Math.abs(t),e);class C extends Error{constructor(e){super(e),this.name="ValidationError",this.message=e}}const G=t=>{if(!t||t.length<6)throw new C("The blurhash string must be at least 6 characters");const e=h(t[0]),s=Math.floor(e/9)+1,n=e%9+1;if(t.length!==4+2*n*s)throw new C(\`blurhash length mismatch: length is \${t.length} but it should be \${4+2*n*s}\`)},V=t=>{const e=t>>16,s=t>>8&255,n=t&255;return[p(e),p(s),p(n)]},D=(t,e)=>{const s=Math.floor(t/361),n=Math.floor(t/19)%19,o=t%19;return[v((s-9)/9,2)*e,v((n-9)/9,2)*e,v((o-9)/9,2)*e]},H=(t,e,s,n)=>{G(t),n=n|1;const o=h(t[0]),c=Math.floor(o/9)+1,l=o%9+1,x=(h(t[1])+1)/166,g=new Array(l*c);for(let r=0;r<g.length;r++)if(r===0){const i=h(t.substring(2,6));g[r]=V(i)}else{const i=h(t.substring(4+r*2,6+r*2));g[r]=D(i,x*n)}const d=e*4,u=new Uint8ClampedArray(d*s);for(let r=0;r<s;r++)for(let i=0;i<e;i++){let y=0,E=0,I=0;for(let m=0;m<c;m++)for(let f=0;f<l;f++){const w=Math.cos(Math.PI*i*f/e)*Math.cos(Math.PI*r*m/s);let M=g[f+m*l];y+=M[0]*w,E+=M[1]*w,I+=M[2]*w}let L=b(y),P=b(E),q=b(I);u[4*i+0+r*d]=L,u[4*i+1+r*d]=P,u[4*i+2+r*d]=q,u[4*i+3+r*d]=255}return u};class R extends HTMLElement{constructor(){super();a(this,"isReady",!1);a(this,"loaded",!1);a(this,"sd");a(this,"mo");a(this,"checkReady",()=>{this.currentImageEl&&this.currentCanvasEl&&(this.ready(),this.mo.disconnect())});a(this,"onImageLoad",()=>{this.getAttribute("url")!==this.currentImageEl.src&&setTimeout(()=>{this.loaded=!0,this.render()},1500)});this.sd=this.attachShadow({mode:"open"}),this.sd.innerHTML='<slot name="preview"></slot>'}get currentImageEl(){return this.querySelector("img")}get currentCanvasEl(){return this.querySelector("canvas")}connectedCallback(){this.mo=new MutationObserver(this.checkReady),this.mo.observe(this,{subtree:!0,childList:!0,attributes:!1})}ready(){this.isReady=!0,this.currentImageEl.complete&&this.onImageLoad(),this.currentImageEl.addEventListener("load",this.onImageLoad),this.loaded||this.updatePreview()}attributeChangedCallback(s,n,o){!this.isReady||(s==="preview"?this.updatePreview():s==="url"&&(this.loaded=!1),this.render())}updatePreview(){const s=JSON.parse(this.getAttribute("preview"));O(this.currentCanvasEl,s)}render(){this.sd.innerHTML=\`<slot name="\${this.loaded?"image":"preview"}"></slot>\`}}a(R,"observedAttributes",["preview","url"]);customElements.get("uikit-image")||customElements.define("uikit-image",R);function O(t,e){const{w:s,h:n}=e;t.width=s,t.height=n;const o=H(e.blurhash,s,n),c=t.getContext("2d"),l=c.createImageData(s,n);l.data.set(o),c.putImageData(l,0,0)}
|