Skip to content

Commit efebdd8

Browse files
Josh-Cenaestelle
andauthored
Add tool for obtaining <area> coords from image (mdn#40470)
* Add tool for obtaining `<area>` `coords` from image * Add more output * Fix format * Fix live sample * Move tool to separate page * Update index.md * Update index.md * Update index.md * Update index.md * Update index.md * Update files/en-us/web/css/css_shapes/index.md Co-authored-by: Estelle Weyl <estelle@openwebdocs.org> * Update index.md * Update files/en-us/web/css/css_shapes/shape_generator/index.md * Update index.md Co-authored-by: Estelle Weyl <estelle@openwebdocs.org> * Update index.md Co-authored-by: Estelle Weyl <estelle@openwebdocs.org> --------- Co-authored-by: Estelle Weyl <estelle@openwebdocs.org>
1 parent 22cf84f commit efebdd8

File tree

4 files changed

+246
-4
lines changed

4 files changed

+246
-4
lines changed

files/en-us/web/css/css_shapes/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ img {
129129

130130
## See also
131131

132+
- [Shape generator](/en-US/docs/Web/CSS/CSS_shapes/Shape_generator)
132133
- [CSS Shapes resources](https://codepen.io/KristopherVanSant/post/css-shapes-resources)
133134
- [CSS Shapes 101](https://alistapart.com/article/css-shapes-101/) via alistapart.com (2014)
134135
- [Creating non-rectangular layouts with CSS Shapes](https://www.sarasoueidan.com/blog/css-shapes/) via sarasoueidan.com (2013)
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
---
2+
title: Shape generator
3+
slug: Web/CSS/CSS_shapes/Shape_generator
4+
page-type: guide
5+
sidebar: cssref
6+
---
7+
8+
This shape generator tool can be used to define the coordinates and syntax for features that use {{cssxref("&lt;basic-shape&gt;")}}
9+
10+
- The [`coords`](/en-US/docs/Web/HTML/Reference/Elements/area#coords) attribute of the {{HTMLElement("area")}} element
11+
- The CSS {{cssxref("basic-shape/inset()")}}, {{cssxref("basic-shape/xywh()")}}, {{cssxref("basic-shape/rect()")}}, {{cssxref("basic-shape/circle()")}}, {{cssxref("basic-shape/polygon()")}} functions
12+
- The SVG {{SVGElement("rect")}}, {{SVGElement("circle")}}, {{SVGElement("polygon")}} elements
13+
14+
The shape generator helps you visualize basic shapes, outputting the coordinates of each point, superimposed over your image. Upload an image by either dragging or clicking to select a file. Then select the type of shape you want to create from the dropdown menu. Optionally, you can also adjust the scale of the canvas using the range input. Then click on points on the image to add reference points for the shape you want to create. The tool will then output the markup for the HTML `coords` attribute value, CSS basic shape function with coordinates, and SVG shape.
15+
16+
```html hidden live-sample___shape-generator
17+
<fieldset>
18+
<legend>Controls</legend>
19+
<p>
20+
<label for="scale">
21+
Scale:
22+
<input type="range" id="scale" min="0.1" max="3" value="1" step="any" />
23+
</label>
24+
</p>
25+
<p>
26+
<label for="shape">
27+
Shape:
28+
<select id="shape">
29+
<option value="rect">Rectangle</option>
30+
<option value="circle">Circle</option>
31+
<option value="poly">Polygon</option>
32+
<option value="points">Discrete points</option>
33+
</select>
34+
</label>
35+
</p>
36+
<p>
37+
<button id="reset">Reset</button>
38+
</p>
39+
</fieldset>
40+
<canvas id="canvas">Drop and image here, or click to upload.</canvas>
41+
<p>
42+
<output id="coords">Click on the canvas to add points.</output>
43+
</p>
44+
```
45+
46+
```css hidden live-sample___shape-generator
47+
#canvas {
48+
display: block;
49+
border: 1px solid black;
50+
transform-origin: top left;
51+
margin: 0.5em 0;
52+
}
53+
54+
#coords {
55+
display: block;
56+
position: relative;
57+
word-break: break-all;
58+
}
59+
60+
dt {
61+
font-weight: bold;
62+
margin-top: 1em;
63+
}
64+
dd {
65+
white-space: pre-wrap;
66+
font-family: monospace;
67+
}
68+
```
69+
70+
```js hidden live-sample___shape-generator
71+
const canvas = document.getElementById("canvas");
72+
const scaleInput = document.getElementById("scale");
73+
const shapeSelect = document.getElementById("shape");
74+
const resetButton = document.getElementById("reset");
75+
const coordsDisplay = document.getElementById("coords");
76+
const ctx = canvas.getContext("2d");
77+
let currentImage = null;
78+
let coords = [];
79+
80+
function init() {
81+
if (currentImage) {
82+
URL.revokeObjectURL(currentImage.src);
83+
currentImage = null;
84+
}
85+
resetCoords();
86+
scaleInput.value = 1;
87+
canvas.style.transform = "scale(1)";
88+
canvas.width = 400;
89+
canvas.height = 300;
90+
canvas.style.width = "400px";
91+
canvas.style.height = "300px";
92+
ctx.font = "20px serif";
93+
ctx.fillText("Drop an image here, or click to upload.", 10, 20);
94+
}
95+
96+
function initImage(file) {
97+
const url = URL.createObjectURL(file);
98+
const img = new Image();
99+
img.src = url;
100+
img.addEventListener("load", () => {
101+
canvas.width = img.width;
102+
canvas.height = img.height;
103+
canvas.style.width = `${img.width}px`;
104+
canvas.style.height = `${img.height}px`;
105+
ctx.drawImage(img, 0, 0);
106+
currentImage = img;
107+
});
108+
}
109+
110+
function displayCoords(htmlCoords, cssFunc, svgElem) {
111+
const dl = document.createElement("dl");
112+
const dt1 = document.createElement("dt");
113+
dt1.innerHTML = "HTML <code>coords</code> attribute";
114+
const dd1 = document.createElement("dd");
115+
dd1.innerText = htmlCoords;
116+
const dt2 = document.createElement("dt");
117+
dt2.textContent = "CSS shape functions";
118+
const dd2 = document.createElement("dd");
119+
dd2.innerText = cssFunc;
120+
const dt3 = document.createElement("dt");
121+
dt3.textContent = "SVG element";
122+
const dd3 = document.createElement("dd");
123+
dd3.innerText = svgElem;
124+
dl.append(dt1, dd1, dt2, dd2, dt3, dd3);
125+
coordsDisplay.textContent = "";
126+
coordsDisplay.appendChild(dl);
127+
}
128+
129+
function renderShape() {
130+
ctx.strokeStyle = "magenta";
131+
ctx.fillStyle = "red";
132+
ctx.strokeWidth = "3";
133+
resetDrawnShape();
134+
if (shapeSelect.value === "rect" && coords.length === 2) {
135+
const { x: x1, y: y1 } = coords[0];
136+
const { x: x2, y: y2 } = coords[1];
137+
const w = x2 - x1;
138+
const h = y2 - y1;
139+
ctx.strokeRect(x1, y1, w, h);
140+
displayCoords(
141+
`coords="${x1},${y1},${x2},${y2}"`,
142+
`inset(${y1}px ${x1}px ${canvas.height - y2}px ${canvas.width - x2}px)
143+
xywh(${x1}px ${y1}px ${w}px ${h}px)
144+
rect(${y1}px ${x2}px ${y2}px ${x1}px)`,
145+
`<rect x="${x1}" y="${y1}" width="${w}" height="${h}" />`,
146+
);
147+
} else if (shapeSelect.value === "circle" && coords.length === 2) {
148+
ctx.beginPath();
149+
const { x, y } = coords[0];
150+
const r = Math.sqrt(
151+
(coords[1].x - x) ** 2 + (coords[1].y - y) ** 2,
152+
).toFixed(1);
153+
ctx.arc(x, y, r, 0, Math.PI * 2);
154+
ctx.stroke();
155+
ctx.closePath();
156+
displayCoords(
157+
`coords="${x},${y},${r}"`,
158+
`circle(${r}px at ${x}px ${y}px)`,
159+
`<circle cx="${x}" cy="${y}" r="${r}" />`,
160+
);
161+
} else if (shapeSelect.value === "poly" && coords.length > 2) {
162+
ctx.beginPath();
163+
ctx.moveTo(coords[0].x, coords[0].y);
164+
for (let i = 1; i < coords.length; i++) {
165+
ctx.lineTo(coords[i].x, coords[i].y);
166+
}
167+
ctx.closePath();
168+
ctx.stroke();
169+
displayCoords(
170+
`coords="${coords.map((coord) => `${coord.x},${coord.y}`).join(",")}"`,
171+
`polygon(${coords.map((coord) => `${coord.x} ${coord.y}`).join(", ")})`,
172+
`<polygon points="${coords.map((coord) => `${coord.x},${coord.y}`).join(" ")}" />`,
173+
);
174+
} else if (shapeSelect.value === "points") {
175+
const p = document.createElement("p");
176+
p.innerText = `Coordinate:\n${coords
177+
.map((coord) => `${coord.x},${coord.y}`)
178+
.join("\n")}`;
179+
coordsDisplay.textContent = "";
180+
coordsDisplay.appendChild(p);
181+
}
182+
for (const coord of coords) {
183+
ctx.beginPath();
184+
ctx.arc(coord.x, coord.y, 3, 0, Math.PI * 2);
185+
ctx.fill();
186+
ctx.closePath();
187+
}
188+
}
189+
190+
function resetDrawnShape() {
191+
if (!currentImage) return;
192+
ctx.clearRect(0, 0, canvas.width, canvas.height);
193+
ctx.drawImage(currentImage, 0, 0);
194+
}
195+
196+
function resetCoords() {
197+
coords = [];
198+
coordsDisplay.textContent = "Click on the canvas to add points.";
199+
}
200+
201+
init();
202+
canvas.addEventListener("dragover", (event) => {
203+
event.preventDefault();
204+
});
205+
canvas.addEventListener("drop", (event) => {
206+
event.preventDefault();
207+
initImage(event.dataTransfer.files[0]);
208+
});
209+
canvas.addEventListener("click", (event) => {
210+
if (!currentImage) {
211+
const input = document.createElement("input");
212+
input.type = "file";
213+
input.accept = "image/*";
214+
input.addEventListener("change", (e) => {
215+
initImage(e.target.files[0]);
216+
});
217+
input.click();
218+
return;
219+
}
220+
if (
221+
(shapeSelect.value === "rect" || shapeSelect.value === "circle") &&
222+
coords.length === 2
223+
) {
224+
resetCoords();
225+
}
226+
coords.push({ x: event.offsetX, y: event.offsetY });
227+
renderShape();
228+
});
229+
scaleInput.addEventListener("input", () => {
230+
canvas.style.transform = `scale(${scaleInput.value})`;
231+
coordsDisplay.style.top = `${canvas.height * (scaleInput.value - 1)}px`;
232+
});
233+
shapeSelect.addEventListener("change", () => {
234+
resetCoords();
235+
resetDrawnShape();
236+
});
237+
resetButton.addEventListener("click", init);
238+
```
239+
240+
{{EmbedLiveSample("shape-generator", "", 700)}}

files/en-us/web/html/reference/elements/area/index.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ This element's attributes include the [global attributes](/en-US/docs/Web/HTML/R
7474
- `poly`: the value is `x1,y1,x2,y2,..,xn,yn`. Value specifies the coordinates of the edges of the polygon.
7575
If the first and last coordinate pairs are not the same, the browser will add the last coordinate pair to close the polygon
7676

77-
The values are numbers of CSS pixels.
77+
The values are numbers of CSS pixels. Our [shape generator](/en-US/docs/Web/CSS/CSS_shapes/Shape_generator) can help you generate the `coords` syntax by selecting points on an image you upload.
7878

7979
- `download`
8080
- : This attribute, if present, indicates that the linked resource is intended to be downloaded rather than displayed in the browser.
@@ -122,6 +122,8 @@ This element's attributes include the [global attributes](/en-US/docs/Web/HTML/R
122122
123123
## Examples
124124

125+
### Image with clickable areas
126+
125127
```html
126128
<map name="primary">
127129
<area
@@ -141,9 +143,7 @@ This element's attributes include the [global attributes](/en-US/docs/Web/HTML/R
141143
alt="350 x 150 pic" />
142144
```
143145

144-
### Result
145-
146-
{{ EmbedLiveSample('Examples', 360, 160) }}
146+
{{ EmbedLiveSample('Image with clickable areas', 360, 160) }}
147147

148148
## Technical summary
149149

files/sidebars/cssref.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ sidebar:
348348
- /Web/CSS/CSS_backgrounds_and_borders/Box-shadow_generator
349349
- /Web/CSS/CSS_backgrounds_and_borders/Border-image_generator
350350
- /Web/CSS/CSS_backgrounds_and_borders/Border-radius_generator
351+
- /Web/CSS/CSS_shapes/Shape_generator
351352
l10n:
352353
de:
353354
Beginners: Einsteiger-Tutorials

0 commit comments

Comments
 (0)