Skip to content

Instantly share code, notes, and snippets.

@Nevraeka
Created January 16, 2014 19:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nevraeka/8461176 to your computer and use it in GitHub Desktop.
Save Nevraeka/8461176 to your computer and use it in GitHub Desktop.
Composed version of Responsive Images Polymer implementation of the HTML <picture> spec
<polymer-element name="x-picture" attributes="src srcset title alt currentSrc media">
<template>
<style>
x-picture {
text-align: center;
object-fit: contain;
object-position: center;
}
x-picture:before, x-picture > img {
display: inline-block;
vertical-align: middle;
}
x-picture:before {
content: '';
height: 100%;
margin-right: -0.25em; /* Adjusts for spacing */
}
x-picture > img {
max-width: 100%;
max-height: 100%;
}
</style>
<content select="source"></content>
<img src="{{ src }}" id="the" />
<script>
(function () {
Polymer('x-picture', {
src: '',
srcset: '',
title: '',
alt: '',
created: function () {
var sources, img, findMatchedMedia, getDensity, getSrcFromElement, xPicture;
this.currentSrc = '';
this.media = '';
sources = this.getElementsByTagName('source');
img = this.$.the;
// Retain a reference.
xPicture = this;
getSrcFromElement = function (el) {
var deviceRatio, src, srcs, srcset, imgDPR, min = 1, max = 1;
if (el) {
// srcset wins if it exists
if (srcset = el.getAttribute('srcset')) {
// next prefer srcset, parsed for pixel density
/*
* if window.devicePixelRatio is undefined assume dpr === 1
* When defined, win.dpr is always a number, therefore
* it can be parsed as an int.
* If it is < 1 (it never is) it should be treated as 1.
* If it is a float, it will be floored.
* If it is higher than the highest defined srcset, it will use
* the highest defined srcset.
*/
deviceRatio = parseInt(window.devicePixelRatio, 10) || 1;
srcs = srcset.replace(/^\s*|\s*$/g, '').split(',');
for (var i = srcs.length - 1; i >= 0; i--) {
src = srcs[i].replace(/^\s*|\s*$/g, '').split(' ');
// return if we have an exact match
if (src[1].match(deviceRatio + 'x')) {
return src[0];
}
// Prep for edge cases.
imgDPR = (src[1]) ? parseInt(src[1].replace('x', ''), 10): 1;
if (imgDPR > max) {
max = [src[0], imgDPR];
}
if (imgDPR < min) {
min = [src[0], imgDPR];
}
}
// Edge case when dpr is out of range supplied in srcset.
if (deviceRatio > max[1]) {
return max[0];
} else if (deviceRatio < min[1]) {
return min[0];
}
} else if (el.src) {
return el.src;
// next check srcset
} else {
// If there is no src or srcset on the element, no vaild url can exist
return '';
}
}
};
findMatchedMedia = function () {
var source, i, len, media, mediaLess;
// Check for srcset, src or media on the x-picture element itself first.
if (xPicture.srcset || xPicture.src) {
return xPicture;
}
/*
* So the question is do we want the first matched media vis-a-vis <video>,
* or the last match vis-a-vis CSS. I feel like the use of <picture> is more
* akin in spirit to CSS than <video>. So to encourage a mobile first approach
* have similar behavior to CSS media query behavior, the last match wins.
*/
// Backwards to return as early as possible on last source order match.
for (i = sources.length-1; i >= 0; i-- ) {
source = sources[i];
media = source.getAttribute('media');
if (window.matchMedia && window.matchMedia(media).matches) {
xPicture.media = media;
return source;
} else if (!media) {
// If no media matches, then the last source with no media attr wins,
// We can't break as there may be a media match up stream which always
// wins over no media so don't overwrite the first medialess source.
mediaLess = mediaLess || source;
}
}
return mediaLess;
};
img.src = getSrcFromElement(findMatchedMedia());
this.currentSrc = img.src;
window.addEventListener('resize', function (e) {
img.src = getSrcFromElement(findMatchedMedia());
});
}
});
})();
</script>
</template>
</polymer-element>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment