Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Im谩genes Interactivas</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| .draggable { | |
| position: absolute; | |
| cursor: grab; | |
| touch-action: none; | |
| user-select: none; | |
| transition: transform 0.1s ease; | |
| } | |
| .draggable:active { | |
| cursor: grabbing; | |
| transform: scale(1.05); | |
| z-index: 10; | |
| } | |
| @media (hover: none) { | |
| .draggable { | |
| touch-action: manipulation; | |
| } | |
| .draggable:active { | |
| transform: scale(1.1); | |
| } | |
| } | |
| #background-container { | |
| position: relative; | |
| width: 100%; | |
| overflow: hidden; | |
| } | |
| #background-img { | |
| width: 100%; | |
| height: auto; | |
| display: block; | |
| } | |
| #draggables-container { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| @media (max-width: 640px) { | |
| .draggable { | |
| width: 30% ; | |
| min-width: 100px; | |
| } | |
| #background-container { | |
| min-height: 300px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <h1 class="text-4xl font-bold text-center mb-8 text-indigo-700">Crea tu composici贸n</h1> | |
| <p class="text-center mb-8 text-gray-600 max-w-2xl mx-auto"> | |
| Arrastra las im谩genes sobre el fondo para crear tu dise帽o personalizado. | |
| Puedes moverlas con el rat贸n en desktop o con el dedo en m贸viles. | |
| </p> | |
| <div class="bg-white rounded-xl shadow-xl overflow-hidden mb-8"> | |
| <div id="background-container" class="relative"> | |
| <img id="background-img" src="https://www.solonoticias.news/i/fondo.png" alt="Fondo" class="w-full"> | |
| <div id="draggables-container"></div> | |
| </div> | |
| </div> | |
| <div class="flex flex-col items-center space-y-4 mb-8"> | |
| <div class="flex space-x-4"> | |
| <button id="reset-btn" class="px-6 py-3 bg-red-500 text-white rounded-lg hover:bg-red-600 transition"> | |
| Reiniciar Posiciones | |
| </button> | |
| <button id="random-btn" class="px-6 py-3 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition"> | |
| Posici贸n Aleatoria | |
| </button> | |
| </div> | |
| <div class="w-64"> | |
| <label for="size-slider" class="block text-sm font-medium text-gray-700 mb-1">Tama帽o de im谩genes</label> | |
| <input type="range" id="size-slider" min="10" max="100" value="100" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const backgroundImg = document.getElementById('background-img'); | |
| const draggablesContainer = document.getElementById('draggables-container'); | |
| const resetBtn = document.getElementById('reset-btn'); | |
| const randomBtn = document.getElementById('random-btn'); | |
| // Image paths with custom base sizes (width, height) | |
| const imagePaths = [ | |
| { path: 'bailadora.png', originalWidth: 600, originalHeight: 1000 }, | |
| { path: 'botella.png', originalWidth: 400, originalHeight: 900 }, | |
| { path: 'copas.png', originalWidth: 750, originalHeight: 750 }, | |
| { path: 'hombre.png', originalWidth: 750, originalHeight: 1000 }, | |
| { path: 'litro.png', originalWidth: 600, originalHeight: 600 }, | |
| { path: 'toro.png', originalWidth: 900, originalHeight: 600 } | |
| ]; | |
| // Base URL for images | |
| const baseUrl = 'https://www.solonoticias.news/i/'; | |
| // Store original positions | |
| const originalPositions = []; | |
| let scaleFactor = 1; | |
| // Global scale for draggable images | |
| let globalScale = 1; | |
| // Update all draggable sizes proportionally | |
| function updateDraggableSizes() { | |
| const draggables = document.querySelectorAll('.draggable'); | |
| draggables.forEach((draggable, index) => { | |
| const originalPos = originalPositions[index]; | |
| const newWidth = originalPos.width * scaleFactor * globalScale; | |
| const newHeight = originalPos.height * scaleFactor * globalScale; | |
| draggable.style.width = `${newWidth}px`; | |
| draggable.style.height = `${newHeight}px`; | |
| // Adjust position to prevent going out of bounds | |
| const maxX = backgroundImg.width - newWidth; | |
| const maxY = backgroundImg.height - newHeight; | |
| const currentLeft = parseFloat(draggable.style.left); | |
| const currentTop = parseFloat(draggable.style.top); | |
| draggable.style.left = `${Math.max(0, Math.min(maxX, currentLeft))}px`; | |
| draggable.style.top = `${Math.max(0, Math.min(maxY, currentTop))}px`; | |
| }); | |
| } | |
| // Initialize the app | |
| function init() { | |
| // Calculate scale factor based on background image dimensions | |
| backgroundImg.onload = function() { | |
| const originalWidth = this.naturalWidth; | |
| const displayedWidth = this.width; | |
| scaleFactor = displayedWidth / originalWidth; | |
| // Create draggable images | |
| createDraggableImages(); | |
| }; | |
| // Handle window resize | |
| window.addEventListener('resize', function() { | |
| // Recalculate scale factor | |
| const originalWidth = backgroundImg.naturalWidth; | |
| const displayedWidth = backgroundImg.width; | |
| scaleFactor = displayedWidth / originalWidth; | |
| // Update all draggable positions | |
| const draggables = document.querySelectorAll('.draggable'); | |
| draggables.forEach((draggable, index) => { | |
| const originalPos = originalPositions[index]; | |
| draggable.style.left = `${originalPos.x * scaleFactor}px`; | |
| draggable.style.top = `${originalPos.y * scaleFactor}px`; | |
| draggable.style.width = `${originalPos.width * scaleFactor}px`; | |
| }); | |
| }); | |
| } | |
| // Create draggable images | |
| function createDraggableImages() { | |
| // Clear container | |
| draggablesContainer.innerHTML = ''; | |
| originalPositions.length = 0; | |
| // Create each draggable image | |
| imagePaths.forEach((imgPath, index) => { | |
| const img = document.createElement('img'); | |
| img.src = baseUrl + imgPath.path; | |
| img.alt = imgPath.path.split('.')[0]; | |
| img.className = 'draggable'; | |
| // Set initial size (scaled down from original dimensions) | |
| const width = imgPath.originalWidth * scaleFactor * globalScale; | |
| const height = imgPath.originalHeight * scaleFactor * globalScale; | |
| // Set random initial position within bounds | |
| const maxX = backgroundImg.width - width; | |
| const maxY = backgroundImg.height - height; | |
| const x = Math.random() * maxX; | |
| const y = Math.random() * maxY; | |
| img.style.width = `${width}px`; | |
| img.style.height = `${height}px`; | |
| img.style.left = `${x}px`; | |
| img.style.top = `${y}px`; | |
| // Store original position (in original image coordinates) | |
| originalPositions.push({ | |
| x: x / scaleFactor, | |
| y: y / scaleFactor, | |
| width: imgPath.originalWidth, | |
| height: imgPath.originalHeight | |
| }); | |
| // Make draggable and add double-click handler | |
| makeDraggable(img); | |
| img.addEventListener('dblclick', function() { | |
| duplicateImage(img); | |
| }); | |
| // Add to container | |
| draggablesContainer.appendChild(img); | |
| }); | |
| } | |
| // Duplicate an image | |
| function duplicateImage(originalImg) { | |
| const img = document.createElement('img'); | |
| img.src = originalImg.src; | |
| img.alt = originalImg.alt; | |
| img.className = 'draggable'; | |
| // Copy dimensions | |
| img.style.width = originalImg.style.width; | |
| img.style.height = originalImg.style.height; | |
| // Position slightly offset from original | |
| const originalLeft = parseFloat(originalImg.style.left); | |
| const originalTop = parseFloat(originalImg.style.top); | |
| img.style.left = `${originalLeft + 20}px`; | |
| img.style.top = `${originalTop + 20}px`; | |
| // Make draggable and add double-click/tap handlers | |
| makeDraggable(img); | |
| img.addEventListener('dblclick', function() { | |
| duplicateImage(img); | |
| }); | |
| // Add to container | |
| draggablesContainer.appendChild(img); | |
| // Store original position (in original image coordinates) | |
| const originalIndex = Array.from(document.querySelectorAll('.draggable')).indexOf(originalImg); | |
| const originalPos = originalPositions[originalIndex]; | |
| originalPositions.push({ | |
| x: (originalLeft + 20) / scaleFactor, | |
| y: (originalTop + 20) / scaleFactor, | |
| width: originalPos.width, | |
| height: originalPos.height | |
| }); | |
| } | |
| // Make an element draggable | |
| function makeDraggable(element) { | |
| let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; | |
| // Mouse events | |
| element.onmousedown = dragMouseDown; | |
| // Touch events | |
| let lastTouchTime = 0; | |
| element.addEventListener('touchstart', function(e) { | |
| const currentTime = new Date().getTime(); | |
| if (currentTime - lastTouchTime < 300) { | |
| // Double tap detected | |
| duplicateImage(element); | |
| e.preventDefault(); | |
| } | |
| lastTouchTime = currentTime; | |
| dragTouchStart(e); | |
| }, { passive: false }); | |
| function dragMouseDown(e) { | |
| e = e || window.event; | |
| e.preventDefault(); | |
| // Get the mouse cursor position at startup | |
| pos3 = e.clientX; | |
| pos4 = e.clientY; | |
| document.onmouseup = closeDragElement; | |
| document.onmousemove = elementDrag; | |
| } | |
| function dragTouchStart(e) { | |
| e.preventDefault(); | |
| const touch = e.touches[0]; | |
| // Get the touch position at startup | |
| pos3 = touch.clientX; | |
| pos4 = touch.clientY; | |
| document.ontouchend = closeDragElement; | |
| document.ontouchmove = elementDragTouch; | |
| } | |
| function elementDrag(e) { | |
| e = e || window.event; | |
| e.preventDefault(); | |
| // Calculate the new cursor position | |
| pos1 = pos3 - e.clientX; | |
| pos2 = pos4 - e.clientY; | |
| pos3 = e.clientX; | |
| pos4 = e.clientY; | |
| // Set the element's new position | |
| const newTop = element.offsetTop - pos2; | |
| const newLeft = element.offsetLeft - pos1; | |
| // Constrain to container bounds | |
| const maxTop = draggablesContainer.offsetHeight - element.offsetHeight; | |
| const maxLeft = draggablesContainer.offsetWidth - element.offsetWidth; | |
| element.style.top = `${Math.max(0, Math.min(maxTop, newTop))}px`; | |
| element.style.left = `${Math.max(0, Math.min(maxLeft, newLeft))}px`; | |
| } | |
| function elementDragTouch(e) { | |
| e.preventDefault(); | |
| const touch = e.touches[0]; | |
| // Calculate the new touch position | |
| pos1 = pos3 - touch.clientX; | |
| pos2 = pos4 - touch.clientY; | |
| pos3 = touch.clientX; | |
| pos4 = touch.clientY; | |
| // Set the element's new position | |
| const newTop = element.offsetTop - pos2; | |
| const newLeft = element.offsetLeft - pos1; | |
| // Constrain to container bounds | |
| const maxTop = draggablesContainer.offsetHeight - element.offsetHeight; | |
| const maxLeft = draggablesContainer.offsetWidth - element.offsetWidth; | |
| element.style.top = `${Math.max(0, Math.min(maxTop, newTop))}px`; | |
| element.style.left = `${Math.max(0, Math.min(maxLeft, newLeft))}px`; | |
| } | |
| function closeDragElement() { | |
| // Stop moving when mouse/touch is released | |
| document.onmouseup = null; | |
| document.onmousemove = null; | |
| document.ontouchend = null; | |
| document.ontouchmove = null; | |
| } | |
| } | |
| // Reset positions | |
| resetBtn.addEventListener('click', function() { | |
| const draggables = document.querySelectorAll('.draggable'); | |
| draggables.forEach((draggable, index) => { | |
| const originalPos = originalPositions[index]; | |
| draggable.style.left = `${originalPos.x * scaleFactor}px`; | |
| draggable.style.top = `${originalPos.y * scaleFactor}px`; | |
| }); | |
| }); | |
| // Random positions | |
| randomBtn.addEventListener('click', function() { | |
| const draggables = document.querySelectorAll('.draggable'); | |
| draggables.forEach(draggable => { | |
| const maxX = backgroundImg.width - draggable.offsetWidth; | |
| const maxY = backgroundImg.height - draggable.offsetHeight; | |
| const x = Math.random() * maxX; | |
| const y = Math.random() * maxY; | |
| draggable.style.left = `${x}px`; | |
| draggable.style.top = `${y}px`; | |
| }); | |
| }); | |
| // Size slider control | |
| document.getElementById('size-slider').addEventListener('input', function(e) { | |
| globalScale = e.target.value / 100; | |
| updateDraggableSizes(); | |
| }); | |
| // Start the app | |
| init(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 馃К <a href="https://enzostvs-deepsite.hf.space?remix=josejuan314/3raya" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |