Élément d'échelle proportionnel à la couverture d'arrière-plan avec jQuery

J'ai une question délicate: j'ai un fond complet sur le site sur lequel je travaille. Maintenant, je souhaite attacher un div à une certaine position sur l'image et aussi que le div divise de la même manière que l'image de fond avec la propriété "background-size: cover". Donc dans cet exemple, j'ai une image d'une ville qui couvre la fenêtre du navigateur et je veux que mon div soit superposé à un bâtiment particulier, peu importe la taille de la fenêtre.

J'ai déjà réussi à faire en sorte que le div reste à un seul poste, mais ne peut pas le redimensionner correctement. Ce que j'ai fait jusqu'ici:

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 = $('<div>').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); 

Mon idée était: Si (imageWidth / windowWidth) <1, définissez Value pour var Scale = (windowWidth / imageWidth) else var Scale (windowHeight / imageHeight) et utilisez la var Scale for transform: scale (Scale, Scale) mais je ne pouvais pas Parvient à faire fonctionner cela …

Peut-être que vous pourriez m'aider …

  • Ajouter quelque chose au sommet d'un objet JSON
  • Pausing setInterval lorsque la page / le navigateur est hors de portée
  • Comment conserver les espaces blancs dans le contenu div éditable
  • Copier sur le Presse-papiers sans Flash
  • Problèmes setTimeout / clearTimeout
  • Passer la variable Javascript vers PHP en utilisant Ajax
  • Vérifier si le nombre saisi est un chiffre en jquery
  • Jquery fonctions différées personnalisées
  • 6 Solutions collect form web for “Élément d'échelle proportionnel à la couverture d'arrière-plan avec jQuery”

    Solution pour l'arrière-plan: couverture

    J'essaie de vous donner une solution (ou considérer comme une idée). Vous pouvez vérifier la démonstration de travail ici . Redimensionnez la fenêtre pour voir le résultat.

    Tout d'abord, je ne comprenais pas pourquoi vous utilisez transform , top:50% et à left:50% pour les points chauds. J'ai donc essayé de résoudre ceci en utilisant un cas d'utilisation minimal et j'ai ajusté votre balisage et css pour ma commodité.

    Ici, rImage est le rapport d'aspect de l'image originale.

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

    Dans le mécanisme de redimensionnement des fenêtres, calculez la ration d'aspect de viewport r . Ensuite, l'astuce consiste à trouver les dimensions de l'image lorsque nous redimensionnons la fenêtre. Mais, la fenêtre affichera l'image pour maintenir le rapport d'aspect. Donc, pour calculer les dimensions de l'image, nous avons besoin d'une formule.

    Lorsque vous utilisez la background-size:cover pour calculer les dimensions de l'image, les formules ci-dessous sont utilisées.

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

    Et

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

    Vous pouvez référencer cette URL pour plus de compréhension sur le calcul des dimensions d'image lors de l'utilisation background-size:cover .

    Après avoir obtenu les dimensions de l'image, nous devons tracer les coordonnées des points chauds de l'image réelle aux nouvelles dimensions de l'image.

    Pour correspondre à l'image dans l'image de la fenêtre, vous serez clipsé en haut et en bas / à gauche et à droite de l'image. Nous devrions donc considérer cette taille d'image coupée comme un décalage lors du traçage des points chauds.

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

    Ajoutez ces valeurs de décalage à chaque point d'accès x,y coordonne

     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 = $('<div>').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; } 
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id='image'></div> 

    Ok, alors j'ai essayé d'utiliser votre idée originale, et j'ai modifié seulement quelques bits ici et là.

    Au lieu d'utiliser des pourcentages, j'ai trouvé plus facile d'utiliser des valeurs de pixels. Alors:

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

    Ensuite, tout ce que nous devons faire est de vérifier la proportion de la fenêtre pour voir comment nous devons modifier les propriétés de la division

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

    Exemple de travail: http://codepen.io/jaimerodas/pen/RaGQVm

    Extrait de pile

     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 = $('<div>').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: ""; } 
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="container"></div> 

    S'appuyant sur css se transforme et l'applique à un seul élément vous offre de meilleures performances indépendamment du nombre de points chauds (moins de manipulations DOM et beaucoup moins de ré-flux). L'accélération matérielle est également agréable à avoir 🙂

    D'abord, le méta-code:

    1. Créez un .hot-spot--container intérieur de votre image .container

    2. Créez le point .hot-spot et placez-les dans le .hot-spot--container

    3. Transformer .hot-spot--container imitant la background-size: cover comportement de background-size: cover

    4. Répétez # 3 chaque fois qu'il y a une taille réelle

    Calculez votre taux d'image bg:

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

    Chaque fois que la fenêtre est redimensionnée, recalculez le rapport du récipient:

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

    Calculez les facteurs d'échelle pour imiter la background-size: cover comportement 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 } 

    … et appliquez la transformation à l'élément de conteneur hot spot, essentiellement en la redimensionnant et en le positionnant de nouveau en "synchronisation" avec l'arrière-plan:

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

    Fiddled: https://jsfiddle.net/ovfiddle/a3pdLodm/ (vous pouvez jouer avec la fenêtre d'aperçu assez efficacement. Notez que le code peut être ajusté pour prendre des dimensions basées sur les pixels et le positionnement des points chauds, vous devrez simplement considérer Les tailles de conteneur et d'image lors du calcul des valeurs d'échelle)

    Mise à jour: le background-size: contain comporte le même calcul, sauf lorsque le containerRatio est plus petit que le bgRatio. La mise à jour de l'arrière-plan css et le basculement du panneau sont suffisants .

    D'accord, donc pas beaucoup de gens connaissent les mesures CSS vh et vw (c'est-à-dire la taille et la taille de la vue). J'ai créé un script qui s'exécute une fois à pageload (contrairement à d'autres réponses avec ~ 50 lignes de code à chaque redimensionnement ).

    Il calcule le ratio de l'image de fond, applique deux éléments de CSS à overlayContainer , et c'est fait.

    J'ai également ajouté un div avec id square . Tout cela fait est de créer un carré pour que vous puissiez travailler avec un ratio de 1: 1, au lieu du ratio de l'arrière-plan. Cela garantit que – si vous voulez créer un carré – vous pouvez utiliser la même largeur et la même hauteur, au lieu d'essayer manuellement de créer un avec des valeurs différentes. Cela vous convient également lorsque vous modifiez légèrement la taille de l'image de fond, car avec ce carré, vos divisions de superposition ne perdront pas leur rapport d'aspect.

    Pour la background-size: cover , voir ce violon .

    Pour la background-size: contain , voir ce violon .

    Le HTML nécessitait:

     <div id="overlayContainer"> <div id="square"> <!-- Create overlay divs here --> </div> </div> 

    Le CSS nécessaire:

     #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 */ 

    Le JavaScript nécessaire:

     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'}) 

    J'espère que cela t'aides


    Mise à jour par @LGSon

    Je ne m'attendais pas à trouver une seule solution CSS , mais voilà, se cachant dans cette réponse, et j'ai donc décidé de l'ajouter dans la même.

    En ajoutant ces 2 lignes à la règle #overlayContainer (fonctionne pour cover et contain ), le script peut être supprimé.

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

    Bien sûr, la version du script a l'avantage d'obtenir automatiquement les valeurs, mais puisque le ou les points d'accès ont un point d'emplacement spécifique en arrière-plan, la taille de l'image sera probablement connue.

    Échantillon avec 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%; } 
     <div id="overlayContainer"> <div id="square"> <div></div> </div> </div> 

    Voici une solution jQuery, le plugin bgCoverTool repositionne un élément basé sur l'échelle de l'image de fond du parent.

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

    Demo:

     $(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> <html> <head> <meta charset="utf-8"> <title>BG Cover Tool</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script type="text/javascript" charset="utf-8"> //bgCoverTool jQuery plugin (function($) { $.bgCoverTool = function(element, options) { var $element = $(element), imgsize = {}; var defaults = { parent: $element.parent(), top: $element.css('top'), left: $element.css('left'), height: $element.css('height'), width: $element.css('width') }; var plugin = this; plugin.settings = {}; plugin.init = function() { plugin.settings = $.extend({}, defaults, options); var tempurl = plugin.settings.parent.css('background-image').slice(4, -1) .replace('"', '').replace('"', ''); var tempimg = new Image(); var console = console || { error: function() {} }; if (plugin.settings.parent.css('background-size') != "cover") { return false; } if (typeof tempurl !== "string") { return false; } if (plugin.settings.top == "auto" || plugin.settings.left == "auto") { console.error("#" + $element.attr('id') + " needs CSS values for 'top' and 'left'"); return false; } $(tempimg).on('load', function() { imgsize.width = this.width; imgsize.height = this.height; imageSizeDetected(imgsize.width, imgsize.height); }); $(window).on('resize', function() { if ('width' in imgsize && imgsize.width != 0) { imageSizeDetected(imgsize.width, imgsize.height); } }); tempimg.src = tempurl; }; var imageSizeDetected = function(w, h) { var scale_h = plugin.settings.parent.width() / w, scale_v = plugin.settings.parent.height() / h, scale = scale_h > scale_v ? scale_h : scale_v; $element.css({ top: parseInt(plugin.settings.top, 10) * scale, left: parseInt(plugin.settings.left, 10) * scale, height: parseInt(plugin.settings.height, 10) * scale, width: parseInt(plugin.settings.width, 10) * scale }); }; plugin.init(); }; /** * @param {options} object Three optional properties are parent, top and left. */ $.fn.bgCoverTool = function(options) { return this.each(function() { if (undefined == $(this).data('bgCoverTool')) { var plugin = new $.bgCoverTool(this, options); $(this).data('bgCoverTool', plugin); } }); } })(jQuery); </script> </head> <body> <div id="container"> <div class="hot-spot"></div> </div> </body> </html> 

    Une approche beaucoup plus simple / meilleure pour votre problème consiste à utiliser un élément SVG, il est mieux adapté à vos besoins. La bonne chose à propos de SVG est que tout va varier proportionnellement par défaut car il s'agit d'un objet vectoriel qui n'est pas un objet de flux de document.

    Cet exemple démontrera la technique

     <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>SVG</title> <style type="text/css" media="screen"> body { background: #eee; margin: 0; } svg { display: block; border: 1px solid #ccc; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #fff; } .face { stroke: #000; stroke-width: 20px; stroke-linecap: round } </style> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-350 -250 700 500"> <circle r="200" class="face" fill="red"/> <path fill="none" class="face" transform="translate(-396,-230)" d="M487.41,282.411c-15.07,36.137-50.735,61.537-92.333,61.537 c-41.421,0-76.961-25.185-92.142-61.076"/> <circle id="leftEye" cx="-60" cy="-50" r="20" fill="#00F"/> <circle id="rightEye" cx="60" cy="-50" r="20" fill="#00F"/> </svg> <script type="text/javascript"> document.getElementById('leftEye').addEventListener('mouseover', function (e) { alert('Left Eye'); }); document.getElementById('rightEye').addEventListener('mouseover', function (e) { alert('Right Eye'); }); </script> </body> </html> 

    Vous pouvez ajouter des images à SVG pour obtenir ce dont vous avez besoin.

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

    JavaScript rend le site Web intelligent, beauté et facile à utiliser.