Okay, let me see how I can make a guide for you based on what I was able to adapt for this scenario and how it came to me. Perhaps someone with much more advanced knowledge than mine can improve it.
* I used ChatGPT to rewrite the code. Sorry if you find any errors. But the way I shared it with you works for me.
-
Interactive Drawing Canvas:
-
Displays a base image of the human body (front and back).
-
User can click to mark injury points (each click draws a red circle).
-
Includes Undo and Clear buttons to remove selections.
-
Image Generation:
-
Download & Reset:
-
A Download Image button is available so the user can save the generated image locally. Then use this image to upload it intro the form Upload Field.
-
A Start Over button allows the user to clear the drawing and generate a new image if needed. This action includes a confirmation dialog and scrolls back to the canvas.
-
Auto-Hide on Form Resume:
-
Final Review with Radio Button Confirmation:
-
A radio button field labeled: “The Injury Image is Correct?” was added.
-
When the user selects “Yes”, the entire canvas section—including drawing tools and preview—is hidden.
-
Only the final uploaded image is shown, using a File Upload field with image thumbnail display enabled, for clean final submission.
IMPLEMENTING:
I do this using Classic Form:
1- Create a Classic Form and insert a Custom HTML Object with the follow Html code:
<div class="body-map-container">
<h3>Select injured areas on the body</h3>
<canvas id="bodyCanvas" width="700" height="500"></canvas>
<div class="buttons">
<button type="button" id="undoBtn">โคบ Undo</button>
<button type="button" id="clearBtn">๐๏ธ Clear</button>
<button type="button" id="saveImageBtn">๐ธ Generate Image</button>
</div>
</div>
* In the advanced options put this CSS class name: bodyCanvasCss
2- Insert a second Custom HTML with the follow code:
<div id="imagePreviewContainer" style="text-align:center; margin-top: 20px;">
<h4>Saved Image</h4>
<div id="imagePreview"></div>
<div id="imageActions" style="margin-top: 15px; display: none;">
<a id="downloadImage" href="#" download="injury-diagram.png" class="action-btn">๐พ Download Image</a>
<button type="button" id="startOverBtn" class="action-btn">๐ Start Over</button>
</div>
</div>
3- Insert a File Upload object (Make it required)
4- Insert a Radio Button for make the questions: The Injury Image is Correct?. In my example I use this filed to hide the Canvas Html section and the Save Image Html section. Because went you submit the form will showing both in the form file you will save into the Repository.
The logic of this is to have only the file upload field showing with the Show uploaded image option active with X-Large view option selected.
5- Put this CSS code in the CSS and JavaScript Tab.
.body-map-container {
text-align: center;
padding: 10px;
}
canvas#bodyCanvas {
border: 1px solid #ccc;
cursor: crosshair;
display: block;
margin: auto;
width: 700px;
height: 500px;
}
.buttons {
margin-top: 15px;
}
.buttons button {
margin: 0 10px;
padding: 10px 20px;
font-weight: bold;
font-size: 14px;
border: 1px solid #444;
border-radius: 5px;
background-color: #f0f0f0;
cursor: pointer;
}
#imagePreviewContainer img {
max-width: 100%;
border: 1px solid #ccc;
margin-top: 10px;
}
.action-btn {
margin: 0 10px;
padding: 10px 20px;
font-weight: bold;
font-size: 14px;
border: 1px solid #444;
border-radius: 5px;
background-color: #e6f0ff;
cursor: pointer;
display: inline-block;
text-decoration: none;
color: black;
}
6- Use this JavaScript:
window.addEventListener("load", function () {
const canvas = document.getElementById("bodyCanvas");
const ctx = canvas.getContext("2d");
const container = document.querySelector(".body-map-container");
const extraSection = document.querySelector(".bodyCanvasCss");
let clicks = [];
const background = new Image();
background.crossOrigin = "anonymous";
background.src = "https://yourdomain.com/path/body-diagram.png"; //Put your Body Diagram Image URL Here
function drawAll() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
clicks.forEach(({ x, y }) => {
ctx.beginPath();
ctx.arc(x, y, 15, 0, 2 * Math.PI);
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
ctx.stroke();
});
}
canvas.addEventListener("click", function (e) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
clicks.push({ x, y });
drawAll();
updateHiddenFields();
});
document.getElementById("undoBtn").addEventListener("click", function () {
clicks.pop();
drawAll();
updateHiddenFields();
});
document.getElementById("clearBtn").addEventListener("click", function () {
clicks = [];
drawAll();
updateHiddenFields();
});
document.getElementById("saveImageBtn").addEventListener("click", function () {
const dataURL = canvas.toDataURL("image/png");
// Show preview
const previewTarget = document.getElementById("imagePreview");
if (previewTarget) {
previewTarget.innerHTML = `<img src="${dataURL}" alt="Saved Image" />`;
}
// Enable download and start over
document.getElementById("downloadImage").href = dataURL;
document.getElementById("imageActions").style.display = "block";
// Hide canvas and extra section
container.style.display = "none";
if (extraSection) extraSection.style.display = "none";
// Save to hidden field (Workflow)
const fieldCanvas = document.querySelector("#canvasImageData input");
if (fieldCanvas) fieldCanvas.value = dataURL;
// Save to optional .ImageDir field
const fieldByClass = document.querySelector(".ImageDir input, .ImageDir textarea");
if (fieldByClass) fieldByClass.value = dataURL;
alert("Image has been generated and is ready for download or submission.");
});
document.getElementById("startOverBtn").addEventListener("click", function () {
const confirmReset = confirm("Are you sure you want to start over? This will delete the current image.");
if (!confirmReset) return;
clicks = [];
drawAll();
// Show canvas and extra section again
container.style.display = "block";
if (extraSection) extraSection.style.display = "block";
// Clear preview and controls
document.getElementById("imagePreview").innerHTML = "";
document.getElementById("imageActions").style.display = "none";
document.getElementById("downloadImage").href = "#";
// Clear stored values
const fieldCanvas = document.querySelector("#canvasImageData input");
if (fieldCanvas) fieldCanvas.value = "";
const fieldByClass = document.querySelector(".ImageDir input, .ImageDir textarea");
if (fieldByClass) fieldByClass.value = "";
// Scroll to canvas
container.scrollIntoView({ behavior: "smooth" });
});
function updateHiddenFields() {
const coordField = document.querySelector("#selectedPoints input");
if (coordField) coordField.value = JSON.stringify(clicks);
}
background.onload = () => drawAll();
if (background.complete) drawAll();
// โ
Auto-load saved image if present
const fieldCanvas = document.querySelector("#canvasImageData input");
if (fieldCanvas && fieldCanvas.value.startsWith("data:image/")) {
container.style.display = "none";
if (extraSection) extraSection.style.display = "none";
const previewTarget = document.getElementById("imagePreview");
if (previewTarget) {
previewTarget.innerHTML = `<img src="${fieldCanvas.value}" alt="Saved Image" />`;
}
const actions = document.getElementById("imageActions");
if (actions) {
document.getElementById("downloadImage").href = fieldCanvas.value;
actions.style.display = "block";
}
}
});
The human body image used as the background for the canvas must be hosted at a public HTTPS-accessible URL (e.g., https://yourdomain.com/path/body-diagram.png).
If the image is hosted locally or on an intranet, it will not render in the canvas for external users or during form rendering.
Why? HTML5 <canvas> cannot draw cross-origin images unless served from a public domain with proper CORS permissions.
7- Use a Process Diagram Service Task or Workflow Service Task. to save the form to the Repository.
8 - Almost forgot: Create a Field Rule where you will hide both Custom HTML if the Radio Button: The Injury Image is Correct? is Yes.
I hope this can help you. If you have any questions let me know.
https://i.postimg.cc/qM7X5wD2/req-img2.gif