Match image container width with image width after it is rendered - React - javascript

I am using react-image-marker which overlays marker on image inside a div.
<ImageMarker
src={props.asset.url}
markers={markers}
onAddMarker={(marker) => setMarkers([...markers, marker])}
className="object-fit-contain image-marker__image"
/>
The DOM elements are as follows:
<div class=“image-marker”>
<img src=“src” class=“image-marker__image” />
</div>
To make vertically long images fit the screen i have added css to contain the image within div
.image-marker {
width: inherit;
height: inherit;
}
.image-marker__image {
object-fit:contain !important;
width: inherit;
height: inherit;
}
But now the image is only a subpart of the entire marker area. Due to which marker can be added beyond image bounds, which i do not want.
How do you think i can tackle this. After the image has been loaded, how can i change the width of parent div to make sure they have same size and markers remain in the image bounds. Please specify with code if possible

Solved it by adding event listeners in useLayoutEffect(). Calculating image size info after it is rendered and adjusting the parent div dimensions accordingly. Initially and also when resize occurs.
If you think you have a better solution. Do specify.
useLayoutEffect(() => {
const getRenderedSize = (contains, cWidth, cHeight, width, height, pos) => {
var oRatio = width / height,
cRatio = cWidth / cHeight;
return function () {
if (contains ? (oRatio > cRatio) : (oRatio < cRatio)) {
this.width = cWidth;
this.height = cWidth / oRatio;
} else {
this.width = cHeight * oRatio;
this.height = cHeight;
}
this.left = (cWidth - this.width) * (pos / 100);
this.right = this.width + this.left;
return this;
}.call({});
}
const getImgSizeInfo = (img) => {
var pos = window.getComputedStyle(img).getPropertyValue('object-position').split(' ');
return getRenderedSize(true,
img.width,
img.height,
img.naturalWidth,
img.naturalHeight,
parseInt(pos[0]));
}
const imgDivs = reactDom.findDOMNode(imageCompRef.current).getElementsByClassName("image-marker__image")
if (imgDivs) {
if (imgDivs.length) {
const thisImg = imgDivs[0]
thisImg.addEventListener("load", (evt) => {
if (evt) {
if (evt.target) {
if (evt.target.naturalWidth && evt.target.naturalHeight) {
let renderedImgSizeInfo = getImgSizeInfo(evt.target)
if (renderedImgSizeInfo) {
setOverrideWidth(Math.round(renderedImgSizeInfo.width))
}
}
}
}
})
}
}
function updateSize() {
const thisImg = imgDivs[0]
if (thisImg){
let renderedImgSizeInfo = getImgSizeInfo(thisImg)
if (renderedImgSizeInfo) {
setOverrideWidth((prev) => {
return null
})
setTimeout(() => {
setOverrideWidth((prev) => {
return setOverrideWidth(Math.round(renderedImgSizeInfo.width))
})
}, 390);
}
}
}
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, [])

Related

Moving element intersecting static element in CSS/Javascript

Video demo
I'm trying to make a rip-off pacman(ish) game for a project using HTML, CSS and Javascript. Check the attached video for reference. The idea is: whenever one of the food elements will intersect the pacman element (ie. get eaten), the score will increase by one. And whenever a food element gets out of the viewport without going through the pacman element, the player will lose 1 life. I tried implementing this with the Intersection Observer API by observing the food elements as target and making the pacman element the root. But that didn't work.
const obsCB1 = function (entries, observer) {
point++;
console.log(point);
};
const obsOptions1 = {
root: pacman,
threshold: 0.01,
};
const obs1 = new IntersectionObserver(obsCB1, obsOptions1);
obs1.observe(f1);
(The food element initially starts from outside the viewport to the right and moves to the left using translateX in an infinite loop)
The callback was called once at the start even when f1 didn't intersect pacman. And the point didn't change afterwards. How can I implement this functionality?
Assuming both pacmen and the dots are circles, I guess the best is to calculate the distance between to center and check if it less the sum of radiuses
const board = document.querySelector('#board');
const score = board.querySelector('#score-val');
let dots = [];
function updateScore(x){score.innerText = +score.innerText+x}
function Dot(){
this.SPEED = (.008 + Math.random()/1000) * window.innerHeight;
this.RADIUS = .02 * window.innerHeight;
this.el = document.createElement('span');
this.el.style = `display: inline-block;width:${2*this.RADIUS}px;height:${2*this.RADIUS}px;position:absolute;left:${window.innerWidth}px;top:${Math.random()*(window.innerHeight-2*this.RADIUS)}px;border-radius:50%;background-color: red;`;
board.appendChild(this.el);
dots.push(this);
this.remove = () => {
dots = dots.filter(d => d!== this);
this.el.remove();
}
this.move = () => {
this.el.style.left = parseFloat(this.el.style.left)-this.SPEED+'px';
if(pacmen.isCollapse(this)){
this.remove();
updateScore(1);
} else
if(parseFloat(this.el.style.left)<0) { this.remove();
updateScore(-1);
}
}
}
function Pacmen(){
this.SPEED = .05 * window.innerHeight;
this.RADIUS = .07 * window.innerHeight;
this.el = board.querySelector('#pacmen');
this.el.style.left=0;
this.el.style.top=0;
this.el.width=this.RADIUS*2;
this.el.height=this.RADIUS*2;
this.move = x => {
this.el.style.top = Math.min(window.innerHeight-this.RADIUS*2, Math.max(0, parseFloat(this.el.style.top)+x*this.SPEED))+'px';
}
window.addEventListener('keydown', (e)=> {
switch(e.key){
case 'ArrowDown':
e.preventDefault();
this.move(1);
break;
case 'ArrowUp':
e.preventDefault();
this.move(-1);
break;
}
});
this.isCollapse = (dot) => {
return Math.sqrt(Math.pow(parseFloat(this.el.style.left + this.RADIUS)-parseFloat(dot.el.style.left + dot.RADIUS),2)+Math.pow(parseFloat(this.el.style.top + this.RADIUS)-parseFloat(dot.el.style.top + dot.RADIUS),2)) <= this.RADIUS + dot.RADIUS
}
}
const pacmen = new Pacmen();
function frame(){
dots.forEach(dot =>dot.move());
requestAnimationFrame(frame);
}
setInterval(()=>new Dot(), 1000);
frame();
html,body{padding:0;margin:0}
#board{
display:block;
width:100%;
height:100vh;
background-color:black;
position: relative;
overflow:hidden;
}
#pacmen {
position: absolute;
}
#score{
position:absolute;
bottom: 0px;
right: 20px;
color: white;
font-family: sans-serif;
}
<div id="board">
<img id="pacmen" src="data:image/gif;base64,R0lGODlh3ADbAPf6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD/ACYtBp2wEdz6Bd79AN/9AiH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgD6ACwAAAAA3ADbAAAI/wABCBxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bNmzhz6tzJs6fPn0CDCh1KtKjRo0iTKl3KtKnTp1CjSp1KtarVq1izat3KtavXr2DDih1LtqzZs2jTql3Ltq3bt3Djyp1Lt67du3jz6t3Lt6/fv4AD88xHuLDhw4gTK168WPDIfPwiS55MubLly5gx78vnGCTkf/5Cix5NurTp06hL99vc2SNhfqBTy55Ne/Rqzq05vo5du7dv0f9u59b9+bfx3sFZD89YGPbx57KTF15+sTlv6NhHSydM3aL17OCB3//m3p0g48XOw4cXDuA84r3uGadXn318/Pd57yueTx+6ff3T5cdPPwQWaOCBCPYHXnAINtggP7jdlU8/0P1znYK+gWbhcRBGKJdhFGIoYmoX/gahXSCOqCKGJ9aV4oowhtciXS/GaONz/EhYWIg39uhbjiju6OOQtQEJ12L9lEjkkqJ1KBB+ZJ2XJJNUktZhY2Pls8+WXHa5z5RVhsmPl16SFxZkDhK4YZhVpkmgcmfyg5qFPLJJZGwWXrhalHLa6adv7MX556C0BQoWZIQmipqhXyGq6KO2wXlon5BCyqhXjlb66KVTnUcpoXmGKmqooSm5YqCHQeXep2yO6uqra/b/+B+UTGmpGath9pPZrmDeqKtmZiqlJayx2nklgK/dCauTte5jaqJXRpSpscEmNaymTQb40LRsMrvUtdj6Ey1E3IpZLVLgYjvutrhW6a2wzoYrrrYOleuuh/A+S+i69bZL5Yzfxhsuvw3Z+y++1gqsLr0F+8skwPnKSzBDBj+MMLoKazrxQhUvCXHC+g66sUIdE/kxxiH/OXJCJQ958lHpaswwxQ57fDHMGVe6MkIt+/iyUTHrPDPHNZt881CG5Qzpzgf13OPPQk2n9KNMG+T0jVAHlXTKflZd0NU2Zp1TYgMFvfTQLIMd489Hw+Te1Ioei2zRPv4zJmPttc2Slm7W/1kpg327+Sdobnqt0pfyJq6i3efKhLjikPfHuN4tPR755dlNjpPlmHdunOY3ce7PqJ6XXhroNolu+uqnoV6T6qzHDty7NMEue+yu1+737bLnPpPtvJvuu+O7B7/68DEBb3znyMOk/PKXN//S89BDLr1L1Fef+PWVF6999LT/7v331odPPPnCm5+831yjPyj3LGVvZ56B12///fjn/2B7m49PqK5kCqAAB0jAAhrwgAgkU+PWJ69ZIeuBEIxgBEPnv8HtqTwOkV+YkoPBDFbQTxzsIEM0WKUQilAhJKSSCU+IkBRS6YIsbOEH/QTDGBrEhUyqoQ0JgsM7rSaBQAyiEP+HSMC89Q9bdNKfEpfIRP0ZLiU9dB8In4iSKEqxVTmiXPxmeEVF2Y1/FOwi+MCYOi6K8X1ZPOIZFfdFLa7Eimu8UxrDGEd5tVGNdUTiHMuYx3DdkY59/NseX2fGQDLpj3w0JKQQSUhFLnKQunPkoxgZSUkmipLis+QlIZlJTaKRjI3UTrE8WTdOEo9YpBwRsUzpNi0NkD+pzFxwxjRAnSjmSXCL5ecmhyWcHM1sujyOhdRXFGAGk0NuBIoxj2miZP5kmczsjdiilstoFsmZPoGmNWczTa1Vc5uy6aYyvwlO1IjzmeQsp2nOmc10qtNK2OyJNt9ZGnbK0530DI09B4P/z3zucyfzzGe2nBJQgc6LoP2k5z9tmdB3LnRstsuTQUFDRX7qq32SNFVFefIlwVmzbxuV534wakgHGkYrxYnmeBr1HZVuppdXUVsfpZMlumWUU1yRaR5pKhad1pGngoomUCc1p9FZEqdb6dgouyjRSMUzKrbCTCGDtytJsZQxvboiL8kWpcZMiKS8Yxatupqqr65RrKl6y4TWyLj8TPV2bcXLWs8YVwm9tXcPzdJdcZfXnlKIWKgEqyqNWirArqmuKEqSYV9lwcW+ykgSIuI+YCPYEQGQiCLczZ+QukOrpdROnO2seT7LptCK9kmkLaFpT6vZ+a1WtK1t1Ws7G9sNUs52h7VVrVVP27TPOPa3wB2VcJ6a2QE18bgNeilvRyjZ5hJwgcsdrQSni7foPpO41t0bdrPL3e5697vgDa94x0ve8pr3vOhNr3rXy972utcoAQEAIfkEBQoA+gAsagBSAFcATAAACP8A9QkcSLCgwYMIEybkt6+hw4cQIyqcSLGixYL88mncyLGjRwAXQ4oMmRGAx3wAUqpcqXKky5cHS55kSTMlzJswZaKsydMmzp8kUWrs2ROo0YoliRI9ynThTqU8m0rF+BQqzalY9WWsatUn1n5gw4odS7Zs2KRdaxoEeTOi27dwGw5Nm7YtV7p486qFue+u3r957QIe/Fcw4cN1+fpFzJil4caQryqOTHnl48qRL2Nu3I+f58+gQ4vuu5kyWrxzSzfeuli165WsX8vmGXu2bZW1b9vOrVv26d6+WwPH/Hu4at7GN+tLnbx01ufQo0ufTr269evYs2vfzr279+/SNzaWd458fOTy5lcLTU98PXvT7t9Dlin/fPz6iOnjZ6x//+H+/g3GUFwECveeZv4huJ+C+DFYn4PyQXjgZAG2RKGB49l10oYczlSZhh2GyCFmN4lm4okoerYPieAJxI9JerWIFHN0yThRcYnZiBCOXenoFGA+7ohhUUEaxKNVRRo5V4h7JTnQkVA5SRBrIn4k5UBmZSkWRQEBACH5BAUKAPoALNsA2gABAAEAAAgEAPUFBAAh+QQFCgD6ACzbANoAAQABAAAIBAD1BQQAOw=="/>
<h3 id="score">Score: <span id="score-val">0</span><h3>
</div>

