You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, comparisonImgSrc, orientation = 'horizontal', initialPosition = 50, label1 = 'Before', label2 = 'After') {
/**
* Loads an image from a URL or data URI.
* @param {string} src The source of the image.
* @returns {Promise<HTMLImageElement>} A promise that resolves with the loaded image element.
*/
const loadImage = (src) => {
return new Promise((resolve, reject) => {
const img = new Image();
// Allow loading images from other origins for use in canvas etc.
img.crossOrigin = "Anonymous";
img.onload = () => resolve(img);
img.onerror = (err) => reject(new Error(`Failed to load comparison image: ${err}`));
img.src = src;
});
};
if (!(originalImg instanceof HTMLImageElement)) {
console.error("The first parameter 'originalImg' must be an HTMLImageElement.");
const errorDiv = document.createElement('div');
errorDiv.textContent = 'Error: Invalid original image provided.';
return errorDiv;
}
if (typeof comparisonImgSrc !== 'string' || comparisonImgSrc.trim() === '') {
console.error("The second parameter 'comparisonImgSrc' must be a valid image URL or data URI string.");
const errorDiv = document.createElement('div');
errorDiv.textContent = 'Error: Invalid comparison image source provided.';
return errorDiv;
}
try {
const comparisonImg = await loadImage(comparisonImgSrc);
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
// Create the main container
const container = document.createElement('div');
Object.assign(container.style, {
position: 'relative',
width: `${width}px`,
height: `${height}px`,
overflow: 'hidden',
cursor: orientation === 'horizontal' ? 'ew-resize' : 'ns-resize',
userSelect: 'none',
fontFamily: 'Helvetica, Arial, sans-serif'
});
// Create the original image element (bottom layer)
const originalImgElement = originalImg.cloneNode();
Object.assign(originalImgElement.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
pointerEvents: 'none'
});
container.appendChild(originalImgElement);
// Create a wrapper for the comparison image (for clipping)
const comparisonWrapper = document.createElement('div');
Object.assign(comparisonWrapper.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
overflow: 'hidden',
pointerEvents: 'none'
});
container.appendChild(comparisonWrapper);
// Crate the comparison image element (top layer)
const comparisonImgElement = comparisonImg.cloneNode();
Object.assign(comparisonImgElement.style, {
position: 'absolute',
top: '0',
left: '0',
width: `${width}px`,
height: `${height}px`
});
comparisonWrapper.appendChild(comparisonImgElement);
// Create the slider line and handle
const slider = document.createElement('div');
slider.style.position = 'absolute';
slider.style.zIndex = '10';
const sliderLine = document.createElement('div');
sliderLine.style.position = 'absolute';
sliderLine.style.backgroundColor = 'white';
sliderLine.style.boxShadow = '0px 0px 3px rgba(0, 0, 0, 0.7)';
slider.appendChild(sliderLine);
const sliderHandle = document.createElement('div');
Object.assign(sliderHandle.style, {
position: 'absolute',
width: '44px',
height: '44px',
borderRadius: '50%',
border: '2px solid white',
backgroundColor: 'rgba(0, 0, 0, 0.3)',
boxShadow: '0px 0px 5px rgba(0, 0, 0, 0.7)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
boxSizing: 'border-box'
});
// Add SVG arrows to handle (self-contained)
const arrowSVG = `<svg viewBox="0 0 24 24" width="20" height="20" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`;
sliderHandle.innerHTML = `${arrowSVG}${arrowSVG.replace('15 18 9 12 15 6', '9 18 15 12 9 6')}`;
slider.appendChild(sliderHandle);
// Style slider based on orientation
if (orientation === 'horizontal') {
Object.assign(slider.style, { top: '0', height: '100%', width: '1px', marginLeft: '-0.5px' });
Object.assign(sliderLine.style, { width: '1px', height: '100%', left: '0' });
Object.assign(sliderHandle.style, { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' });
} else { // vertical
Object.assign(slider.style, { left: '0', width: '100%', height: '1px', marginTop: '-0.5px' });
Object.assign(sliderLine.style, { height: '1px', width: '100%', top: '0' });
Object.assign(sliderHandle.style, { left: '50%', top: '50%', transform: 'translate(-50%, -50%) rotate(90deg)' });
}
container.appendChild(slider);
// Add labels
const createLabel = (text) => {
const labelEl = document.createElement('div');
labelEl.textContent = text;
Object.assign(labelEl.style, {
position: 'absolute',
top: '1rem',
padding: '0.5rem 1rem',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
color: 'white',
borderRadius: '4px',
pointerEvents: 'none',
zIndex: '5'
});
return labelEl;
};
const beforeLabelEl = createLabel(label1);
beforeLabelEl.style.left = '1rem';
container.appendChild(beforeLabelEl);
const afterLabelEl = createLabel(label2);
afterLabelEl.style.right = '1rem';
comparisonWrapper.appendChild(afterLabelEl); // Add to wrapper so it gets clipped
// --- Interaction Logic ---
let isDragging = false;
const updateSliderPosition = (value) => {
const clampedValue = Math.max(0, Math.min(value, 100));
if (orientation === 'horizontal') {
slider.style.left = `${clampedValue}%`;
comparisonWrapper.style.width = `${clampedValue}%`;
} else {
slider.style.top = `${clampedValue}%`;
comparisonWrapper.style.height = `${clampedValue}%`;
}
};
const getPositionFromEvent = (e) => {
const rect = container.getBoundingClientRect();
const clientX = e.clientX || e.touches[0].clientX;
const clientY = e.clientY || e.touches[0].clientY;
const x = clientX - rect.left;
const y = clientY - rect.top;
if (orientation === 'horizontal') {
return (x / width) * 100;
} else {
return (y / height) * 100;
}
};
const startSlide = (e) => {
e.preventDefault();
isDragging = true;
window.addEventListener('mousemove', moveSlide, { passive: true });
window.addEventListener('touchmove', moveSlide, { passive: true });
window.addEventListener('mouseup', stopSlide);
window.addEventListener('touchend', stopSlide);
};
const moveSlide = (e) => {
if (!isDragging) return;
const pos = getPositionFromEvent(e);
updateSliderPosition(pos);
};
const stopSlide = () => {
isDragging = false;
window.removeEventListener('mousemove', moveSlide);
window.removeEventListener('touchmove', moveSlide);
window.removeEventListener('mouseup', stopSlide);
window.removeEventListener('touchend', stopSlide);
};
container.addEventListener('mousedown', startSlide);
container.addEventListener('touchstart', startSlide, { passive: false });
// Set initial position
updateSliderPosition(initialPosition);
return container;
} catch (error) {
console.error(error);
const errorDiv = document.createElement('div');
errorDiv.textContent = `Error: ${error.message}`;
return errorDiv;
}
}
Apply Changes