Hi all!
I added a custom drag-and-drop function to my questions (finally). I want to save what the final position of the dragged images looks like, and it would be ideal if I could save it as an image. One way I saw this is that I could save the image as base64 encoding and decode it later to get the image. I can do this in console printing, however it won’t save as embedded data to my survey. I have the HTML code, JS code (which is where I set up the image saving), and the error below. The error seems to be due to Qualtrics thinking that the image is not coming from Qualtrics, but this is the url from the image library!
Error:
E API Error: SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
at saveFinalImage (eval at <anonymous> (jfeLib.fa2484b7040929bdf0ac.min.js:2:125561), <anonymous>:81:28)
at A.i.eval (eval at <anonymous> (jfeLib.fa2484b7040929bdf0ac.min.js:2:125561), <anonymous>:89:4)
at A.i.<anonymous> (jfe.f9dd9f5e15c3385f2b68.min.js:2:268179)
at A.i.r (jfeLib.fa2484b7040929bdf0ac.min.js:2:29148)
at A.i.s (jfeLib.fa2484b7040929bdf0ac.min.js:2:29191)
at s (jfeLib.fa2484b7040929bdf0ac.min.js:2:28461)
at A.i._trigger (jfeLib.fa2484b7040929bdf0ac.min.js:2:30008)
at A.i.r [as _trigger] (jfe.f9dd9f5e15c3385f2b68.min.js:2:359422)
at A.i.<anonymous> (jfe.f9dd9f5e15c3385f2b68.min.js:2:267208)
at jfe.f9dd9f5e15c3385f2b68.min.js:2:372392 ƒ () {
saveFinalImage();
const tomorrowCoordinates = document.getElementById("tomorrowB1-coordinates").value;
const yesterdayCoordinates = document.getElementById("yesterdayB…
HTML:
<div style="position: relative; width: 600px; height: 400px;
background-image: url('https://uwartsandsciences.pdx1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_eXemGs5D7yFpvhz');
background-size: contain;
background-position: center;
background-repeat: no-repeat;
border: 1px solid #ccc;
margin: 0 auto;
overflow: hidden;" id="image-containerB1D" >
<img style="position: absolute; top: 150px; left: 460px; width: 50px; cursor: grab; z-index: 10;"
alt="Yesterday"
src="https://uwartsandsciences.pdx1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_LtUZPmmUGH5uBbA"
id="yesterdayB1">
<img style="position: absolute; top: 175px; left: 230px; width: 50px; height: 50px; cursor: grab; z-index: 10;"
alt="Today"
src="https://uwartsandsciences.pdx1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_a7VqGJ0RqP8RSH0"
id="todayB1">
<img style="position: absolute; top: 220px; left: 460px; width: 50px; cursor: grab; z-index: 10;"
alt="Tomorrow"
src="https://uwartsandsciences.pdx1.qualtrics.com/ControlPanel/Graphic.php?IM=IM_d5Oei9ZlTDYwxxp"
id="tomorrowB1">
</div>
<input id="yesterdayB1-coordinates" type="hidden">
<input id="todayB1-coordinates" type="hidden">
<input id="tomorrowB1-coordinates" type="hidden">
JS:
Qualtrics.SurveyEngine.addOnload(function () {
// Ensure the script only runs for the visible question
if (this.getQuestionContainer().style.display !== "none") {
let draggingElement = null;
let offsetX = 0;
let offsetY = 0;
const container = document.getElementById("image-containerB1D");
// Add drag-and-drop functionality
document.querySelectorAll('img').forEach(function (img) {
img.addEventListener('mousedown', function (event) {
draggingElement = img;
offsetX = event.clientX - img.getBoundingClientRect().left;
offsetY = event.clientY - img.getBoundingClientRect().top;
// Set a high z-index while dragging to ensure it's on top
img.style.zIndex = 1000;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
});
function onMouseMove(event) {
if (!draggingElement) return;
const containerRect = container.getBoundingClientRect();
// Calculate the new position
let left = event.clientX - containerRect.left - offsetX;
let top = event.clientY - containerRect.top - offsetY;
// Prevent dragging outside the container
left = Math.max(0, Math.min(left, containerRect.width - draggingElement.offsetWidth));
top = Math.max(0, Math.min(top, containerRect.height - draggingElement.offsetHeight));
// Update position
draggingElement.style.left = left + "px";
draggingElement.style.top = top + "px";
}
function onMouseUp() {
if (!draggingElement) return;
// Update the hidden input for the dragged element
const hiddenInput = document.getElementById(draggingElement.id + "-coordinates");
if (hiddenInput) {
hiddenInput.value = draggingElement.style.left.replace("px", "") + "," + draggingElement.style.top.replace("px", "");
}
// Reset z-index after dragging
draggingElement.style.zIndex = 10;
draggingElement = null;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
// Save image as embedded data
function saveFinalImage() {
const container = document.getElementById("image-containerB1D");
if (!container) return;
const containerRect = container.getBoundingClientRect();
const canvas = document.createElement('canvas');
canvas.width = containerRect.width;
canvas.height = containerRect.height;
const ctx = canvas.getContext('2d');
// Draw all images onto the canvas at their current positions
document.querySelectorAll('#image-containerB1D img').forEach(function (img) {
const rect = img.getBoundingClientRect();
const imgLeft = rect.left - containerRect.left;
const imgTop = rect.top - containerRect.top;
ctx.drawImage(img, imgLeft, imgTop, img.width, img.height);
});
// Convert the canvas to a base64 image
const imageUrl = canvas.toDataURL('image/png');
// Store base64 image in Embedded Data
Qualtrics.SurveyEngine.setEmbeddedData("finalImage_1", imageUrl);
}
// Save coordinates on page submission
Qualtrics.SurveyEngine.addOnPageSubmit(function () {
saveFinalImage();
const tomorrowCoordinates = document.getElementById("tomorrowB1-coordinates").value;
const yesterdayCoordinates = document.getElementById("yesterdayB1-coordinates").value;
// Save coordinates into Embedded Data fields
Qualtrics.SurveyEngine.setEmbeddedData("tomorrowCoordinates", tomorrowCoordinates);
Qualtrics.SurveyEngine.setEmbeddedData("yesterdayCoordinates", yesterdayCoordinates);
});
}
});