-
Notifications
You must be signed in to change notification settings - Fork 5
/
scalable-pictures.js
147 lines (106 loc) · 3.61 KB
/
scalable-pictures.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//
// Scalables.js
//
/* a responsive images script by Eric Portis (ericportis.com)
released to the public domain or whatever. */
(function() {
// debouncing function from John Hann
// resize events can fire like crazy. this calms them down.
// http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced() {
var obj = this, args = arguments;
function delayed() {
if (!execAsap) {
func.apply(obj, args);
}
timeout = null;
};
if (timeout) {
clearTimeout(timeout);
} else if (execAsap) {
func.apply(obj, args);
}
timeout = setTimeout(delayed, threshold || 100);
};
}
//
// fuzzyFactor!
//
// A number between 0-1 that determines how we want to deal with device-pixel-ratios above 1
// lower = more quality, higher = faster loads
// 0 = images will always render as crisply as the device will allow (effective image resolution of 2x @ 2x, 3x @ 3x, etc.)
// 1 = screw device pixels, I only care about css-px (effective image resolution of 1x @ 2x, 1x @ 3x, etc.)
// 0.5 = eric's pick (effective image resolution of 1.414x @ 2x, 2x @ 4x, 4x @16x...)
// note! that these are *worst-case resolutions* - once an image is stretched such that its pixel-density falls below this threshold, we load a bigger one
var fuzzyFactor = 0.5,
pixelRatio = Math.pow( (window.devicePixelRatio || 1), fuzzyFactor );
//
// Scalable object
//
function Scalable(el) {
this.el = el;
// store version dimensions + sources
this.versions = [];
this.versions.push({
src: el.getAttribute('src'),
width: parseInt(el.getAttribute('data-width')),
height: parseInt(el.getAttribute('data-height'))
});
// parse the linked versions
var links = el.querySelectorAll( 'source' );
for ( var i = 0, len = links.length; i < len; i++ ) {
this.versions.push({
src: links[i].getAttribute('src'),
width: parseInt( links[i].getAttribute('data-width') ),
height: parseInt( links[i].getAttribute('data-height') )
});
}
}
Scalable.prototype.loadImg = function() {
// determine the number of pixels we want to paint across the image's width
minPx = this.el.clientWidth * pixelRatio;
// get versions with a width greater than or equal to our minPx
var biggerThanMin = [];
for ( var i = 0, len = this.versions.length; i < len; i++ ) {
if ( this.versions[i].width >= minPx ) {
biggerThanMin.push( this.versions[i] );
}
}
// if there AREN'T any bigger than our min use the biggest one we have
if ( biggerThanMin.length == 0) {
this.currentVersion = this.versions[0];
for ( var i = 1, len = this.versions.length; i < len; i++ ) {
if ( this.versions[i].width > this.currentVersion.width ) {
this.currentVersion = this.versions[i];
}
}
// otherwise use the smallest one that's bigger
} else {
for ( var i = 0, len = biggerThanMin.length; i < len; i++ ) {
if ( i == 0 || biggerThanMin[i].width < this.currentVersion.width ) {
this.currentVersion = biggerThanMin[i];
}
}
}
// load the source
this.el.src = this.currentVersion.src;
}
//
// On load
//
var scalableEls = document.querySelectorAll('picture[data-scalable]'),
scalables = [];
for ( var i = 0, len = scalableEls.length; i < len; i++ ) {
scalables.push( new Scalable( scalableEls[i] ) );
}
function loadTheScalables() {
for ( var i = 0, len = scalables.length; i < len; i++ ) {
scalables[i].loadImg();
}
}
window.addEventListener('load', loadTheScalables, false);
window.addEventListener('resize', debounce(loadTheScalables, 200, false), false);
window.addEventListener('orientationchange', loadTheScalables, false);
})();