Add multiple times image dropping down the screen (video game js)

So I am building a video game where some fireball drop down the screen. However, there is only one image crossing the screen at a time. I would like that the image is actually multiplied an number of times. To get an idea of what I am saying, here is an image:
But what I would like to do is instead of only having one image (fireball) going down the screen, I would like to have a bunch of images dropping down the screen.
Here is the code for the fireball:
//Fireball script
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
let fireball = {x: fFireball(fireballElement.offsetWidth), y: 0}
const fireLoop = function() {
fireball.y += 2; fireballElement.style.top = fireball.y + 'px'
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth)
fireballElement.style.left = fireball.x + 'px'; fireball.y = 0
}
}
fireballElement.style.left = fireball.x + 'px'
let fireInterval = setInterval(fireLoop, 1000 / 100)
And the image:
<img src="Photo/fireball.png" id="fireball">
Thanks!
Use document.createElement()
Demo : https://jsfiddle.net/hexzero/ukh1dpwn/
I wrote down several comments in the code below to help you understand it better. If you have any additional question don't hesitate to ask.
const fireballArray = [];
// You can add any additional attributes you need for your fire balls here like ids and class names.
function generateFireBallWithAttributes(el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key]);
}
return el;
}
function createFireBalls(amount){
for (let i = 0; i <= amount; i++) {
fireballArray.push( // create an image element
generateFireBallWithAttributes(document.createElement("img"), {
src: "Photo/fireball.png",
width: "32",
height: "32",
})
);
}}
createFireBalls(10)
fireballArray.forEach((fireballElement) => {
// Just add the id of the game body here, so that you could append your fire balls
document.getElementById("game-body").appendChild(fireballElement);
const fireball = { x: fFireball(fireballElement.offsetWidth), y: 0 };
const fireLoop = function () {
fireball.y += 2;
fireballElement.style.top = fireball.y + "px";
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth);
fireballElement.style.left = fireball.x + "px";
fireball.y = 0;
}
};
fireballElement.style.left = fireball.x + "px";
// I've randomised the interval
// you may want to implement your own version to get more control
let fireInterval = setInterval(fireLoop, 1000 / ((Math.random() * (125 - 75)) + 75));
});
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset));
}
img {
position: absolute;
}
I got inspired by this post Setting multiple attributes for an element at once with JavaScript, when adding extra attributes. If you like it please show them some love.
You can simply create a new <img> element for each bullet
I created a snippet doing it the way you were doing it. However, it would be more efficient if you had a single loop that updates all of the images at once.
const create = (x, y, vx, vy) => {
const object = {
x,
y,
vx,
vy,
el: document.createElement("img")
}
object.el.src = "http://placehold.it/50x50";
object.el.style.left = `${object.x}px`;
object.el.style.top = `${object.y}px`;
document.body.appendChild(object.el);
const intervalID = setInterval(() => {
object.x += object.vx;
object.y += object.vy;
object.el.style.left = `${object.x}px`;
object.el.style.top = `${object.y}px`;
if (object.y > window.innerHeight) {
document.body.removeChild(object.el);
clearInterval(intervalID)
}
}, 20);
}
setInterval(() => {
const randX = Math.floor(Math.random() * (window.innerWidth - 50));
create(randX, -50, 0, 2);
}, 500);
body {
margin: 0;
overflow: hidden;
}
img {
position: absolute;
}

