Position element over background image. But the BG img changes size with the window. CSS
What you are asking for is not a trivial thing at all, it basically involves figuring out how background-size:cover
works and then positioning your element using JavaScript. Due to the nature of background-size:cover
how the image can flow out of the x-axis or y-axis this cannot be done with CSS.
Here is my solution to the problem, on load and resize it calculates the scale of the image and the x or y offset and draws the pointer at the relevant location.
jsFiddle (red dot in Google's red 'o')
HTML
<div id="pointer"></div>
CSS
body {
background:url(https://www.google.com.au/images/srpr/logo4w.png) no-repeat center center fixed;
-webkit-background-size:cover;
-moz-background-size:cover;
-o-background-size:cover;
background-size:cover;
}
#pointer {
margin-left:-10px;
margin-top:-10px;
width:20px;
height:20px;
background-color:#F00;
position:fixed;
}
JS
var image = { width: 550, height: 190 };
var target = { x: 184, y: 88 };
var pointer = $('#pointer');
$(document).ready(updatePointer);
$(window).resize(updatePointer);
function updatePointer() {
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// Get largest dimension increase
var xScale = windowWidth / image.width;
var yScale = windowHeight / image.height;
var scale;
var yOffset = 0;
var xOffset = 0;
if (xScale > yScale) {
// The image fits perfectly in x axis, stretched in y
scale = xScale;
yOffset = (windowHeight - (image.height * scale)) / 2;
} else {
// The image fits perfectly in y axis, stretched in x
scale = yScale;
xOffset = (windowWidth - (image.width * scale)) / 2;
}
pointer.css('top', (target.y) * scale + yOffset);
pointer.css('left', (target.x) * scale + xOffset);
}
Position element over image element but the image changes size with the window
Wrap the image etc in an shrink-wrapped div and base the positioning off that.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.map {
margin: 10px;
border: 5px solid red;
position: relative;
display: inline-block;
}
.map img {
max-width: 100%;
display: block;
}
.box {
width: 5%;
height: 5%;
background-image: url(http://www.clker.com/cliparts/W/0/g/a/W/E/map-pin-red.svg);
background-position: top center;
background-repeat: no-repeat;
background-size: contain;
position: absolute;
}
#pin-1 {
top: 25%;
left: 36%;
}
.box:hover > .pin-text {
display: block;
}
.pin-text {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 75%;
white-space: nowrap;
display: none;
}
.pin-text h3 {
color: white;
text-shadow: 1px 1px 1px #000;
}
<div class="map">
<img src="http://connect.homes.com/wp-content/uploads/2012/05/200392710-0012.jpg" alt="Sample Image" />
<div id="pin-1" class="box">
<div class="pin-text">
<h3>My House</h3>
</div>
</div>
</div>
Keep div overlay relative to background image regardless of window size
EDIT: Added js alternative
I was convinced that this could be done with css and almost gave up, but then I remembered the new(ish) css units vh and vw....
jsfiddle
CSS
html, body{
height: 100%;
width: 100%;
}
.cat {
height: 100%;
width: 100%;
position: relative;
background:url(http://placekitten.com/g/800/400) no-repeat center center / cover;
}
.place-on-eye {
position: absolute;
color: #fff;
margin:0;
}
@media (min-aspect-ratio: 2/1) {
.place-on-eye {
bottom: 50%;
left: 46.875%;
margin-bottom: 1.25vw;
}
}
@media (max-aspect-ratio: 2/1) {
.place-on-eye {
bottom: 52.5%;
left: 50%;
margin-left: -6.25vh;
}
}
Explanation
So the left eye is at approx 375, 190, and since the image is centered, we will also want to know how far off the center it is, so 25, 10. Since the image is covering, the size of the image will change based on whether the aspect ratio of the viewport is greater or less than the aspect ratio of the background image. Knowing this, we can use media queries to position the text.
The image is 2:1, so when the viewport aspect ratio is > 2:1, we know that the width of the image is the width of the viewport, so the left position of the <p>
should always be 46.867% (375/800). The bottom position is going to be more difficult because the image extends beyond the viewport top and bottom. We know that the image is centered, so first move the <p>
to the middle, then push it up by 2.5% (10/400) of the height of the image. We don't know the height of the image, but we do know the image aspect ratio and that the width of the image is equal to the width of the viewport, so 2.5% of the height = 1.25% width. So we have to move the bottom up by 1.25% width, which we can do by setting margin-bottom:1.25vw
. Incidentally, we can do this without vw
in this case because padding is always calculated relative to the width, so we could have set padding-bottom:1.25%
, however this won't work in the next case where you have to position the left relative to the height.
The case when the aspect ratio is < 2:1 is analogous. The height of the image is the height of the viewport, so the bottom position should always be 52.5% (210/400) and the left is calculated similar to above. Move it over to center, then back it up by 3.125% (25/800) the width of the image, which is equal to 6.25% the height of the image, which is equal to the viewport height, so margin-left:-6.25vh
.
Hopefully this is correct and helps you out!
JS Alternative
jsfiddle
Here's an alternative that uses js. It uses some features like forEach and bind that might cause problems depending on how old a browser you need it to work on, but they are easily replaceable. With js you can directly calculate the scaled dimensions of the bg image which makes the positioning easier. Not the most elegant code, but here goes:
//elem: element that has the bg image
//features: array of features to mark on the image
//bgWidth: intrinsic width of background image
//bgHeight: intrinsic height of background image
function FeatureImage(elem, features, bgWidth, bgHeight) {
this.ratio = bgWidth / bgHeight; //aspect ratio of bg image
this.element = elem;
this.features = features;
var feature, p;
for (var i = 0; i < features.length; i++) {
feature = features[i];
feature.left = feature.x / bgWidth; //percent from the left edge of bg image the feature resides
feature.bottom = (bgHeight - feature.y) / bgHeight; //percent from bottom edge of bg image that feature resides
feature.p = this.createMarker(feature.name);
}
window.addEventListener("resize", this.setFeaturePositions.bind(this));
this.setFeaturePositions(); //initialize the <p> positions
}
FeatureImage.prototype.createMarker = function(name) {
var p = document.createElement("p"); //the <p> that acts as the feature marker
p.className = "featureTag";
p.innerHTML = name;
this.element.appendChild(p);
return p
}
FeatureImage.prototype.setFeaturePositions = function () {
var eratio = this.element.clientWidth / this.element.clientHeight; //calc the current container aspect ratio
if (eratio > this.ratio) { // width of scaled bg image is equal to width of container
this.scaledHeight = this.element.clientWidth / this.ratio; // pre calc the scaled height of bg image
this.scaledDY = (this.scaledHeight - this.element.clientHeight) / 2; // pre calc the amount of the image that is outside the bottom of the container
this.features.forEach(this.setWide, this); // set the position of each feature marker
}
else { // height of scaled bg image is equal to height of container
this.scaledWidth = this.element.clientHeight * this.ratio; // pre calc the scaled width of bg image
this.scaledDX = (this.scaledWidth - this.element.clientWidth) / 2; // pre calc the amount of the image that is outside the left of the container
this.features.forEach(this.setTall, this); // set the position of each feature marker
}
}
FeatureImage.prototype.setWide = function (feature) {
feature.p.style.left = feature.left * this.element.clientWidth + "px";
feature.p.style.bottom = this.scaledHeight * feature.bottom - this.scaledDY + "px"; // calc the pixels above the bottom edge of the image - the amount below the container
}
FeatureImage.prototype.setTall = function (feature) {
feature.p.style.bottom = feature.bottom * this.element.clientHeight + "px";
feature.p.style.left = this.scaledWidth * feature.left - this.scaledDX + "px"; // calc the pixels to the right of the left edge of image - the amount left of the container
}
var features = [
{
x: 375,
y: 190,
name: "right eye"
},
{
x: 495,
y: 175,
name: "left eye"
},
{
x: 445,
y: 255,
name: "nose"
},
{
x: 260,
y: 45,
name: "right ear"
},
{
x: 540,
y: 20,
name: "left ear"
}
];
var x = new FeatureImage(document.getElementsByClassName("cat")[0], features, 800, 400);
CSS: Element's relative size makes background-image disappear
You have to change the position:absolute
in .settings-container
to position:relative
as your image in this case act as a Child
for .settings-container
and the image should be according to its parent. So Position:absolute
will not work.
Check the snippet
.main-guy {
position:absolute;
width: 100%;
height: 100%;
margin: auto;
background:#999;
}
.screen-inside {
margin:auto;
position:relative;
height:60%;
width:66.66%;
background-color:blue;
}
.audio-container, .settings-container {
width:100%;
/* Same result with max-width */
height:100%;
background-image:url(http://reservations.life/css/images/bg-01.jpg);
background-repeat: no-repeat;
background-size: cover;
position:absolute;
}
<div ng-controller="MainController" class="main-guy">
<div class="screen-inside">
<div class="audio-container" ng-controller="AudioController">
</div>
</div>
</div>
Preserve position of elements in respect of background image while resize
You won't be able to accomplish this with pure CSS and background-image
with sizing method set to contain
.
You can however do pure CSS and use <img />
tag to load the svg because Images keep proportions when scaled.
First you'll need to to add the img
tag in the .building
Make your markers 0x0px wide and tall and give them negative margin offset by half the width and height.
That way the center of the marker will always be your anchor when your use percentages. (Provided you use top % and left %. In your case you use bottom % so you need to add 15px)
Set display of .building
to inline-block
-- that way it always "wraps around" the image.
You'll now have a responsive image that you can control the width of trough .building{width:XX%}
Demo
.building {
width: 100%;
position: relative;
img{
width:100%;
}
&__item {
position: absolute;
z-index: 50;
width: 0;
height: 0;
margin-left: -15px; //sub half of width
margin-top: 15px; // add half of height
...
That's as far you'll get using pure CSS. For anything more advanced use jQuery and a Responsive Hotspot Plugin
Good luck!
Related Topics
What Is the Default Style of the Blue Focus Outline in Chrome
Tweaking Bootstrap 2.0 for Semantic Markup
Angular 2 Animation VS CSS Animation - When to Use What
Css3 Appearance Property for Ie
How to Make Angled Tab Like This Using CSS
Oocss Separation of Container and Content
Can the Default Named CSS Colors Be Overridden
In Less CSS How to Get Less to Watch a Bunch of Files But Compile a Different File When They Change
Flex Box Container Width Doesn't Grow
Can You Use If/Else Conditions in Css
How to Disable Automatic Links Coloring Without Selecting a Color
Change Color of Select Component's Border and Arrow Icon Material Ui
How to Change Svg Fill Color When Used as Base-64 Background Image Data
Local Website Renders Differently Using (Ip Address or MAChine Name) VS Localhost
In Visual Studio 2010, How to Easily Comment Out Lines in CSS
How to Get a Grid-Gap Between the Grid Item and the Container
CSS to Change the Cursor Style of the Resize Button on a Textarea
How Can Block Container Establish Both Block and Inline Formatting Contexts at the Same Time