Skip to content

Commit a0e20bd

Browse files
committed
webp uploads
1 parent 580cd9a commit a0e20bd

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

features/features.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
[
2+
{
3+
"version": 2,
4+
"id": "webp-uploads",
5+
"versionAdded": "v4.0.0"
6+
},
27
{
38
"version": 2,
49
"id": "explore-filter",

features/webp-uploads/data.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"title": "WEBP Uploads",
3+
"description": "Allows you to upload webp images in the editor for new sprites, costumes and stage backdrops.",
4+
"credits": [
5+
{ "username": "rgantzos", "url": "https://scratch.mit.edu/users/rgantzos/" }
6+
],
7+
"type": ["Editor"],
8+
"tags": ["New", "Featured"],
9+
"scripts": [{ "file": "script.js", "runOn": "/projects/*" }]
10+
}

features/webp-uploads/script.js

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
export default async function ({ feature, console }) {
2+
let fileInput = {
3+
costume: null,
4+
sprite: null,
5+
stage: null,
6+
};
7+
8+
ScratchTools.waitForElements(
9+
"div[class*='asset-panel_wrapper_'] input[type=file]",
10+
function (input) {
11+
fileInput.costume = input;
12+
13+
fileInput.costume.parentElement.addEventListener("click", function (e) {
14+
e.preventDefault();
15+
e.stopImmediatePropagation();
16+
17+
let input = document.createElement("input");
18+
input.accept = ".svg, .png, .bmp, .jpg, .jpeg, .gif, .webp";
19+
input.setAttribute("multiple", null);
20+
input.type = "file";
21+
22+
let files = [];
23+
24+
input.addEventListener("change", async function () {
25+
const dataTransfer = new DataTransfer();
26+
27+
for (var i in input.files) {
28+
let file = input.files[i];
29+
if (file?.type?.startsWith("image/")) {
30+
if (file.type === "image/webp") {
31+
let blob = await convertWebPFileToPng(file);
32+
file = new File([blob], file.name.split(".")[0] + ".png", {
33+
type: "image/png",
34+
});
35+
dataTransfer.items.add(file);
36+
} else {
37+
dataTransfer.items.add(file);
38+
}
39+
}
40+
}
41+
42+
fileInput.costume.files = dataTransfer.files;
43+
44+
fileInput.costume.dispatchEvent(
45+
new Event("change", { bubbles: true })
46+
);
47+
});
48+
49+
input.click();
50+
});
51+
}
52+
);
53+
54+
ScratchTools.waitForElements(
55+
"div[class*='sprite-selector_sprite-selector_'] input[type=file]",
56+
function (input) {
57+
fileInput.sprite = input;
58+
59+
fileInput.sprite.parentElement.addEventListener("click", function (e) {
60+
e.preventDefault();
61+
e.stopImmediatePropagation();
62+
63+
let input = document.createElement("input");
64+
input.accept = ".svg, .png, .bmp, .jpg, .jpeg, .gif, .webp";
65+
input.setAttribute("multiple", null);
66+
input.type = "file";
67+
68+
let files = [];
69+
70+
input.addEventListener("change", async function () {
71+
const dataTransfer = new DataTransfer();
72+
73+
for (var i in input.files) {
74+
let file = input.files[i];
75+
if (file?.type?.startsWith("image/")) {
76+
if (file.type === "image/webp") {
77+
let blob = await convertWebPFileToPng(file);
78+
file = new File([blob], file.name.split(".")[0] + ".png", {
79+
type: "image/png",
80+
});
81+
dataTransfer.items.add(file);
82+
} else {
83+
dataTransfer.items.add(file);
84+
}
85+
}
86+
}
87+
88+
fileInput.sprite.files = dataTransfer.files;
89+
90+
fileInput.sprite.dispatchEvent(
91+
new Event("change", { bubbles: true })
92+
);
93+
});
94+
95+
input.click();
96+
});
97+
}
98+
);
99+
100+
ScratchTools.waitForElements(
101+
"div[class*='stage-selector_stage-selector_'] input[type=file]",
102+
function (input) {
103+
fileInput.stage = input;
104+
105+
fileInput.stage.parentElement.addEventListener("click", function (e) {
106+
e.preventDefault();
107+
e.stopImmediatePropagation();
108+
109+
let input = document.createElement("input");
110+
input.accept = ".svg, .png, .bmp, .jpg, .jpeg, .gif, .webp";
111+
input.setAttribute("multiple", null);
112+
input.type = "file";
113+
114+
let files = [];
115+
116+
input.addEventListener("change", async function () {
117+
const dataTransfer = new DataTransfer();
118+
119+
for (var i in input.files) {
120+
let file = input.files[i];
121+
if (file?.type?.startsWith("image/")) {
122+
if (file.type === "image/webp") {
123+
let blob = await convertWebPFileToPng(file);
124+
file = new File([blob], file.name.split(".")[0] + ".png", {
125+
type: "image/png",
126+
});
127+
dataTransfer.items.add(file);
128+
} else {
129+
dataTransfer.items.add(file);
130+
}
131+
}
132+
}
133+
134+
fileInput.stage.files = dataTransfer.files;
135+
136+
fileInput.stage.dispatchEvent(new Event("change", { bubbles: true }));
137+
});
138+
139+
input.click();
140+
});
141+
}
142+
);
143+
144+
async function convertWebPFileToPng(webpFile) {
145+
try {
146+
const img = new Image();
147+
const webpUrl = URL.createObjectURL(webpFile);
148+
img.src = webpUrl;
149+
150+
await new Promise((resolve, reject) => {
151+
img.onload = resolve;
152+
img.onerror = reject;
153+
});
154+
155+
const canvas = document.createElement("canvas");
156+
canvas.width = img.width;
157+
canvas.height = img.height;
158+
const ctx = canvas.getContext("2d");
159+
ctx.drawImage(img, 0, 0);
160+
161+
const pngBlob = await new Promise((resolve) => {
162+
canvas.toBlob(resolve, "image/png");
163+
});
164+
165+
URL.revokeObjectURL(webpUrl);
166+
167+
return pngBlob;
168+
} catch (error) {
169+
console.error("Error converting webp to png:", error);
170+
throw error;
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)