I have these script that integrate dragging with interact.js but after integrating i cannot get the position of the image right after dragging using interact.js.
Here is the screen recording gif of the problem:
Erorr Screen Recording
Here’s my custom controls code:
// Function to update red dots
function updateRedDots(offsetX, offsetY) {
$(controls[0]).css({ left: offsetX + xOffset, top: offsetY + yOffset }).show();
$(controls[1]).css({ left: offsetX + xOffset + imagePosition.width - $(controls[1]).width(), top: offsetY + yOffset }).show();
$(controls[2]).css({ left: offsetX + xOffset + imagePosition.width - $(controls[2]).width(), top: offsetY + yOffset + imagePosition.height - $(controls[2]).height() }).show();
$(controls[3]).css({ left: offsetX + xOffset, top: offsetY + yOffset + imagePosition.height - $(controls[3]).height() }).show();
}
I want the image keep attached everytime dragging the image and use the custom controls (skew features).
And here’s my script:
<script>
var controls = [];
var canvas;
var context;
var image = new Image();
var triangles = [];
var dirtyTriangles = true;
var opacitySlider = document.getElementById('opacityRange');
var currentOpacity = 1.0;
var isDragging = false;
let imagePosition = { width: 0, height: 0 };
var interactInstance;
$(document).ready(function() {
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
var container = $('#container');
var containerWidth = container.width();
var containerHeight = container.height();
canvas.width = containerWidth;
canvas.height = containerHeight;
// Initialize red dots and blue lines
for (var i = 0; i < 4; ++i) {
var control = document.createElement('div');
control.id = 'node' + i;
$(control).addClass('node').hide(); // Hide red dots initially
$('#container').append(control);
controls.push(control);
}
// Initialize blue lines
var lines = ['line1', 'line2', 'line3', 'line4'];
lines.forEach(function(line) {
var lineElement = document.createElement('div');
lineElement.id = line;
$(lineElement).addClass('blue-line').hide(); // Hide blue lines initially
$('#container').append(lineElement);
});
for (var i = 0; i < 4; ++i) {
var control = document.createElement('div');
$(control).addClass('node');
$('#container').append(control);
controls.push(control);
}
// Function to update red dots
function updateRedDots(offsetX, offsetY) {
$(controls[0]).css({ left: offsetX + xOffset, top: offsetY + yOffset }).show();
$(controls[1]).css({ left: offsetX + xOffset + imagePosition.width - $(controls[1]).width(), top: offsetY + yOffset }).show();
$(controls[2]).css({ left: offsetX + xOffset + imagePosition.width - $(controls[2]).width(), top: offsetY + yOffset + imagePosition.height - $(controls[2]).height() }).show();
$(controls[3]).css({ left: offsetX + xOffset, top: offsetY + yOffset + imagePosition.height - $(controls[3]).height() }).show();
}
$('#imageUpload').on('change', function(event) {
var file = event.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
image.src = e.target.result;
image.onload = function() {
imagePosition.width = 120;
imagePosition.height = image.height * (120 / image.width);
xOffset = (canvas.width - imagePosition.width) / 2;
yOffset = (canvas.height - imagePosition.height) / 2;
// Clear canvas and redraw the image
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, xOffset, yOffset, imagePosition.width, imagePosition.height);
// Update red dots positions initially
updateRedDots(0, 0);
$('.opacity-slider').show(); // Show opacity slider
$('#downloadButton').show(); // Show download button
setInterval(draw, 500 / 30);
// Initialize interact.js instance for the canvas
if (interactInstance) {
interactInstance.unset();
}
interactInstance = interact('#canvas')
.draggable({
listeners: {
start() {
isDragging = true;
},
move(event) {
if (isDragging) {
const x = (parseFloat(canvas.getAttribute('data-x')) || 0) + event.dx;
const y = (parseFloat(canvas.getAttribute('data-y')) || 0) + event.dy;
canvas.style.transform = `translate(${x}px, ${y}px)`;
canvas.setAttribute('data-x', x);
canvas.setAttribute('data-y', y);
// Update red dots' positions
updateRedDots(x, y);
}
},
end() {
isDragging = false;
}
}
})
.resizable({
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
start() {
isDragging = true;
},
move(event) {
if (isDragging) {
const x = parseFloat(canvas.getAttribute('data-x')) || 0;
const y = parseFloat(canvas.getAttribute('data-y')) || 0;
imagePosition.width = event.rect.width;
imagePosition.height = event.rect.height;
const newX = x + event.deltaRect.left;
const newY = y + event.deltaRect.top;
canvas.style.transform = `translate(${newX}px, ${newY}px)`;
canvas.setAttribute('data-x', newX);
canvas.setAttribute('data-y', newY);
// Clear canvas and redraw the image
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, xOffset, yOffset, imagePosition.width, imagePosition.height);
// Update red dots' positions
updateRedDots(newX, newY);
}
},
end() {
isDragging = false;
}
}
});
// Update red dots' positions
updateRedDots(0, 0);
};
};
reader.readAsDataURL(file);
});
/*function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
var xOffset = (canvas.width - imagePosition.width) / 2;
var yOffset = (canvas.height - imagePosition.height) / 2;
context.drawImage(image, xOffset, yOffset, imagePosition.width, imagePosition.height);
updateRedDots(xOffset, yOffset);
}*/
$('#dragButton').on('click', function() {
enableDragMode();
});
$('#skewButton').on('click', function() {
enableSkewMode();
});
function enableDragMode() {
disableSkewMode();
$('#dragButton').addClass('active');
interactInstance = interact('#container')
.draggable({
listeners: {
move(event) {
const target = event.target;
const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
}
});
}
function enableSkewMode() {
disableDragMode();
$('#skewButton').addClass('active');
interactInstance = interact('#container')
.resizable({
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
move(event) {
const target = event.target;
let x = parseFloat(target.getAttribute('data-x')) || 0;
let y = parseFloat(target.getAttribute('data-y')) || 0;
target.style.width = `${event.rect.width}px`;
target.style.height = `${event.rect.height}px`;
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
}
});
}
function disableDragMode() {
$('#dragButton').removeClass('active');
if (interactInstance) {
interactInstance.unset();
}
}
function disableSkewMode() {
$('#skewButton').removeClass('active');
if (interactInstance) {
interactInstance.unset();
}
}
// Function to handle the dragging of nodes
function handleDrag(node, offsetX, offsetY) {
$(document).on('mousemove touchmove', function(e) {
e.preventDefault(); // Prevent default scrolling behavior on touch devices
e.stopPropagation(); // Prevent event from propagating to parent elements
var x = (e.pageX || e.originalEvent.touches[0].pageX) - $('#container').offset().left - offsetX;
var y = (e.pageY || e.originalEvent.touches[0].pageY) - $('#container').offset().top - offsetY;
// Update node position
$(node).css({
'left': x + 'px',
'top': y + 'px'
});
dirtyTriangles = true;
drawLines();
});
$(document).on('mouseup touchend', function() {
$(document).off('mousemove touchmove');
$(document).off('mouseup touchend');
});
}
// Mouse down event
$('body').on('mousedown touchstart', function(e) {
if ($(e.target).hasClass('node')) {
var node = e.target;
var offsetX = e.pageX - $(node).offset().left;
var offsetY = e.pageY - $(node).offset().top;
handleDrag(node, offsetX, offsetY);
}
});
// CSS to prevent selection during dragging
$('body').on('mousedown touchstart', function(e) {
if ($(e.target).hasClass('node')) {
e.preventDefault(); // Prevent text selection and other default behaviors
}
});
opacitySlider.addEventListener('input', function() {
currentOpacity = opacitySlider.value / 100;
draw();
});
document.getElementById('downloadButton').addEventListener('click', function() {
downloadImage();
});
function drawLines() {
var controlPositions = [
{ x: parseInt($(controls[0]).css('left')), y: parseInt($(controls[0]).css('top')) },
{ x: parseInt($(controls[1]).css('left')), y: parseInt($(controls[1]).css('top')) },
{ x: parseInt($(controls[2]).css('left')), y: parseInt($(controls[2]).css('top')) },
{ x: parseInt($(controls[3]).css('left')), y: parseInt($(controls[3]).css('top')) }
];
var lineElements = ['line1', 'line2', 'line3', 'line4'];
lineElements.forEach(function(line, index) {
var lineElement = document.getElementById(line);
var nextIndex = (index + 1) % 4;
var startPos = controlPositions[index];
var endPos = controlPositions[nextIndex];
var length = Math.sqrt(Math.pow(endPos.x - startPos.x, 2) + Math.pow(endPos.y - startPos.y, 2));
var angle = Math.atan2(endPos.y - startPos.y, endPos.x - startPos.x) * 180 / Math.PI;
$(lineElement).css({
width: length + 'px',
transform: 'rotate(' + angle + 'deg)',
transformOrigin: '0 0', // Rotate around the start point
left: startPos.x + 'px',
top: startPos.y + 'px'
}).show();
});
}
$(document).ready(function() {
// Toggle instructions visibility
$('#toggleInstructions').on('click', function() {
var instructions = $('#instructions');
if (instructions.is(':visible')) {
instructions.hide();
$(this).text('Lihat Petunjuk');
} else {
instructions.show();
$(this).text('Sembunyikan Petunjuk');
}
});
});
});
var draw = function() {
context.clearRect(0, 0, canvas.width, canvas.height);
var render = function(wireframe, image, tri) {
if (wireframe) {
context.strokeStyle = "black";
context.beginPath();
context.moveTo(tri.p0.x, tri.p0.y);
context.lineTo(tri.p1.x, tri.p1.y);
context.lineTo(tri.p2.x, tri.p2.y);
context.lineTo(tri.p0.x, tri.p0.y);
context.stroke();
context.closePath();
}
if (image) {
context.globalAlpha = currentOpacity;
drawTriangle(context, image,
tri.p0.x, tri.p0.y,
tri.p1.x, tri.p1.y,
tri.p2.x, tri.p2.y,
tri.t0.u, tri.t0.v,
tri.t1.u, tri.t1.v,
tri.t2.u, tri.t2.v);
}
};
if (dirtyTriangles) {
dirtyTriangles = false;
calculateGeometry();
}
for (triangle of triangles) {
render(false, image, triangle);
}
};
var calculateGeometry = function() {
triangles = [];
var subs = 5;
var divs = 5;
var p1 = new Point(parseInt($(controls[0]).css('left')), parseInt($(controls[0]).css('top')));
var p2 = new Point(parseInt($(controls[1]).css('left')), parseInt($(controls[1]).css('top')));
var p3 = new Point(parseInt($(controls[2]).css('left')), parseInt($(controls[2]).css('top')));
var p4 = new Point(parseInt($(controls[3]).css('left')), parseInt($(controls[3]).css('top')));
var dx1 = p4.x - p1.x;
var dy1 = p4.y - p1.y;
var dx2 = p3.x - p2.x;
var dy2 = p3.y - p2.y;
var imgW = image.width;
var imgH = image.height;
for (var sub = 0; sub < subs; ++sub) {
var curRow = sub / subs;
var nextRow = (sub + 1) / subs;
var curRowX1 = p1.x + dx1 * curRow;
var curRowY1 = p1.y + dy1 * curRow;
var curRowX2 = p2.x + dx2 * curRow;
var curRowY2 = p2.y + dy2 * curRow;
var nextRowX1 = p1.x + dx1 * nextRow;
var nextRowY1 = p1.y + dy1 * nextRow;
var nextRowX2 = p2.x + dx2 * nextRow;
var nextRowY2 = p2.y + dy2 * nextRow;
for (var div = 0; div < divs; ++div) {
var curCol = div / divs;
var nextCol = (div + 1) / divs;
var dCurX = curRowX2 - curRowX1;
var dCurY = curRowY2 - curRowY1;
var dNextX = nextRowX2 - nextRowX1;
var dNextY = nextRowY2 - nextRowY1;
var p1x = curRowX1 + dCurX * curCol;
var p1y = curRowY1 + dCurY * curCol;
var p2x = curRowX1 + (curRowX2 - curRowX1) * nextCol;
var p2y = curRowY1 + (curRowY2 - curRowY1) * nextCol;
var p3x = nextRowX1 + dNextX * nextCol;
var p3y = nextRowY1 + dNextY * nextCol;
var p4x = nextRowX1 + dNextX * curCol;
var p4y = nextRowY1 + dNextY * curCol;
var u1 = curCol * imgW;
var u2 = nextCol * imgW;
var v1 = curRow * imgH;
var v2 = nextRow * imgH;
var triangle1 = new Triangle(
new Point(p1x-1, p1y),
new Point(p3x+2, p3y+1),
new Point(p4x-1, p4y+1),
new TextCoord(u1, v1),
new TextCoord(u2, v2),
new TextCoord(u1, v2)
);
var triangle2 = new Triangle(
new Point(p1x-2, p1y),
new Point(p2x+1, p2y),
new Point(p3x+1, p3y+1),
new TextCoord(u1, v1),
new TextCoord(u2, v1),
new TextCoord(u2, v2)
);
triangles.push(triangle1);
triangles.push(triangle2);
}
}
};
var drawTriangle = function(ctx, im, x0, y0, x1, y1, x2, y2,
sx0, sy0, sx1, sy1, sx2, sy2) {
ctx.save();
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.clip();
var denom = sx0 * (sy2 - sy1) - sx1 * sy2 + sx2 * sy1 + (sx1 - sx2) * sy0;
if (denom == 0) {
return;
}
var m11 = -(sy0 * (x2 - x1) - sy1 * x2 + sy2 * x1 + (sy1 - sy2) * x0) / denom;
var m12 = (sy1 * y2 + sy0 * (y1 - y2) - sy2 * y1 + (sy2 - sy1) * y0) / denom;
var m21 = (sx0 * (x2 - x1) - sx1 * x2 + sx2 * x1 + (sx1 - sx2) * x0) / denom;
var m22 = -(sx1 * y2 + sx0 * (y1 - y2) - sx2 * y1 + (sx2 - sx1) * y0) / denom;
var dx = (sx0 * (sy2 * x1 - sy1 * x2) + sy0 * (sx1 * x2 - sx2 * x1) + (sx2 * sy1 - sx1 * sy2) * x0) / denom;
var dy = (sx0 * (sy2 * y1 - sy1 * y2) + sy0 * (sx1 * y2 - sx2 * y1) + (sx2 * sy1 - sx1 * sy2) * y0) / denom;
ctx.transform(m11, m12, m21, m22, dx, dy);
ctx.drawImage(im, 0, 0);
ctx.restore();
};
var Point = function(x,y) {
this.x = x ? x : 0;
this.y = y ? y : 0;
}
var TextCoord = function(u,v) {
this.u = u ? u : 0;
this.v = v ? v : 0;
}
var Triangle = function(p0, p1, p2, t0, t1, t2) {
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.t0 = t0;
this.t1 = t1;
this.t2 = t2;
}
function downloadImage() {
// Create a new canvas to combine the container background and the canvas image
var downloadCanvas = document.createElement('canvas');
var downloadContext = downloadCanvas.getContext('2d');
downloadCanvas.width = canvas.width;
downloadCanvas.height = canvas.height;
// Draw the container background
var containerBg = new Image();
containerBg.src = $('#container').css('background-image').replace(/url(['"]?(.*?)['"]?)/, '$1');
containerBg.onload = function() {
downloadContext.drawImage(containerBg, 0, 0, canvas.width, canvas.height);
// Draw the canvas image
downloadContext.drawImage(canvas, 0, 0);
// Create a link element
var link = document.createElement('a');
link.download = 'mockup_hasil.png';
link.href = downloadCanvas.toDataURL('image/png');
link.click();
};
}
/*ZOOM FUNCTION1*/
let zoomFactor = 1; // Initial zoom factor
let isZoomed = false; // Flag to track zoom state
let skewX = 0; // Initial skew values
let skewY = 0;
// Function to handle zoom on mouse wheel
function zoom(e) {
e.preventDefault();
const zoomAmount = 0.1; // Amount to zoom each level
if (e.deltaY < 0) {
zoomFactor += zoomAmount; // Zoom in
} else {
zoomFactor -= zoomAmount; // Zoom out
if (zoomFactor < 1) zoomFactor = 1; // Prevent zooming out too far
}
updateTransform(e.clientX, e.clientY); // Pass the mouse coordinates to adjust zoom
}
// Function to update the transform CSS property
function updateTransform(x, y) {
const container = document.getElementById('container');
const containerRect = container.getBoundingClientRect();
// Calculate the position relative to the container
const offsetX = (x - containerRect.left) / containerRect.width;
const offsetY = (y - containerRect.top) / containerRect.height;
container.style.transform = `scale(${zoomFactor}) skew(${skewX}deg, ${skewY}deg)`;
container.style.transformOrigin = `${offsetX * 100}% ${offsetY * 100}%`; // Adjust transform origin based on the click position
}
// Function to toggle zoom
function toggleZoom(e) {
const container = document.getElementById('container');
const rect = container.getBoundingClientRect();
const x = e.clientX - rect.left; // Get click position relative to container
const y = e.clientY - rect.top;
if (isZoomed) {
// Zoom out
zoomFactor = 1;
container.classList.remove('zoom-out-cursor');
container.classList.add('zoom-in-cursor');
} else {
// Zoom in
zoomFactor = 1.5;
container.classList.remove('zoom-in-cursor');
container.classList.add('zoom-out-cursor');
}
updateTransform(x + rect.left, y + rect.top); // Pass the adjusted coordinates
isZoomed = !isZoomed; // Toggle zoom state
}
// Attach event listener to the canvas
document.getElementById('canvas').addEventListener('click', toggleZoom);
// Function to update skew values
function updateSkew(newSkewX, newSkewY) {
skewX = newSkewX;
skewY = newSkewY;
updateTransform();
}
// Attach zoom functionality to the container-wrapper
document.getElementById('container-wrapper').addEventListener('wheel', zoom);
// Toggle zoom on button click
document.getElementById('zoom-button').addEventListener('click', function() {
toggleZoom({ clientX: window.innerWidth / 2, clientY: window.innerHeight / 2 }); // Default to center if no event
});
// Click outside the container to close zoom
document.addEventListener('click', function(event) {
if (isZoomed && !event.target.closest('#container-wrapper, #zoom-button')) {
// Reset zoom
zoomFactor = 1;
updateTransform();
isZoomed = false;
}
});
// Ensure the zoom is applied correctly when clicking inside the container-wrapper
document.getElementById('container-wrapper').addEventListener('click', function(e) {
e.stopPropagation(); // Prevent the click event from bubbling up to the document
});
// Example function to update skew and reapply transformations
function setSkew(x, y) {
updateSkew(x, y); // Update skew values and adjust transformations
}
</script>
I’ve tried to register the updateRedDots(x, y); to the interact.js dragging. But still no luck.
setiawanfarlin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.