Skip to content

Commit 073b85d

Browse files
authored
Merge pull request sauravhathi#4 from sauravhathi/3-loading-icon-when-generate-image
3 loading icon when generate image
2 parents d4de328 + e17b287 commit 073b85d

File tree

3 files changed

+222
-107
lines changed

3 files changed

+222
-107
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ This is a simple image generator using [OpenAI API](https://openai.com/api/). yo
1616

1717
## How to use
1818

19-
- type a short description of the image or a keyword
20-
- click on the generate button and wait 3-5 seconds for the images to load
21-
- you will get 3 images generated by the API
19+
- Enter your OpenAI API key
20+
- Enter a short description of the image or a keyword
21+
- Select image size
22+
- Select number of images
23+
- Click on generate button
24+
- Double click on the image to download it
2225

2326
## API Reference
2427

@@ -54,4 +57,4 @@ Open `index.html` in your browser
5457

5558
## License
5659

57-
[MIT](https://github.com/sauravhathi/image-generator-using-openai-api/blob/master/LICENSE)
60+
[MIT](https://github.com/sauravhathi/image-generator-using-openai-api/blob/master/LICENSE)

index.html

Lines changed: 186 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,197 @@
11
<!DOCTYPE html>
22
<html lang="en">
3-
<head>
4-
<meta charset="UTF-8" />
5-
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<script src="https://cdn.tailwindcss.com"></script>
8-
<script src="api.js"></script>
9-
<title>Image Gererator using OpenAI API</title>
10-
<style>
11-
.min-height {
12-
min-height: 100vh;
13-
}
14-
</style>
15-
</head>
16-
<body class="max-w-4xl mx-auto">
17-
<div
18-
class="min-height flex flex-col justify-center px-4 py-12 sm:px-6 lg:px-8 -mt-20"
19-
>
20-
<div class="text-center">
21-
<h1 class="text-4xl font-bold mb-10">Image Generator</h1>
22-
<p class="text-md lg:text-xl mb-10">
23-
This is a simple image generator using
24-
<a href="https://openai.com/api/" class="font-semibold"
25-
>OpenAI API.</a
26-
>
27-
you can generate images by entering short description of the image or
28-
by entering a keyword.
29-
</p>
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<script src="https://cdn.tailwindcss.com"></script>
9+
<script src="api.js"></script>
10+
<link rel="stylesheet" href="style.css" />
11+
<title>Image Gererator using OpenAI API</title>
12+
</head>
13+
14+
<body class="max-w-4xl mx-auto">
15+
<div class="flex flex-col justify-center px-4 pt-12 sm:px-6 lg:px-8">
16+
<div class="text-center">
17+
<h1 class="text-4xl font-bold mb-10">Image Generator</h1>
18+
<p class="text-md lg:text-xl mb-10">
19+
This is a simple image generator using
20+
<a href="https://openai.com/api/" class="font-semibold">OpenAI API.</a>
21+
You can generate images by entering a short description of the image or by entering a keyword.
22+
</p>
23+
</div>
24+
<div class="bg-white shadow-md rounded-lg px-8 pt-6 pb-8 mb-4 flex flex-col my-2">
25+
<div class="mb-4 flex flex-col gap-2">
26+
<label for="api" class="text-gray-600 apiDiv">OpenAI API Key</label>
27+
<input type="text" id="api" class="input-style apiDiv" placeholder="Enter your OpenAI API key here" />
28+
<label for="text" class="text-gray-600">Description or Keyword</label>
29+
<textarea id="text" class="input-style"
30+
placeholder="Enter a short description of the image or a keyword"></textarea>
31+
<label for="sizeSelect" class="text-gray-600">Image Size</label>
32+
<select id="sizeSelect" class="input-style"></select>
33+
<label for="numImagesSelect" class="text-gray-600">Number of Images</label>
34+
<select id="numImagesSelect" class="input-style"></select>
3035
</div>
31-
<div class="flex flex-row justify-center">
32-
<input
33-
type="text"
34-
id="text"
35-
class="shadow-lg outline-none w-full rounded-l-lg px-4 py-4 text-gray-600 border-r-2 border-gray-200"
36-
placeholder="An Impressionist oil painting of sunflowers in a purple vase…"
37-
/>
38-
<button
39-
id="btn"
40-
class="font-bold py-4 px-8 rounded-r-lg shadow-lg"
41-
onclick="generateImage()"
42-
>
43-
Generate
44-
</button>
36+
<div class="mt-2 text-right">
37+
<button id="btn" class="button-style">Generate</button>
4538
</div>
46-
<div id="image" class="grid grid-cols-2 lg:grid-cols-3 gap-1 mt-4"></div>
47-
<footer
48-
class="text-center mt-10 border-t-2 border-gray-200 pt-4"
49-
id="checkAuthor"
50-
>
51-
<p class="text-sm">
52-
Made with ❤️ by
53-
<a href="https://github.com/sauravhathi" class="font-semibold"
54-
>@sauravhathi</a
55-
>
56-
</p>
57-
</footer>
39+
<div id="loading" class="hidden flex justify-center items-center mt-10">
40+
<svg class="animate-spin h-10 w-10 text-gray-500" viewBox="0 0 24 24">
41+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
42+
<path class="opacity-75" fill="currentColor"
43+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647zm11-2.647A7.962 7.962 0 0120 12h-4c0 3.042-1.135 5.824-3 7.938l-3-2.647z">
44+
</path>
45+
</svg>
46+
47+
</div>
48+
<div id="image" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 mt-10"></div>
5849
</div>
59-
</body>
60-
<script>
61-
62-
if (api === ""){
63-
alert("Use your openAI API key");
64-
}
65-
const url = "https://api.openai.com/v1/images/generations";
66-
const text = document.getElementById("text");
67-
const image = document.getElementById("image");
68-
const btn = document.getElementById("btn");
69-
70-
function generateImage() {
71-
if (text.value === "") {
72-
alert("Please enter a value");
73-
} else {
74-
const data = {
75-
prompt: text.value,
76-
n: 3,
77-
size: "1024x1024",
78-
};
79-
fetch(url, {
80-
method: "POST",
81-
headers: {
82-
"Content-Type": "application/json",
83-
Authorization: `Bearer ${api}`,
84-
},
85-
body: JSON.stringify(data),
86-
})
87-
.then((res) => res.json())
88-
.then((data) => {
89-
console.log(data);
90-
const arraySize = data.data.length;
91-
for (let i = 0; i < arraySize; i++) {
92-
image.innerHTML += `<img src="${data.data[i].url}" alt="image" class="w-100 p-2">`;
93-
}
94-
});
50+
</div>
51+
<footer class="text-center mt-10 border-t-2 border-gray-200 py-4" id="checkAuthor">
52+
<p class="text-sm">
53+
Made with ❤️ by
54+
<a href="https://github.com/sauravhathi" class="font-semibold">@sauravhathi</a>
55+
</p>
56+
</footer>
57+
</body>
58+
59+
<script>
60+
const url = "https://api.openai.com/v1/images/generations";
61+
const text = document.getElementById("text");
62+
const image = document.getElementById("image");
63+
const btn = document.getElementById("btn");
64+
const sizeSelect = document.getElementById("sizeSelect");
65+
const numImagesSelect = document.getElementById("numImagesSelect");
66+
const loadingSpinner = document.getElementById("loading");
67+
const apiDiv = document.getElementsByClassName("apiDiv");
68+
const apiInput = document.getElementById("api");
69+
70+
const sizeOptions = ["256", "512", "1024", "1280", "2560", "3840", "5120", "7680"];
71+
const optionsFragment = document.createDocumentFragment();
72+
73+
sizeOptions.forEach((size) => {
74+
const option = document.createElement("option");
75+
option.value = size;
76+
option.textContent = size;
77+
optionsFragment.appendChild(option);
78+
});
79+
80+
sizeSelect.appendChild(optionsFragment);
81+
82+
for (let i = 1; i <= 10; i++) {
83+
const option = document.createElement("option");
84+
option.value = i;
85+
option.textContent = i;
86+
numImagesSelect.appendChild(option);
87+
}
88+
89+
let imageSizes = sizeSelect.value;
90+
let numImages = parseInt(numImagesSelect.value);
91+
let apiKey = "";
92+
93+
function generateImage() {
94+
if (apiKey === "") {
95+
apiKey = apiInput.value.trim() || api;
96+
if (apiKey === "") {
97+
alert("Please enter your OpenAI API key");
98+
return;
9599
}
100+
apiDiv[0].classList.add("hidden");
101+
apiDiv[1].classList.add("hidden");
102+
}
103+
if (text.value === "") {
104+
alert("Please enter a value");
105+
return;
96106
}
97107

98-
const checkAuthor = document.getElementById("checkAuthor");
99-
if (checkAuthor.children[0].children[0].textContent === "@sauravhathi") {
108+
loadingSpinner.classList.remove("hidden");
109+
btn.disabled = true;
110+
111+
const data = {
112+
prompt: text.value,
113+
n: numImages,
114+
size: `${imageSizes}x${imageSizes}`,
115+
};
116+
117+
fetch(url, {
118+
method: "POST",
119+
headers: {
120+
"Content-Type": "application/json",
121+
Authorization: "Bearer " + apiKey,
122+
},
123+
body: JSON.stringify(data),
124+
})
125+
.then((res) => res.json())
126+
.then((data) => {
127+
console.log(data);
128+
loadingSpinner.classList.add("hidden");
129+
btn.disabled = false;
130+
131+
data.data.forEach((item) => {
132+
const img = document.createElement("img");
133+
img.src = item.url;
134+
img.alt = "image";
135+
img.classList.add(
136+
"w-full",
137+
"h-auto",
138+
"rounded-lg",
139+
"shadow-lg",
140+
"hover:shadow-2xl",
141+
"transition",
142+
"duration-500",
143+
"ease-in-out",
144+
"transform",
145+
"hover:-translate-y-1",
146+
"hover:scale-103"
147+
);
148+
149+
image.appendChild(img);
150+
151+
// Double-click event listener
152+
img.addEventListener("dblclick", () => {
153+
downloadImage(item.url);
154+
});
155+
});
156+
})
157+
.catch((err) => {
158+
console.log(err);
159+
loadingSpinner.classList.add("hidden");
160+
btn.disabled = false;
161+
alert("Something went wrong. Please try again.");
162+
});
163+
}
164+
165+
const checkAuthor = document.getElementById("checkAuthor");
166+
if (checkAuthor.children[0].children[0].textContent !== "@sauravhathi") {
167+
window.location.href = "https://github.com/sauravhathi";
168+
}
169+
170+
text.addEventListener("input", function () {
171+
if (text.value === "") {
172+
btn.classList.remove("bg-slate-900", "text-slate-50");
173+
text.classList.add("border-r-2", "border-gray-200");
100174
} else {
101-
window.location.href = "https://github.com/sauravhathi";
175+
text.classList.remove("border-r-2", "border-gray-200");
176+
btn.classList.add("bg-slate-900", "text-slate-50");
102177
}
178+
});
103179

104-
text.addEventListener("input", function () {
105-
if (text.value === "") {
106-
btn.classList.remove("bg-slate-900", "text-slate-50");
107-
text.classList.add("border-r-2", "border-gray-200");
108-
} else {
109-
text.classList.remove("border-r-2", "border-gray-200");
110-
btn.classList.add("bg-slate-900", "text-slate-50");
111-
}
112-
});
113-
</script>
114-
</html>
180+
sizeSelect.addEventListener("change", function () {
181+
imageSizes = sizeSelect.value;
182+
});
183+
184+
numImagesSelect.addEventListener("change", function () {
185+
numImages = parseInt(numImagesSelect.value);
186+
});
187+
188+
btn.addEventListener("click", generateImage);
189+
190+
function downloadImage(url) {
191+
const link = document.createElement("a");
192+
link.href = url;
193+
link.download = document.getElementById("text").value.split(" ").join("_") + ".png";
194+
link.target = "_blank";
195+
link.click();
196+
}
197+
</script>

style.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.input-style {
2+
border: 2px solid #ccc;
3+
border-radius: 0.25rem;
4+
padding: 0.5rem;
5+
color: #666;
6+
background-color: #fff;
7+
width: 100%;
8+
outline: none;
9+
}
10+
11+
.button-style {
12+
background-color: #eee;
13+
color: #111;
14+
font-weight: bold;
15+
padding: 0.5rem 1rem;
16+
border-radius: 0.25rem;
17+
outline: none;
18+
transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease;
19+
}
20+
21+
.button-style:hover {
22+
background-color: #111;
23+
color: #fff;
24+
transform: translateY(-1px);
25+
}
26+
27+
.button-style:focus {
28+
outline: none;
29+
}

0 commit comments

Comments
 (0)