使用jQuery按比例缩放背景的元素

我有一个棘手的问题:我在我正在工作的网站上有一个全面的背景。 现在我想附加一个div到图像上的某个位置,并且div的缩放方式与我的背景图像的“background-size:cover”属性一样。 因此,在这个例子中,我有一个城市的照片,覆盖浏览器窗口,我希望我的div覆盖一个特定的build筑物,不pipe窗口大小。

我已经设法使div坚持一个位置,但不能使其正确resize。 我到目前为止做了什么:

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); 

我的想法是:如果(图像宽度/窗口宽度)<1,然后设置为VAR比例值=(windowWidth / imageWidth)否则无量纲(windowHeight / imageHeight),并使用变换比例缩放(比例,缩放),但我couldnt设法做这个工作…

也许你们可以帮我…

解决scheme的背景大小:封面

我试图给你解决scheme(或考虑作为一个想法)。 你可以在这里查看工作演示。 调整窗口大小以查看结果。

首先,我不明白你为什么使用transformtop:50%left:50% for hotots。 所以我试着用最less的用例来解决这个问题,并且为了方便起见而调整了你的标记和CSS。

这里rImage是原始图像的宽高比。

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

在窗口大小调整处理程序中,计算视口r的纵横比。 接下来,诀窍是当我们调整窗口的大小时,find图像的尺寸。 但是,视口会剪切图像以保持宽高比。 所以要计算图像的尺寸,我们需要一些公式。

当使用background-size:cover来计算图像的尺寸时,使用下面的公式。

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

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

在使用background-size:cover时,可以参考此URL以更好地理解图像尺寸计算。

在获得图像的尺寸之后,我们需要绘制从实际图像到新图像尺寸的热点坐标。

为了适应图像的视口图像将剪切在图像的顶部和底部/左侧和右侧。 所以我们应该考虑这个剪辑的图像大小作为一个偏移量,而绘制热点。

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

将此偏移值添加到每个热点的x,y坐标

 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> 

好吧,所以我试图用你最初的想法,并且在这里和那里只修改了几个位。

而不是使用百分比,我发现使用像素值更容易。 所以:

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

那么,我们所要做的就是检查视口的比例,看看我们如何修改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; 

工作示例: http : //codepen.io/jaimerodas/pen/RaGQVm

堆栈片段

 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> 

依靠css转换并将其应用于单个元素,无论热点的数量如何(更less的DOM操作和更less的重新stream动),都可以提供更好的性能。 硬件加速也是一件好事:)

首先,元码:

  1. 在您的图像.container创build一个.hot-spot--container

  2. .hot-spot--container内创build.hot-spot和位置/大小

  3. 转换.hot-spot--container模仿background-size: cover .hot-spot--container background-size: cover行为

  4. 重复#3,每当有一个重新大小

计算你的图像比例:

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

每当窗户重新resize时,重新计算集装箱比率:

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

计算比例因子以模仿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 } 

…并将转换应用于热点容器元素,实质上重新resize并将其与后台“同步”重新定位:

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

小提琴: https ://jsfiddle.net/ovfiddle/a3pdLodm/(你可以很有效地使用预览窗口,注意代码可以调整,以像素为基础的尺寸和热点定位,你只需要考虑计算比例值时的容器和图像大小)

更新:除了当containerRatio 小于 bgRatio时, background-size: contain contains行为使用相同的计算。 更新背景CSS和翻转标志就足够了 。

好吧,所以不是很多人知道CSS测量vhvw (意思是视图高度和视图宽度)。 我创build了一个在pageload上运行一次的脚本(不同于在每个resize时使用~50行代码的其他答案)。

它计算背景图像的比例,将两个CSS应用到overlayContainer ,然后完成。

我也添加了一个ID为square的div。 所有这些都是为你创build一个正方形,它的比例是1:1,而不是背景的比例。 这确保了 – 如果你想创build一个正方形 – 你可以使用相同的宽度和高度,而不是手动尝试创build一个不同的值。 当你稍微改变背景图像的大小时,这也会派上用场,因为用这个正方形,你的叠加div不会失去宽高比。

对于background-size: cover ,请参阅此小提琴 。

对于background-size: contain ,请参阅此小提琴 。

所需的HTML:

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

需要的CSS:

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

所需的JavaScript:

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

希望这可以帮助


由@LGSon更新

我没有想到find一个CSS解决scheme,尽pipe这里是隐藏在这个答案中,所以我决定把它添加到同一个。

通过将这两行添加到#overlayContainer规则(适用于covercontain ),脚本可以被删除。

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

当然,脚本版本具有自动获取值的优点,但是由于热点在背景中具有特定的位置点,因此图像大小将很可能已知。

带有background-size: cover样本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> 

下面是一个jQuery解决scheme,bgCoverTool插件根据父级背景图像的比例重新定位元素。

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

演示:

 $(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> 

更简单/更好的方法是使用SVG元素,它更适合您的需求。 关于SVG的一件很酷的事情是,默认情况下所有东西都会按比例缩放,因为它是一个vector对象而不是文档stream对象。

这个例子将演示这个技术

 <!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> 

您可以将图像添加到SVG以实现您所需的function。

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