Resize positioned background image - javascript

I have span, and it's styles are represented below. My problem is, it was designed to fill 60px*60px span. But now, I have to make it to fill another span with 50px*50px size. But it can't work with background position, because if i change the background-size, all position slips away. So is there any way (css or javascript hack) to resize an image or a block element with bacground-image after the image has been drawn? I want to avoid rewriting all background positions (I've got classes for each icons like ".entertainment").
<span class="icon icon2 entertainment"></span>
span.icon2 {
float: left;
width: 50px;
height: 50px;
margin: 5px 0 0 0;
}
#wrapper span.icon.entertainment {
background-position: -60px -360px;
}
#wrapper span.icon {
background: url(https://teeg.hu/image/icon.png);
}
Thanks for any help!

There is no pure css solution.
There is a js solution. Resize the background (background-size) as you did, then for each element move the position with the difference between sizes / 2 (in your case 5px).
You don't rewrite the classes, just iterate through elements.
Note: This might become an extensive operation, it is better to rewrite classes, even though that is what you want to avoid (40 is not so much... at most 30 min - testing included).

Ok, I've written some hack. I resized the background:
background-size: 100px 1550px;
and did it with jQuery:
$(function() {
$("span.icon2").each(function(index, value) {
var pos = $(this).css("background-position").split(' ');
var newPos = [];
newPos[0] = parseInt(pos[0].replace("px", "")) / 60 * 50;
newPos[1] = parseInt(pos[1].replace("px", "")) / 60 * 50;
$(this).css("background-position", newPos[0] + "px " + newPos[1] + "px");
});
});

Related

JavaScript: Get background-position in pixels

I am working on a JavaScript animation library and ran into a problem: All values are usually returned in pixels using this default function:
window.getComputedStyle(element).getPropertyValue(property);
However, when getting the value for background-position like so:
window.getComputedStyle(element).getPropertyValue('background-position');
The result is 50% 50% (background-position: center). How can I convert the values to pixels? I wrote the following function, but it gives me the wrong result, the reason being is that percentages on background-position are also relative to the image size.
var pixelsX = (parseFloat(percentX) / 100 * element.offsetWidth) + 'px';
var pixelsY = (parseFloat(percentY) / 100 * element.offsetHeight) + 'px';
I also cannot get the size using Image() since the calculation has to happen in real time and I cannot wait for the image to load.
Thanks!
I actually did tried your code and it works...
Is this not the accurate sizes?
I checked it on different screen sizes and it seems accurate.
Please have a look:
var demoDiv = document.getElementById('divDemo');
var demoCalc = window.getComputedStyle(demoDiv).getPropertyValue('background-position');
var pixelsX = (parseFloat(demoCalc) / 100 * demoDiv.offsetWidth) + 'px';
var pixelsY = (parseFloat(demoCalc) / 100 * demoDiv.offsetHeight) + 'px';
console.log(demoCalc);
console.log(pixelsX);
console.log(pixelsY);
body {height: 100vh; width: 100vw; overflow: hidden}
#divDemo {
height: 100%; width: 100%;
background-image: url(https://images.unsplash.com/photo-1554056588-6c35fac03654?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80);
background-position: 50% 50%;
background-repeat: no-repeat;
}
<div id="divDemo"></div>
More SO on this subject here
If you just need the position of the element, maybe try this:
element.getBoundingClientRect();
(source: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect)
You can get the position of each corner as well as dimensions of the element like so:
element.getBoundingClientRect().left;
This returns properties: left, top, right, bottom, x, y, width, and height.
All in pixels!

how to center and make various images sizes fit in a container

