Elemento de escala proporcional a Background Cover con jQuery

Tengo una pregunta difícil: tengo un fondo completo sobre el sitio en el que estoy trabajando. Ahora quiero adjuntar un div a cierta posición en la imagen y también que el div se escala de la misma manera que la imagen de fondo con la propiedad “background-size: cover”. Entonces en este ejemplo, tengo una imagen de una ciudad, que cubre la ventana del navegador y quiero que mi div se superponga a un edificio en particular, sin importar el tamaño de la ventana.

Ya logré hacer que el div se quedara en una posición, pero no puedo ajustar el tamaño correctamente. Lo que hice hasta ahora

http://codepen.io/EmmieBln/pen/YqWaYZ

var imageWidth = 1920, imageHeight = 1368, imageAspectRatio = imageWidth / imageHeight, $window = $(window); var hotSpots = [{ 'x': -160, 'y': -20, 'height': 400, 'width': 300 }]; function appendHotSpots() { for (var i = 0; i < hotSpots.length; i++) { var $hotSpot = $('
').addClass('hot-spot'); $('.container').append($hotSpot); } positionHotSpots(); } function positionHotSpots() { var windowWidth = $window.width(), windowHeight = $window.height(), windowAspectRatio = windowWidth / windowHeight, $hotSpot = $('.hot-spot'); $hotSpot.each(function(index) { var xPos = hotSpots[index]['x'], yPos = hotSpots[index]['y'], xSize = hotSpots[index]['width'], ySize = hotSpots[index]['height'], desiredLeft = 0, desiredTop = 0; if (windowAspectRatio > imageAspectRatio) { yPos = (yPos / imageHeight) * 100; xPos = (xPos / imageWidth) * 100; xSize = (xSize / imageWidth) * 1000; ySize = (ySize / imageHeight) * 1000; } else { yPos = ((yPos / (windowAspectRatio / imageAspectRatio)) / imageHeight) * 100; xPos = ((xPos / (windowAspectRatio / imageAspectRatio)) / imageWidth) * 100; } $(this).css({ 'margin-top': yPos + '%', 'margin-left': xPos + '%', 'width': xSize + 'px', 'height': ySize + 'px' }); }); } appendHotSpots(); $(window).resize(positionHotSpots);

Mi idea era: If (imageWidth / windowWidth) <1 luego establecer Valor para var Scale = (windowWidth / imageWidth) else var Scale (windowHeight / imageHeight) y usar var Scale para transform: scale (Scale, Scale) pero no pude lograr que esto funcione …

Tal vez ustedes podrían ayudarme …

    Solución para el fondo de pantalla: cubierta

    Estoy tratando de darte una solución (o considerarla como una idea). Puede consultar la demostración de trabajo aquí . Cambiar el tamaño de la ventana para ver el resultado.

    En primer lugar, no entendí por qué está utilizando transform , top:50% y a la left:50% para zona activa. Así que traté de resolver esto usando un mínimo de uso y ajustando su marcado y CSS para mi conveniencia.

    Aquí rImage es la relación de aspecto de la imagen original.

      var imageWidth = 1920; var imageHeight = 1368; var h = { x: imageWidth / 2, y: imageHeight / 2, height: 100, width: 50 }; var rImage= imageWidth / imageHeight; 

    En el controlador de cambio de tamaño de ventana, calcule la relación de aspecto de la ventana gráfica r . A continuación, el truco es encontrar las dimensiones de la imagen cuando cambiamos el tamaño de la ventana. Pero, la ventana gráfica recortará la imagen para mantener la relación de aspecto. Entonces, para calcular las dimensiones de la imagen, necesitamos alguna fórmula.

    Cuando se utiliza el background-size:cover para calcular las dimensiones de la imagen, a continuación se utilizan las fórmulas.

     if(actual_image_aspectratio < = viewport_aspectratio) image_width = width_of_viewport image_height = width_ofviewport / actual_image_aspectratio 

    Y

     if(actual_image_aspectratio > viewport_aspectratio) image_width = height_of_viewport * actual_image_aspectratio image_height = height_of_viewport 

    Puede consultar esta URL para obtener más información sobre el cálculo de las dimensiones de la imagen al usar background-size:cover .

    Después de obtener las dimensiones de la imagen, debemos trazar las coordenadas del punto caliente de la imagen real a las nuevas dimensiones de la imagen.

    Para ajustar la imagen en la imagen de la ventana gráfica, se recortará en la parte superior e inferior / izquierda y derecha de la imagen. Por lo tanto, debemos considerar este tamaño de imagen recortada como un desplazamiento al trazar puntos de acceso.

     offset_top=(image_height-viewport_height)/2 offset_left=(image_width-viewport_width)/2 

    agregue estos valores de desplazamiento a las coordenadas x,y cada punto de acceso

     var imageWidth = 1920; var imageHeight = 1368; var hotspots = [{ x: 100, y: 200, height: 100, width: 50 }, { x: 300, y: 500, height: 200, width: 100 }, { x: 600, y: 600, height: 150, width: 100 }, { x: 900, y: 550, height: 100, width: 25 }]; var aspectRatio = imageWidth / imageHeight; $(window).resize(function() { positionHotSpots(); }); var positionHotSpots = function() { $('.hotspot').remove(); var wi = 0, hi = 0; var r = $('#image').width() / $('#image').height(); if (aspectRatio < = r) { wi = $('#image').width(); hi = $('#image').width() / aspectRatio; } else { wi = $('#image').height() * aspectRatio; hi = $('#image').height(); } var offsetTop = (hi - $('#image').height()) / 2; var offsetLeft = (wi - $('#image').width()) / 2; $.each(hotspots, function(i, h) { var x = (wi * hx) / imageWidth; var y = (hi * hy) / imageHeight; var ww = (wi * (h.width)) / imageWidth; var hh = (hi * (h.height)) / imageHeight; var hotspot = $('
    ').addClass('hotspot').css({ top: y - offsetTop, left: x - offsetLeft, height: hh, width: ww }); $('body').append(hotspot); }); }; positionHotSpots();
     html, body { height: 100%; padding: 0; margin: 0; } #image { height: 100%; width: 100%; background: url('https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Alexanderplatz_Stadtmodell_1.jpg/1920px-Alexanderplatz_Stadtmodell_1.jpg'); background-size: cover; background-repeat: no-repeat; background-position: center; } .hotspot { position: absolute; z-index: 1; background: red; } 
      

    De acuerdo, muchas personas no conocen las medidas de CSS vh y vw (es decir, viewheight y viewwidth). Creé una secuencia de comandos que se ejecuta una vez en la carga de página (a diferencia de otras respuestas con ~ 50 líneas de código en cada cambio de tamaño ).

    Calcula la proporción de la imagen de fondo, aplica dos piezas de CSS a overlayContainer , y está hecho.

    También agregué un div con un square identificación. Todo lo que hace es crear un cuadrado para que trabajes con una proporción de 1: 1, en lugar de la proporción que tiene el fondo. Esto garantiza que, si desea crear un cuadrado, puede usar el mismo ancho y alto, en lugar de intentar crear uno con valores diferentes. Esto también es útil cuando modificas ligeramente el tamaño de la imagen de fondo, porque con ese cuadrado, tus divs superpuestos no perderán su relación de aspecto.

    Para el background-size: cover , ver este violín .

    Para el background-size: contain , ver este violín .

    El HTML necesario:

     

    El CSS necesitaba:

     #overlayContainer{ position: absolute; /* Fixed if the background-image is also fixed */ min-width: 100vw; /* When cover is applied */ min-height: 100vh; /* When cover is applied */ max-width: 100vw; /* When contain is applied */ max-height: 100vh; /* When contain is applied */ top: 50%; left: 50%; transform: translate(-50%, -50%); } #square{ position: relative; padding-bottom: 100%; } /* When creating divs, make them all absolutely positioned, and work with percentages only */ /* I advise looking at my Fiddle for an example */ 

    El JavaScript necesario:

     var image = new Image() image.src = $('body').css('background-image').replace(/url\((['"])?(.*?)\1\)/gi,'$2').split(',')[0] /* When cover is applied, use this: */ $('#overlayContainer').css({'height':100/(image.width/image.height)+'vw','width':100/(image.height/image.width)+'vh'}) /* When contain is applied, use this: */ $('#overlayContainer').css({'height':100*(image.height/image.width)+'vw','width':100*(image.width/image.height)+'vh'}) 

    Espero que esto ayude


    Actualización por @LGSon

    No esperaba encontrar una solución de CSS única , aunque aquí está, ocultándose en esta respuesta, y por lo tanto, decidí agregarla a la misma.

    Al agregar estas 2 líneas a la regla #overlayContainer (funciona tanto para la cover como para contain ), la secuencia de comandos se puede descartar.

     width: calc(100vh * (1920 / 1368)); height: calc(100vw * (1368 / 1920)); 

    Por supuesto, la versión de script tiene la ventaja de obtener automáticamente los valores, aunque debido a que los puntos de acceso tienen un punto de ubicación específico en el fondo, es muy probable que se conozca el tamaño de la imagen.

    Muestra con background-size: cover

     html, body{ height: 100%; overflow: hidden; } body{ margin: 0; background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Alexanderplatz_Stadtmodell_1.jpg/1920px-Alexanderplatz_Stadtmodell_1.jpg'); background-size: cover; background-repeat: no-repeat; background-position: center; } #overlayContainer{ position: absolute; width: calc(100vh * (1920 / 1368)); height: calc(100vw * (1368 / 1920)); min-width: 100vw; /* for cover */ min-height: 100vh; /* for cover */ /* max-width: 100vw; for contain */ /* max-height: 100vh; for contain */ top: 50%; left: 50%; transform: translate(-50%, -50%); } #square{ position: relative; padding-bottom: 100%; } #square div{ position: absolute; top: 19.75%; left: 49.75%; width: 4.75%; height: 4.75%; background-color: rgba(255,0,0,.7); border-radius: 50%; } 
     

    Ok, intenté usar tu idea original y modifiqué solo algunas partes aquí y allá.

    En lugar de usar porcentajes, me resultó más fácil usar valores de píxeles. Asi que:

     $(this).css({ 'margin-top': yPos + 'px', 'margin-left': xPos + 'px', 'width': xSize + 'px', 'height': ySize + 'px' }); 

    Entonces, todo lo que tenemos que hacer es verificar la proporción de la ventana gráfica para ver cómo tenemos que modificar las propiedades del div .

     if (windowAspectRatio > imageAspectRatio) { var ratio = windowWidth / imageWidth; } else { var ratio = windowHeight / imageHeight; } xPos = xPos * ratio; yPos = yPos * ratio; xSize = xSize * ratio; ySize = ySize * ratio; 

    Ejemplo de trabajo: http://codepen.io/jaimerodas/pen/RaGQVm

    Fragmento de stack

     var imageWidth = 1920, imageHeight = 1368, imageAspectRatio = imageWidth / imageHeight, $window = $(window); var hotSpots = [{ x: -210, y: -150, height: 250, width: 120 }, { x: 240, y: 75, height: 85, width: 175 }]; function appendHotSpots() { for (var i = 0; i < hotSpots.length; i++) { var $hotSpot = $('
    ').addClass('hot-spot'); $('.container').append($hotSpot); } positionHotSpots(); } function positionHotSpots() { var windowWidth = $window.width(), windowHeight = $window.height(), windowAspectRatio = windowWidth / windowHeight, $hotSpot = $('.hot-spot'); $hotSpot.each(function(index) { var cambio = 1, xPos = hotSpots[index]['x'], yPos = hotSpots[index]['y'], xSize = hotSpots[index]['width'], ySize = hotSpots[index]['height'], desiredLeft = 0, desiredTop = 0; if (windowAspectRatio > imageAspectRatio) { var ratio = windowWidth / imageWidth; } else { var ratio = windowHeight / imageHeight; } xPos = xPos * ratio; yPos = yPos * ratio; xSize = xSize * ratio; ySize = ySize * ratio; $(this).css({ 'margin-top': yPos + 'px', 'margin-left': xPos + 'px', 'width': xSize + 'px', 'height': ySize + 'px' }); }); } appendHotSpots(); $(window).resize(positionHotSpots);
     html, body { margin: 0; width: 100%; height: 100%; } .container { width: 100%; height: 100%; position: relative; background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Alexanderplatz_Stadtmodell_1.jpg/1920px-Alexanderplatz_Stadtmodell_1.jpg); background-size: cover; background-repeat: no-repeat; background-position: center; } .hot-spot { background-color: red; border-radius: 0; position: absolute; top: 50%; left: 50%; z-index: 1; opacity: 0.8; content: ""; } 
      

    Confiar en las transformaciones de css y aplicarlo a un solo elemento le proporciona un rendimiento mucho mejor independientemente de la cantidad de puntos de acceso (menos manipulaciones DOM y muchos menos re-flujos). La aceleración de hardware también es agradable 🙂

    Primero, meta-código:

    1. Crea un .hot-spot--container dentro de tu imagen .container

    2. Cree .hot-spot y .hot-spot / .hot-spot--container dentro del .hot-spot--container

    3. Transformar .hot-spot--container imita el background-size: cover comportamiento de la background-size: cover

    4. Repite # 3 cada vez que hay un tamaño

    Calcule su relación de imagen bg:

     var bgHeight = 1368; var bgWidth = 1920; var bgRatio = bgHeight / bgWidth; 

    Cada vez que se vuelva a dimensionar la ventana, vuelva a calcular la proporción de contenedores:

     var containerHeight = $container.height(); var containerWidth = $container.width(); var containerRatio = containerHeight / containerWidth; 

    Calcule los factores de escala para imitar background-size: cover comportamiento de background-size: cover

     if (containerRatio > bgRatio) { //fgHeight = containerHeight //fgWidth = containerHeight / bgRatio xScale = (containerHeight / bgRatio) / containerWidth } else { //fgHeight = containerWidth / bgRatio //fgWidth = containerWidth yScale = (containerWidth * bgRatio) / containerHeight } 

    … y aplicar la transformación al elemento contenedor de zonas activas, esencialmente redimensionándola y reposicionándola “en sincronización” con el fondo:

     var transform = 'scale(' + xScale + ', ' + yScale + ')'; $hotSpotContainer.css({ 'transform': transform }); 

    Fiddled: https://jsfiddle.net/ovfiddle/a3pdLodm/ (puede jugar con la ventana de vista previa con bastante eficacia. Tenga en cuenta que el código se puede ajustar para tomar las dimensiones basadas en píxeles y el posicionamiento de los puntos críticos, solo tendrá que considerar tamaños de contenedor e imagen al calcular los valores de escala)

    Actualización: el comportamiento background-size: contain usa el mismo cálculo, excepto cuando el containerRatio es más pequeño que el bgRatio. Actualizar el CSS de fondo y darle la vuelta al letrero es suficiente .

    A continuación se muestra una solución jQuery, el complemento bgCoverTool reposiciona un elemento basado en la escala de la imagen de fondo del padre.

     //bgCoverTool Properties $('.hot-spot').bgCoverTool({ parent: $('#container'), top: '100px', left: '100px', height: '100px', width: '100px'}) 

    Manifestación:

     $(function() { $('.hot-spot').bgCoverTool(); }); 
     html, body { height: 100%; padding: 0; margin: 0; } #container { height: 100%; width: 100%; background: url('https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Alexanderplatz_Stadtmodell_1.jpg/1920px-Alexanderplatz_Stadtmodell_1.jpg'); background-size: cover; background-repeat: no-repeat; position: relative; } .hot-spot { position: absolute; z-index: 1; background: red; left: 980px; top: 400px; height: 40px; width: 40px; opacity: 0.7; } 
     < !DOCTYPE html>    BG Cover Tool     

    Un enfoque mucho más simple / mejor para su problema es usar un elemento SVG, que se adapta mejor a sus necesidades. Lo bueno de SVG es que todo se escalará proporcionalmente de forma predeterminada porque es un objeto vectorial, no un objeto de flujo de documentos.

    Este ejemplo demostrará la técnica

     < !DOCTYPE html>   SVG             

    Puede agregar imágenes a SVG para lograr lo que necesita.

    https://jsfiddle.net/tnt1/3f23amue/