You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, vintageEffect = 1, lensSize = 150, MathMagnification = 2) {
// Parse parameters
const useVintage = Number(vintageEffect) > 0;
const size = Number(lensSize) || 150;
const mag = Number(MathMagnification) || 2;
// Define reasonable maximum display dimensions to keep UI clean
const maxWidth = 800;
const maxHeight = 600;
const scale = Math.min(maxWidth / originalImg.width, maxHeight / originalImg.height, 1);
const w = originalImg.width * scale;
const h = originalImg.height * scale;
// Create main container (The "Case File" wrapper)
const container = document.createElement('div');
container.style.position = 'relative';
container.style.width = w + 'px';
container.style.height = h + 'px';
container.style.overflow = 'hidden';
container.style.cursor = 'none'; // Hide default cursor, replaced by magnifying lens
container.style.display = 'inline-block';
container.style.fontFamily = 'monospace';
container.style.boxShadow = '0 10px 30px rgba(0,0,0,0.5)';
container.style.backgroundColor = '#1a1a1a';
// Create Base Canvas (The vintage investigation photo)
const baseCanvas = document.createElement('canvas');
baseCanvas.width = w;
baseCanvas.height = h;
baseCanvas.style.display = 'block';
const ctx = baseCanvas.getContext('2d');
// Draw original image initially
ctx.drawImage(originalImg, 0, 0, w, h);
if (useVintage) {
try {
// Attempt pixel manipulation for optimal sepia & noise
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i+1];
const b = data[i+2];
// Sepia conversion
const tr = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
const tg = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
const tb = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
// Add minor static noise for vintage photo effect
const noise = (Math.random() - 0.5) * 40;
data[i] = Math.min(255, Math.max(0, tr + noise));
data[i+1] = Math.min(255, Math.max(0, tg + noise));
data[i+2] = Math.min(255, Math.max(0, tb + noise));
}
ctx.putImageData(imgData, 0, 0);
} catch(e) {
// Fallback for CORS-tainted images
ctx.filter = 'sepia(1) contrast(1.2) brightness(0.8)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
}
// Add Vignette for focus
const gradient = ctx.createRadialGradient(w/2, h/2, Math.min(w, h) * 0.3, w/2, h/2, Math.max(w, h) * 0.8);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.8)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
// Add classification stamp / title ("Дело о Драконе и Георгии")
ctx.fillStyle = '#ff3333';
ctx.font = 'bold 20px "Courier New", Courier, monospace';
ctx.shadowColor = 'rgba(0,0,0,0.9)';
ctx.shadowBlur = 5;
// Add stamp rotation for realism
ctx.save();
ctx.translate(20, 30);
ctx.rotate(-0.05); // slight angle
ctx.fillText("ДЕЛО: ДРАКОН И ГЕОРГИЙ", 0, 0);
ctx.font = '14px "Courier New", Courier, monospace';
ctx.fillText("СОВЕРШЕННО СЕКРЕТНО", 0, 20);
ctx.restore();
}
container.appendChild(baseCanvas);
// Create Detective Lens element (Magnifying glass)
const lens = document.createElement('div');
lens.style.position = 'absolute';
lens.style.width = size + 'px';
lens.style.height = size + 'px';
lens.style.borderRadius = '50%';
lens.style.border = '5px solid #bd9b16'; // Brass/gold rim
lens.style.boxShadow = '0 0 15px rgba(0,0,0,0.8), inset 0 0 20px rgba(0,0,0,0.6)';
lens.style.pointerEvents = 'none';
lens.style.overflow = 'hidden';
lens.style.display = 'none';
lens.style.zIndex = '10';
lens.style.backgroundColor = '#ffffff';
// Lens reflection/gloss element
const gloss = document.createElement('div');
gloss.style.position = 'absolute';
gloss.style.top = '2%';
gloss.style.left = '10%';
gloss.style.width = '80%';
gloss.style.height = '45%';
gloss.style.background = 'linear-gradient(to bottom, rgba(255,255,255,0.45) 0%, rgba(255,255,255,0) 100%)';
gloss.style.borderRadius = '50% 50% 15% 15%';
gloss.style.pointerEvents = 'none';
gloss.style.zIndex = '12';
lens.appendChild(gloss);
// Inner reticle / crosshairs
const crosshairH = document.createElement('div');
crosshairH.style.position = 'absolute';
crosshairH.style.top = '50%';
crosshairH.style.left = '0';
crosshairH.style.width = '100%';
crosshairH.style.height = '1px';
crosshairH.style.backgroundColor = 'rgba(189, 155, 22, 0.4)';
crosshairH.style.zIndex = '11';
lens.appendChild(crosshairH);
const crosshairV = document.createElement('div');
crosshairV.style.position = 'absolute';
crosshairV.style.left = '50%';
crosshairV.style.top = '0';
crosshairV.style.height = '100%';
crosshairV.style.width = '1px';
crosshairV.style.backgroundColor = 'rgba(189, 155, 22, 0.4)';
crosshairV.style.zIndex = '11';
lens.appendChild(crosshairV);
// Zoom Image representing the unaffected, true-color deep view
const zoomCanvas = document.createElement('canvas');
zoomCanvas.width = w * mag;
zoomCanvas.height = h * mag;
zoomCanvas.style.position = 'absolute';
zoomCanvas.style.zIndex = '9';
const zCtx = zoomCanvas.getContext('2d');
zCtx.imageSmoothingEnabled = false; // Sharp pixel investigation
zCtx.drawImage(originalImg, 0, 0, zoomCanvas.width, zoomCanvas.height);
lens.appendChild(zoomCanvas);
container.appendChild(lens);
// Physics / Move logic
const moveLens = (e) => {
e.preventDefault();
const rect = container.getBoundingClientRect();
let clientX = e.clientX;
let clientY = e.clientY;
// Touch screen support
if (e.touches && e.touches.length > 0) {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
}
const x = clientX - rect.left;
const y = clientY - rect.top;
const halfLens = size / 2;
// Position lens precisely under cursor
lens.style.left = (x - halfLens) + 'px';
lens.style.top = (y - halfLens) + 'px';
// Position large zoom canvas inversely to reveal specific section
const zx = (x * mag) - halfLens;
const zy = (y * mag) - halfLens;
zoomCanvas.style.left = '-' + zx + 'px';
zoomCanvas.style.top = '-' + zy + 'px';
};
// Attach Event Listeners
container.addEventListener('mouseenter', () => { lens.style.display = 'block'; });
container.addEventListener('mouseleave', () => { lens.style.display = 'none'; });
container.addEventListener('mousemove', moveLens);
// Touch screen overrides
container.addEventListener('touchmove', moveLens, {passive: false});
container.addEventListener('touchstart', (e) => {
lens.style.display = 'block';
moveLens(e);
}, {passive: false});
container.addEventListener('touchend', () => { lens.style.display = 'none'; });
return container;
}
Apply Changes