image coords changing on browser width change

Hi I am clicking on particular point on image and displaying a icon on selected area. When i change resolution that point is moving to other part of image. below is my code.How to fix the coords on resolution or browser width change. Added eventlistner and need to know on window resize any calculation have to be done?
My component is:
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { showCardDetails } from "../../store/Action";
let temp = [];
class cardDetails extends Component {
constructor(props) {
super(props);
}
state = {
left: "",
right: "",
coords: []
};
componentDidMount() {
const { dispatch } = this.props;
console.log(this.props.backOffice + "props");
console.log(this.props.match.params.userId);
let coords = JSON.parse(localStorage.getItem("coords"));
this.setState({ coords: coords });
window.addEventListener("resize", this.handlePress);
}
handlePress = () => {
const { coords } = this.state;
console.log(this.state);
//anycalculation here?
};
handleclick = e => {
var canvas = document.getElementById("imgCoord");
var w = document.documentElement.clientWidth;
var h = document.documentElement.clientHeight;
console.log(e.offsetLeft);
var x =
e.clientX +
document.body.scrollLeft +
document.documentElement.scrollLeft;
var y =
e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
let left = x + "px";
let top = y + "px";
let obj = {
left: left,
top: top,
width: w,
height: h
};
temp.push(obj);
this.setState({ left: left, top: top, coords: temp });
localStorage.setItem("coords", JSON.stringify(temp));
};
render() {
const { backOffice, match } = this.props;
const { left, top, coords } = this.state;
console.log(coords);
return (
<React.Fragment>
<div>
<img
id="imgCoord"
src={require("../../assets/images/imageTagging.PNG")}
onClick={$event => this.handleclick($event)}
/>
{coords &&
coords.map(item => (
<img
style={{
width: "20px",
position: "absolute",
left: item.left,
top: item.top,
backgroundColor: "white"
}}
src={require("../../assets/images/tag.png")}
id="marker"
/>
))}
</div>
</React.Fragment>
);
}
}
/**
* Redux map state
#param {} state
#param {} ownParams
*/
function mapStateToProps(state, ownParams) {
console.log(state);
return {
backOffice: state.selectedCardData
};
}
export default withRouter(connect(mapStateToProps)(cardDetails));
[EDITED]
you need to add a event listener (here is docs https://www.w3schools.com/jsref/event_onresize.asp ) in componentDidMount method, and remove this listener in componentWillUnmount method.
inside listener put a function to set new coordinates.
and also need to calculate a percentage of icon's coordinates relative to width and height of the window
handleclick = e => {
...your previous code
const yPercent = h / top;
const xPercent = w / left;
this.setState({ yPercent, xPercent });
};
handleResize = () => {
var w = document.documentElement.clientWidth;
var h = document.documentElement.clientHeight;
const newTop = (this.state.yPercent * h) + "px";
const newLeft = (this.state.xPercent * w) + "px";
this.setState({ left: newLeft; top: newTop });
}

fit image to screen properly in javascript

I need to resize image from its natural size to fit the screen the same way as Chrome does with opened images in it, I wrote a rescale() function for this purpose (below).
It works mostly fine but for big landscape-oriented images (example where both width and height both exceed screen after resizing with my function http://imageontime.com/upload/big/2013/07/20/51ea60b522bba.jpg ) fullscreened image still exceeds screen by few lines at height (and rarely at width), so i wanted to ask if there is a better rescale javascript function out there or if here are any chromium source adepts who could look into chromiums source code to get the image resizing function from it so I could port it into javascript?
Here's the code:
function setautow() {
img.style.width = 'auto';
img.style.removeProperty("height");
if (document.documentElement.clientHeight == 0) // Firefox again...
{
img.height = window.innerHeight;
}
else {
img.height = document.documentElement.clientHeight;
}
}
function setautoh() {
img.style.height = 'auto';
img.style.removeProperty("width");
if (document.documentElement.clientWidth == 0) // FF
{
img.width = window.innerWidth;
}
else {
img.width = document.documentElement.clientWidth;
}
}
function shrink(a) {
if (a) {
if (img.width != document.documentElement.clientWidth) {
setautoh();
}
}
else {
if (img.height != document.documentElement.clientHeight) {
setautow();
}
}
}
var rescaled = false;
function rescale() {
if (rescaled) {
rescaled = false;
img.height = img.naturalHeight;
img.width = img.naturalWidth;
img.style.removeProperty("height");
img.style.removeProperty("width");
}
else {
rescaled = true;
if (img.naturalWidth > img.naturalHeight) {
setautoh();
setTimeout(function () {
shrink(true);
}, 0);
}
else {
setautow();
setTimeout(function () {
shrink(false);
}, 0);
}
}
}
The following seems to do the job nicely, as long as image width/height is not specified in CSS.
#Javascript
window.onresize = window.onload = function()
{
resize();
}
function resize()
{
var img = document.getElementsByTagName('img')[0];
winDim = getWinDim();
img.style.height = winDim.y + "px";
if (img.offsetWidth > winDim.x)
{
img.style.height = null;
img.style.width = winDim.x + "px";
}
}
function getWinDim()
{
var body = document.documentElement || document.body;
return {
x: window.innerWidth || body.clientWidth,
y: window.innerHeight || body.clientHeight
}
}
#CSS
body{
margin:0;
padding:0;
text-align:center;
background:rgb(51, 51, 51);
}
#HTML
<img src="http://placehold.it/4000x3000" />
Finally made what I was looking for, works great for FireFox + Chrome
edit: well, perfect for images larger than screen, gonna work on it so it could work on smaller images
function fit_to_screen()
{
img.removeAttribute("style");
var winX = window.innerWidth + "px";
var winY = window.innerHeight + "px";
var vbar = false;
if (document.body.scrollHeight > document.body.clientHeight) // vertical scrollbar
{
img.style.height = winY;
vbar = true;
}
if (document.body.scrollWidth > document.body.clientWidth) // horizontal scrollbar
{
if (vbar) // both scrollbars
{
if ((document.body.scrollHeight - document.body.clientHeight) > (document.body.scrollWidth - document.body.clientWidth)) // let's see which one is bigger
{
img.removeAttribute("style");
img.style.height = winY;
}
else
{
img.removeAttribute("style");
img.style.width = winX;
}
}
else
{
img.removeAttribute("style");
img.style.width = winX;
}
}
}
// bonus, switch between original size and fullscreen
var rescaled = false;
function rescale()
{
if (rescaled)
{
rescaled = false;
img.removeAttribute("style");
}
else
{
rescaled = true;
fit_to_screen();
}
}

Get the Size of a CSS Background Image Using JavaScript?

Is it possible to use JavaScript to get the actual size (width and height in pixels) of a CSS referenced background image?
Yes, and I'd do it like this...
window.onload = function () {
var imageSrc = document
.getElementById('hello')
.style.backgroundImage.replace(/url\((['"])?(.*?)\1\)/gi, '$2')
.split(',')[0];
// I just broke it up on newlines for readability
var image = new Image();
image.src = imageSrc;
image.onload = function () {
var width = image.width,
height = image.height;
alert('width =' + width + ', height = ' + height);
};
};
Some notes...
We need to remove the url() part that JavaScript returns to get the proper image source. We need to split on , in case the element has multiple background images.
We make a new Image object and set its src to the new image.
We can then read the width & height.
jQuery would probably a lot less of a headache to get going.
Can't comment under answers, so here is jQuery version including background-size (posted because this question is first one in google search and may be useful to someone else than me):
function getBackgroundSize(selector, callback) {
var img = new Image(),
// here we will place image's width and height
width, height,
// here we get the size of the background and split it to array
backgroundSize = $(selector).css('background-size').split(' ');
// checking if width was set to pixel value
if (/px/.test(backgroundSize[0])) width = parseInt(backgroundSize[0]);
// checking if width was set to percent value
if (/%/.test(backgroundSize[0])) width = $(selector).parent().width() * (parseInt(backgroundSize[0]) / 100);
// checking if height was set to pixel value
if (/px/.test(backgroundSize[1])) height = parseInt(backgroundSize[1]);
// checking if height was set to percent value
if (/%/.test(backgroundSize[1])) height = $(selector).parent().height() * (parseInt(backgroundSize[0]) / 100);
img.onload = function () {
// check if width was set earlier, if not then set it now
if (typeof width == 'undefined') width = this.width;
// do the same with height
if (typeof height == 'undefined') height = this.height;
// call the callback
callback({ width: width, height: height });
}
// extract image source from css using one, simple regex
// src should be set AFTER onload handler
img.src = $(selector).css('background-image').replace(/url\(['"]*(.*?)['"]*\)/g, '$1');
}
or as jQuery plugin:
(function ($) {
// for better performance, define regexes once, before the code
var pxRegex = /px/, percentRegex = /%/, urlRegex = /url\(['"]*(.*?)['"]*\)/g;
$.fn.getBackgroundSize = function (callback) {
var img = new Image(), width, height, backgroundSize = this.css('background-size').split(' ');
if (pxRegex.test(backgroundSize[0])) width = parseInt(backgroundSize[0]);
if (percentRegex.test(backgroundSize[0])) width = this.parent().width() * (parseInt(backgroundSize[0]) / 100);
if (pxRegex.test(backgroundSize[1])) height = parseInt(backgroundSize[1]);
if (percentRegex.test(backgroundSize[1])) height = this.parent().height() * (parseInt(backgroundSize[0]) / 100);
// additional performance boost, if width and height was set just call the callback and return
if ((typeof width != 'undefined') && (typeof height != 'undefined')) {
callback({ width: width, height: height });
return this;
}
img.onload = function () {
if (typeof width == 'undefined') width = this.width;
if (typeof height == 'undefined') height = this.height;
callback({ width: width, height: height });
}
img.src = this.css('background-image').replace(urlRegex, '$1');
return this;
}
})(jQuery);
var actualImage = new Image();
actualImage.src = $('YOUR SELECTOR HERE').css('background-image').replace(/"/g,"").replace(/url\(|\)$/ig, "");
actualImage.onload = function() {
width = this.width;
height = this.height;
}
var dimension, image;
image = new Image();
image.src = {url/data}
image.onload = function() {
dimension = {
width: image.naturalWidth,
height: image.naturalHeight
};
console.log(dimension); // Actual image dimension
};
Here it is in jQuery:
var actualImage = new Image();
actualImage.src = $('YOUR SELECTOR HERE').css('background-image').replace(/"/g,"").replace(/url\(|\)$/ig, "");
actualImage.width // The actual image width
actualImage.height // The actual image height
Thanks for the sweet regex alex.
If you're using React you can create a custom hook:
import { useEffect, useState, useCallback, useRef } from 'react'
const urlRgx = /url\((['"])?(.+?)\1\)/
const getImagePromise = src =>
new Promise(resolve => {
const img = new Image()
img.onload = () =>
resolve({
src,
width: img.naturalWidth,
height: img.naturalHeight
})
img.src = src
})
const useBackgroundImageSize = (asCallbackFlagOrUrls = false) => {
const ref = useRef()
const [images, setImages] = useState(null)
const callback = useCallback(async () => {
if (Array.isArray(asCallbackFlagOrUrls)) {
const imgPromises = asCallbackFlagOrUrls.map(getImagePromise)
const imgs = await Promise.all(imgPromises)
if (ref?.current) {
setImages(imgs)
}
}
if (typeof asCallbackFlagOrUrls === 'string') {
const image = await getImagePromise(asCallbackFlagOrUrls)
if (ref?.current) {
setImages(image)
}
}
if (typeof asCallbackFlagOrUrls === 'boolean') {
if (ref.current) {
const matches = window
.getComputedStyle(ref.current)
.backgroundImage.match(new RegExp(urlRgx, 'g'))
if (Array.isArray(matches)) {
const imgPromises = matches.map(match =>
getImagePromise(match.replace(new RegExp(urlRgx), '$2'))
)
const imgs = await Promise.all(imgPromises)
if (ref?.current) {
setImages(imgs.length > 1 ? imgs : imgs[0])
}
}
}
}
}, [ref, asCallbackFlagOrUrls])
useEffect(() => {
if (asCallbackFlagOrUrls !== true) {
callback()
}
}, [asCallbackFlagOrUrls, callback])
return asCallbackFlagOrUrls === true ? [ref, images, callback] : [ref, images]
}
export { useBackgroundImageSize }
Then use it like:
const App = () => {
const [ref, image] = useBackgroundImageSize()
console.log(image) // { width, height, src }
return <div ref={ref} image={image} />
}
You can also install background-image-size-hook and use it as a dependency. See the README for more usage details.
Here is a fixed version of the code from klh's post.
I pointed out some small mistakes on the comment section of his post and was told please edit it.
And so I did.
However, reviewers Jan Wilamowski and Dave rejected it.
"This edit was intended to address the author of the post and makes no sense as an edit. It should have been written as a comment or an answer."
Apparently they did not see the comments section.
I had no choice but to write the revised code as a new answer.
function getBackgroundSize(selector, callback) {
var img = new Image(),
// here we will place image's width and height
width, height,
// here we get the size of the background and split it to array
backgroundSize = $(selector).css('background-size').split(' ');
// checking if width was set to pixel value
if (/px/.test(backgroundSize[0])) width = parseInt(backgroundSize[0]);
// checking if width was set to percent value
if (/%/.test(backgroundSize[0])) width = $(selector).width() * (parseInt(backgroundSize[0]) / 100);
// checking if height was set to pixel value
if (/px/.test(backgroundSize[1])) height = parseInt(backgroundSize[1]);
// checking if height was set to percent value
if (/%/.test(backgroundSize[1])) height = $(selector).height() * (parseInt(backgroundSize[1]) / 100);
img.onload = function () {
// check if width was set earlier, if not then set it now
if (typeof width == 'undefined') width = this.width;
// do the same with height
if (typeof height == 'undefined') height = this.height;
// call the callback
callback({ width: width, height: height });
}
// extract image source from css using one, simple regex
// src should be set AFTER onload handler
img.src = $(selector).css('background-image').replace(/url\(['"]*(.*?)['"]*\)/g, '$1');
}
JQuery
(function ($) {
// for better performance, define regexes once, before the code
var pxRegex = /px/, percentRegex = /%/, urlRegex = /url\(['"]*(.*?)['"]*\)/g;
$.fn.getBackgroundSize = function (callback) {
var img = new Image(), width, height, backgroundSize = this.css('background-size').split(' ');
if (pxRegex.test(backgroundSize[0])) width = parseInt(backgroundSize[0]);
if (percentRegex.test(backgroundSize[0])) width = this.width() * (parseInt(backgroundSize[0]) / 100);
if (pxRegex.test(backgroundSize[1])) height = parseInt(backgroundSize[1]);
if (percentRegex.test(backgroundSize[1])) height = this.height() * (parseInt(backgroundSize[1]) / 100);
// additional performance boost, if width and height was set just call the callback and return
if ((typeof width != 'undefined') && (typeof height != 'undefined')) {
callback({ width: width, height: height });
return this;
}
img.onload = function () {
if (typeof width == 'undefined') width = this.width;
if (typeof height == 'undefined') height = this.height;
callback({ width: width, height: height });
}
img.src = this.css('background-image').replace(urlRegex, '$1');
return this;
}
})(jQuery);

Categories