I'm using bxslider to have a carousel of images. The thing is though, the images it receives to display are of somewhat unpredictable sizes. The container size is 243x243. And we know that no image will have a side smaller than 243. So...I'd like to center the image in the container. And either zoom in until the shorter of the two dimensions (L vs W) fills the container at 243, and the longer dimension overflow is hidden.
For the images I'm working with, doing this will be perfect for getting the important details of the picture in the frame.
But I'm having trouble...
I've tried the following to center the picture in the frame:
jQuery(".bx-container").each(function() {
var img_w = jQuery(this).children("img").width();
var img_h = jQuery(this).children("img").height();
var pos_top = (img_h - containerHeight) / 2;
var pos_left = (img_w - containerWidth) / 2;
var pos_top = (243 - img_h) / 2;
var pos_left = (243 - img_w) / 2;
jQuery(this).children("img").css({
'top' : pos_top + 'px',
'left' : pos_left + 'px'
});
});
And I've tried this to position not square images into the frame:
jQuery(".bx-container").each(function(){
var refRatio = 1;
var imgH = jQuery(this).children("img").height();
var imgW = jQuery(this).children("img").width();
if ( (imgW/imgH) < refRatio ) {
jQuery(this).addClass("bx-portrait");
} else {
jQuery(this).addClass("bx-landscape");
}
});
});
I've messed with both scripts and the css but I just can't seem to get it work. It either centers but doesn't resize right. Or resizes but centers wrong. Or does both wrong.
Here's the jsfiddle: http://jsfiddle.net/vgJ9X/298/
Could someone help me out?
Thanks!
EDIT:
New jsfiddle...the portrait ones work right. The landscape images still squish. :(
http://jsfiddle.net/vgJ9X/307/
EDIT:
I THINK it has something to do with relatively positioned elements not being allowed to overlap. Trying to find a fix. If anyone knows, edit the last fiddle I posted.
jQuery(".bx-container img").each(function () {
var w = jQuery(this).width();
var h = jQuery(this).height();
if (w > h) $(this).addClass('bx-landscape');
else $(this).addClass('bx-portrait');
});
Check this Updated JSFiddle
Update
jQuery(".bx-container img").each(function () {
var w = jQuery(this).width();
var h = jQuery(this).height();
if (w > h){
$(this).addClass('bx-landscape');
var trans= -243/2;
$(this).css('-webkit-transform','translateZ('+trans+'px)');
}
else if(h > w){
$(this).addClass('bx-portrait');
var trans= -243/2;
$(this).css('-webkit-transform','translateY('+trans+'px)');
}
});
check this JSFiddle
Update of Update
Found the issue with landscape, the plugin is setting max-width:100%; overriding it with max-width:none; fixes the issue...
Update Of Updated Fiddle
Try this:
img{
position:relative;
height:100%;
width:300px;
}
Simple an clean.
Demo: http://jsfiddle.net/vgJ9X/302/
I did a couple things to your jsfiddle.
First I changed the order of your resize and center functions, so the resize comes first. This way, the smaller images get resized, then centered. I also uncommented the first portion of your code.
You also had a couple of errors in your css. There was an extra closing bracket after img style declaration. Your .bx-portrait img and .bx-landscape img declarations were set to 100%px;.
Update:
Change the css in your two .bx classes to:
.bx-portrait img {
height: 100%;
width: auto;
}
.bx-landscape img {
height: auto;
width: 100%;
}
And add a clearfix to your ul:
.bxslider:after {
content: '';
clear: both;
display: table;
padding: 0;
margin: 0;
}
The height is clipping because .bx-viewport has a set height of 243px but also has a 5px border, which makes the actual internal height 233px. You'll need to make the height 253px to account for the 10px of border. This is why they don't look centered vertically.
DEMO
Why don't you just use background images instead and center them. Here is a demo from your original code
http://jsfiddle.net/8y8df/
If you want to show the full size image, just remove the background-size:contain; from the css.

White space on right of page

How do I get rid of that undesired white border on the right of the page?
The website basically dynamically resizes images on a grid, here's a video: https://vine.co/v/h2wtnw6K3H0
CSS:
body {
height: 100%;
width: 100%;
margin: 0;
}
grid {
height: 100%;
width: 100%;
}
.gridImage {
vertical-align: bottom;
margin-left: 0px;
margin-right: 0px;
margin-top: 0px;
margin-bottom: 0px;
}
JS:
function resize() {
console.log($(window).width());
var newBody = "";
for (var i = 0; i <= 100; i++) {
newBody += '<img class="gridImage" src="Images/image2.jpg" width="' + $(window).width() / Math.floor(($(window).width() / 100)) + 'px" height="' + $(window).width() / Math.floor(($(window).width() / 100)) + 'px">';
}
document.getElementById("grid").innerHTML = newBody;
}
If my margins are zero, why is this showing up? Anything I'm missing? Thanks.
Ridcully has covered what the problem is, but here’s a solution.
First you would need to calculate the desired width of each image. This is simply your current equation wrapped in Math.ceil().
var windowWidth = $(window).width() // A slight performance improvement, plus cleaner code
var maxImageWidth = <your value here>
var unroundedImageWidth = windowWidth / Math.floor(windowWidth / maxImageWidth)
var roundedImageWidth = Math.ceil(unroundedImageWidth)
Unless your images fit perfectly, this will make each row slightly wider than the window, causing the final image on each line to wrap to the next. To prevent this, you need to set the gridContainer’s width to that of each row.
$('.gridContainer').width(windowWidth * roundedImageWidth / unroundedImageWidth)
Everything should look good, except for one thing: the horizontal scrollbar. This is easily fixed, however. Add this to your CSS:
.gridContainer {
overflow-x: hidden;
}
This will hide both the scrollbar and the final few pixels on each line. Perfect! Well, not quite.
The problem with this method is that one image per row takes the hit (loses pixels) for all of the others. If you have small images and a lot of images per row, you could end up losing a significant portion of your final column.
To avoid this, you can round your image widths upwards and distribute the overflow amongst all images in the row. This is a little more complicated than the previous method, but it does give a better result.
There are three more numbers you need to calculate.
var imagesPerRow = windowWidth / unroundedImageWidth
var numOfRows = Math.ceil($('.gridContainer img').length / imagesPerRow)
var spillage = windowWidth / roundedImageWidth - windowWidth // Pixels we have to lose
Now it’s just a matter of distributing the spillage.
var i = 0 // Loop counter
while (spillage !== 0) {
// Set the width of all images in column i to the width of that column - 1
$('.gridContainer img:nth-child(' + imagesPerRow + 'n-' + (i+1) + ')')
.width($('.gridContainer img:nth-child(' + (i+1) + ')').width() - 1)
spillage--
i++
}
There should no longer be more than a single pixel difference between the widths of the images.
It's because of rounding errors. What you do is fill the grid with 100 scaled images, depending on the browser to wrap to a new line when the image doesn't fit in the current row any more.
Now imagine a width of 305 pixels. Your formula gives an image width of 100 for that, so you get 3 images in a row and the next one wraps to the next row, leaving 5 pixels blank at the right border.
i think you should also add padding:0; to body its missing from your code.
Try it and even better just make a jsfiddle then it would be easier to check for everyone.

Enyo JS Vertical Sliders

I am trying to code a vertical slider in enyo (Like a control on mixing desk). I was trying to avoid starting from scratch so I started tweaking the onyx.Slider class. I changed to styles from left to top and from width to height and with a few other tweaks, it's working. I'm now stuck on getting the slider to fill from bottom to top as at the minute it is vertical but it fills from the top down. Thanks in advance for any help.
Here are the code changes I have done:
in ProgressBar.js:
updateBarPosition: function(inPercent) {
this.$.bar.applyStyle("height", inPercent + "%");
},
in Slider.js (dividing by 64 is a temporary hack):
valueChanged: function() {
this.value = this.clampValue(this.min, this.max, this.value);
var p = this.calcPercent(this.value);
this.updateKnobPosition(p/64);
if (this.lockBar) {
this.setProgress(this.value);
}
},
updateKnobPosition: function(inPercent) {
this.$.knob.applyStyle("top", inPercent + "%");
},
calcKnobPosition: function(inEvent) {
var y = inEvent.clientY - this.hasNode().getBoundingClientRect().top;
return (y / this.getBounds().height) * (this.max - this.min) + this.min;
},
CSS:
/* ProgressBar.css */
.onyx-progress-bar {
margin: 8px;
height: 400px;
width: 8px;
border: 1px solid rgba(15, 15, 15, 0.2);
border-radius: 3px;
background: #b8b8b8 url(./../images/gradient-invert.png) repeat-x;
background-size: auto 100%;
}
.onyx-progress-bar-bar {
height: 100%;
border-radius: 3px;
background: #58abef url(./../images/gradient.png) repeat-x;
background-size: auto 100%;
}
Tom
There are a couple of approaches you could take. The most obvious (except for the fact it didn't occur to me first) is just to swap the background/gradient of the bar and the bar-bar. This will give you the appearance of filling from the bottom. I would recommend this.
The other method is what I did in this jsFiddle here: http://jsfiddle.net/RoySutton/b9PmA/ (Do ignore the doubled updateBarPosition function)
Instead of modifying those files directly, I derived from Slider and overrode the appropriate functions and added a new class for the vertical slider.
I changed the 'fill' to be absolutely positioned within the slider.
Now, your next problem is that value '0' is fully filled and '100' is fully empty. I handled that by modifying your calcKnobPosition to adjust from max and inverting the positioning logic as seen in this fiddle here: http://jsfiddle.net/RoySutton/b9PmA/2/
return this.max - (y / this.getBounds().height) * (this.max - this.min);

Two triangular clickable area within a square

Basically I want to split a square div diagonally in two resulting in two triangles.
Each triangle has to respond to the hover event.
This is what I have so far but the problem is: if you go from one corner of the div straight to the opposite corner it doesn't re-trigger the hover event since the event is applied to the div element and not the define triangle area within.
I'm open to any suggestions, I don't even mind if I need to approach the problem from a different angle all together. There's got to be an easier solution, at least I hope!
The HTML
<div class="day_box">
</div>
The CSS
html, body { margin: 0; }
.day_box, .upper_left_hover, .lower_right_hover, .full_day {
background: url(/images/corner-sprites.png);
border: 1px solid black;
width: 25px;
height: 25px;
float: left;
margin: 100px;
}
.upper_left_hover { background-position: 75px 0; }
.lower_right_hover { background-position: 50px 0; }
.full_day { background-position: 25px 0; }
The JS
$(".day_box").hover(function(event){
var offset = $(this).offset();
var h = $(this).height() + offset.top;
if((h - event.pageY)>(event.pageX - offset.left)) {
console.log("Upper left");
$(this).toggleClass("upper_left_hover");
} else {
console.log("Lower right");
$(this).toggleClass("lower_right_hover");
}
});
The Fiddle: http://jsfiddle.net/zsay6/
You can use the mousemove event like this (adding mouseout to remove both of the classes when you leave the square):
$(".day_box").mousemove(function(event){
var offset = $(this).offset();
var h = $(this).height() + offset.top;
if((h - event.pageY)>(event.pageX - offset.left)) {
console.log("Upper left");
$(this).removeClass("lower_right_hover");
$(this).addClass("upper_left_hover");
} else if ((h - event.pageY)<(event.pageX - offset.left)) {
console.log("Lower right");
$(this).removeClass("upper_left_hover");
$(this).addClass("lower_right_hover");
}
}).mouseout(function(event)
{
$(this).removeClass("lower_right_hover upper_left_hover");
});
http://jsfiddle.net/zsay6/14/
I altered your fiddle to produce the effect you wanted... and I didn't clean it up at all (was just fiddling... haha)
Using the right-triangle formula (here), I set the given style you set up in your original fiddle. It also throws up some values in a debugging div so you can see it in action a little more clearly.
You can also use HTML map areas for that purpose:
http://www.w3schools.com/tags/tag_map.asp
On hover, change the background of the element to which the usemap is applied.

Categories