window.getBackgroundImageBox = function(elem,i) {
// elem -- the element that instantiates the background image
// i -- the index into the list of background images set in the CSS
// there are 8 specific CSS background properties:
//
// background-image: assume this is set to an image URL
// background-position: will get resolved value, must calculate used value
// background-size: will get resolved value, must calculate used value
// background-repeat: assume this is set to no-repeat
// background-origin: must take this into account when determining pos and size
// background-clip: can ignore this; only clips, doesn't change actual image pos and size
// background-attachment: assume either scroll (default), or fixed with no scrolling, so ignore this
// background-color: not relevant to pos and size
// default i if not given
if (i === undefined) i = 0;
// get resolved origin, size, and pos
let computedStyle = getComputedStyle(elem);
let resolvedOrigin = computedStyle.backgroundOrigin.split(', ')[i];
let resolvedSize = computedStyle.backgroundSize.split(', ')[i];
let resolvedPos = computedStyle.backgroundPosition.split(', ')[i];
// get resolved elem width and height
let resolvedWidth = computedStyle.width.split(', ')[i];
let resolvedHeight = computedStyle.height.split(', ')[i];
let elemWidth = parseInt(resolvedWidth.replace('px',''),10);
let elemHeight = parseInt(resolvedHeight.replace('px',''),10);
// get resolved border sizes
let borderLeft = !computedStyle.borderLeftWidth ? 0 : parseInt(computedStyle.borderLeftWidth.replace('px',''),10);
let borderTop = !computedStyle.borderTopWidth ? 0 : parseInt(computedStyle.borderTopWidth.replace('px',''),10);
let borderRight = !computedStyle.borderRightWidth ? 0 : parseInt(computedStyle.borderRightWidth.replace('px',''),10);
let borderBottom = !computedStyle.borderBottomWidth ? 0 : parseInt(computedStyle.borderBottomWidth.replace('px',''),10);
// get resolved padding sizes
let paddingLeft = !computedStyle.paddingLeft ? 0 : parseInt(computedStyle.paddingLeft.replace('px',''),10);
let paddingTop = !computedStyle.paddingTop ? 0 : parseInt(computedStyle.paddingTop.replace('px',''),10);
let paddingRight = !computedStyle.paddingRight ? 0 : parseInt(computedStyle.paddingRight.replace('px',''),10);
let paddingBottom = !computedStyle.paddingBottom ? 0 : parseInt(computedStyle.paddingBottom.replace('px',''),10);
// derive actual bounding box, which could be different from content box due to origin
let byOriginLeft;
let byOriginTop;
let byOriginRight;
let byOriginBottom;
if (resolvedOrigin === 'content-box') {
byOriginLeft = paddingLeft;
byOriginTop = paddingTop;
byOriginRight = paddingLeft+elemWidth;
byOriginBottom = paddingTop+elemHeight;
} else if (resolvedOrigin === 'padding-box') {
byOriginLeft = 0;
byOriginTop = 0;
byOriginRight = paddingLeft+elemWidth+paddingRight;
byOriginBottom = paddingTop+elemHeight+paddingBottom;
} else if (resolvedOrigin === 'border-box') {
byOriginLeft = -borderLeft;
byOriginTop = -borderTop;
byOriginRight = paddingLeft+elemWidth+paddingRight+borderRight;
byOriginBottom = paddingTop+elemHeight+paddingBottom+borderBottom;
} else {
throw 'unsupported origin: '+resolvedOrigin;
} // end if
let byOriginWidth = byOriginRight-byOriginLeft;
let byOriginHeight = byOriginBottom-byOriginTop;
// create image object to get original image's size
let image = new Image();
image.src = computedStyle.backgroundImage.split(', ')[i].replace(/url\((['"])?(.*?)\1\)/gi,'$2').replace(/\\(.)/,'$1'); // note: don't have to decode URI to assign, but must manually strip backslash escape codes
let imageWidth = image.width;
let imageHeight = image.height;
// get original image y/x ratio, and elem y/x ratio
let imageRatio = imageHeight/imageWidth;
let elemRatio = elemHeight/elemWidth;
// compute initial idea of the result based on origin and size; will apply position afterward
if (resolvedSize === 'cover') {
if (elemRatio > imageRatio) { // more height in elem than image => flush height, surplus width
res = {
'left':byOriginLeft,
'top':byOriginTop,
'right':byOriginLeft+byOriginHeight/imageRatio,
'bottom':byOriginBottom
};
} else { // more width in elem than image => flush width, surplus height
res = {
'left':byOriginLeft,
'top':byOriginTop,
'right':byOriginRight,
'bottom':byOriginTop+byOriginWidth*imageRatio
};
} // end if
} else if (resolvedSize === 'contain') {
if (elemRatio > imageRatio) { // more height in elem than image => flush width, deficient height
res = {
'left':byOriginLeft,
'top':byOriginTop,
'right':byOriginRight,
'bottom':byOriginTop+byOriginWidth*imageRatio
};
} else { // more width in elem than image => flush height, deficient width
res = {
'left':byOriginLeft,
'top':byOriginTop,
'right':byOriginLeft+byOriginHeight/imageRatio,
'bottom':byOriginBottom
};
} // end if
} else {
// parse size into width and height values
let resolvedSizeSplit = resolvedSize.split(' ');
let resolvedSizeWidth = resolvedSizeSplit[0];
let resolvedSizeHeight = resolvedSizeSplit.length >= 2 ? resolvedSizeSplit[1] : 'auto'; // resolved always has both in FF, but not IE
// resolve to integer width and height values
let sizeWidth;
let sizeHeight;
if (resolvedSizeWidth === 'auto' && resolvedSizeHeight === 'auto') {
// double auto uses original img size
sizeWidth = imageWidth;
sizeHeight = imageHeight;
} else {
// if not double auto, calculate non-auto first, then resolve auto if exists
if (resolvedSizeWidth !== 'auto') {
if (resolvedSizeWidth.indexOf('%') > -1) {
let sizeWidthPct = parseInt(resolvedSizeWidth.replace('%',''));
sizeWidth = sizeWidthPct*byOriginWidth/100;
} else {
sizeWidth = parseInt(resolvedSizeWidth.replace('px',''));
} // end if
} // end if
if (resolvedSizeHeight !== 'auto') {
if (resolvedSizeHeight.indexOf('%') > -1) {
let sizeHeightPct = parseInt(resolvedSizeHeight.replace('%',''));
sizeHeight = sizeHeightPct*byOriginHeight/100;
} else {
sizeHeight = parseInt(resolvedSizeHeight.replace('px',''));
} // end if
} // end if
// resolve dependent auto
if (resolvedSizeWidth === 'auto') sizeWidth = sizeHeight/imageRatio;
if (resolvedSizeHeight === 'auto') sizeHeight = sizeWidth*imageRatio;
} // end if
res = {
'left':byOriginLeft,
'top':byOriginTop,
'right':byOriginLeft+sizeWidth,
'bottom':byOriginTop+sizeHeight
};
} // end if
// get absolute pos value in pixels
let resolvedPosSplit = resolvedPos.split(' ');
let resolvedPosLeft = resolvedPosSplit[0];
let resolvedPosTop = resolvedPosSplit.length >= 2 ? resolvedPosSplit[1] : '50%'; // resolved always has both in FF, but not IE
let posLeft;
let isPosLeftPct;
if (resolvedPosLeft.indexOf('%') > -1) {
isPosLeftPct = true;
let posLeftPct = parseInt(resolvedPosLeft.replace('%',''));
posLeft = posLeftPct*(byOriginWidth-(res.right-res.left))/100;
} else {
isPosLeftPct = false;
posLeft = parseInt(resolvedPosLeft.replace('px',''));
} // end if
let posTop;
let isPosTopPct;
if (resolvedPosTop.indexOf('%') > -1) {
isPosTopPct = true;
let posTopPct = parseInt(resolvedPosTop.replace('%',''));
posTop = posTopPct*(byOriginHeight-(res.bottom-res.top))/100;
} else {
isPosTopPct = false;
posTop = parseInt(resolvedPosTop.replace('px',''));
} // end if
// apply pos
// tricky: must *not* apply pct pos adjustment to flush dimension for cover and contain
if (!(
isPosLeftPct && (
resolvedSize === 'cover' && elemRatio < imageRatio
|| resolvedSize === 'contain' && elemRatio > imageRatio
)
)) {
res.left += posLeft;
res.right += posLeft;
} // end if
if (!(
isPosTopPct && (
resolvedSize === 'cover' && elemRatio > imageRatio
|| resolvedSize === 'contain' && elemRatio < imageRatio
)
)) {
res.top += posTop;
res.bottom += posTop;
} // end if
return res;
};
// global constants
var SMALLERIMAGE_LEFTPCT = [67,90,15];
var SMALLERIMAGE_TOPPCT = [64,10,70];
var MULT = 1.2;
// global variables
var g_pctIndex = 0;
// state change functions
window.updateSmallerImage = function() {
let leftPct = SMALLERIMAGE_LEFTPCT[g_pctIndex];
let topPct = SMALLERIMAGE_TOPPCT[g_pctIndex];
// get the smaller image element and its containing element
let smallerImageElem = document.getElementById('smallerImage');
let containingElem = smallerImageElem.parentElement;
// get the bounding box of the background image in the containing element
let bgbox = getBackgroundImageBox(containingElem);
// get the computed style of the smaller image element
let computedStyle = getComputedStyle(smallerImageElem);
// move the smaller image to the required position
// note: subtracting half the width and height for centering, but could do it differently
// must use computedStyle for the subtraction in case the inline style attribute has not been set yet
smallerImageElem.style.left = (bgbox.left + (bgbox.right-bgbox.left)*leftPct/100 - computedStyle.width.replace('px','')/2)+'px';
smallerImageElem.style.top = (bgbox.top + (bgbox.bottom-bgbox.top)*topPct/100 - computedStyle.height.replace('px','')/2)+'px';
// ensure the smaller image is displayed
smallerImageElem.style.display = 'block';
};
window.multContainingElementDim = function(prop,mult) {
let containingElem = document.getElementById('containingElement');
let computedStyle = getComputedStyle(containingElem);
containingElem.style[prop] = (computedStyle[prop].replace('px','')*mult)+'px';
};
window.advanceSmallerIndexPctIndex = function() {
g_pctIndex = (g_pctIndex+1)%SMALLERIMAGE_LEFTPCT.length;
let indexButton = document.getElementById('indexButton');
indexButton.value = '['+g_pctIndex+']';
updateSmallerImage();
};
window.onload = function() {
updateSmallerImage();
let containingElem = document.getElementById('containingElement');
containingElem.addEventListener('transitionend',updateSmallerImage,false);
};
#containingElement {
position:relative;
background-color:#202020;
background-image:url('http://media.zenfs.com/en_US/News/BGR_News/planet-earth.jpg');
background-repeat:no-repeat;
background-size:contain;
background-position:center center;
width:500px;
height:400px;
transition:
width 0.3s ease-in-out,
height 0.3s ease-in-out;
}
#smallerImage {
position:absolute;
width:80px;
height:32px;
display:none; /* will show after positioning */
transition:
left 0.3s ease-in-out,
top 0.3s ease-in-out;
}
#controls {
position:fixed;
left:0;
top:0;
z-index:1;
}
<div id="controls">
<input type="button" value="▲" onclick="multContainingElementDim('height',1/MULT);"/>
<input type="button" value="◄" onclick="multContainingElementDim('width',1/MULT);"/>
<input type="button" value="►" onclick="multContainingElementDim('width',MULT);"/>
<input type="button" value="▼" onclick="multContainingElementDim('height',MULT);"/>
<input id="indexButton" type="button" value="[0]" onclick="advanceSmallerIndexPctIndex();"/>
</div>
<div id="containingElement">
<img id="smallerImage" src="http://freepngimages.com/wp-content/uploads/2015/12/international-space-station-transparent-background.png"/>
</div>
圖像應該保持與背景圖像上相對位置的相對位置嗎? – Aaron
你需要使用剩餘百分比,頂部和變換屬性。 – Aaron
@Ritesh這樣的事情? https://jsfiddle.net/LeoLion/dd8k66yj/ –