imgix-optimizer 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +19 -0
- data/LICENSE +7 -0
- data/README.md +123 -0
- data/Rakefile +1 -0
- data/dist/imgix-optimizer-0.0.1.min.js +1 -0
- data/dist/imgix-optimizer.js +643 -0
- data/dist/index.html +65 -0
- data/dist/main.css +10 -0
- data/imgix-optimizer.gemspec +17 -0
- data/lib/imgix-optimizer/rails/engine.rb +6 -0
- data/lib/imgix-optimizer/rails.rb +6 -0
- data/lib/imgix-optimizer/version.rb +8 -0
- data/lib/imgix-optimizer.rb +5 -0
- data/package-lock.json +1680 -0
- data/package.json +26 -0
- data/rollup.config.dev.js +6 -0
- data/rollup.config.js +50 -0
- data/src/imgix_bg_image.js +291 -0
- data/src/imgix_image.js +143 -0
- data/src/main.js +9 -0
- data/src/optimizer.js +43 -0
- data/test/test.js +53 -0
- data/vendor/assets/javascripts/.keep +0 -0
- data/vendor/assets/javascripts/imgix-optimizer.js +643 -0
- metadata +84 -0
@@ -0,0 +1,643 @@
|
|
1
|
+
(function () {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
var classCallCheck = function (instance, Constructor) {
|
5
|
+
if (!(instance instanceof Constructor)) {
|
6
|
+
throw new TypeError("Cannot call a class as a function");
|
7
|
+
}
|
8
|
+
};
|
9
|
+
|
10
|
+
var createClass = function () {
|
11
|
+
function defineProperties(target, props) {
|
12
|
+
for (var i = 0; i < props.length; i++) {
|
13
|
+
var descriptor = props[i];
|
14
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
15
|
+
descriptor.configurable = true;
|
16
|
+
if ("value" in descriptor) descriptor.writable = true;
|
17
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
return function (Constructor, protoProps, staticProps) {
|
22
|
+
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
23
|
+
if (staticProps) defineProperties(Constructor, staticProps);
|
24
|
+
return Constructor;
|
25
|
+
};
|
26
|
+
}();
|
27
|
+
|
28
|
+
var ImgixBgImage = function () {
|
29
|
+
function ImgixBgImage(el) {
|
30
|
+
classCallCheck(this, ImgixBgImage);
|
31
|
+
|
32
|
+
// Length of time to complete fade-in transition.
|
33
|
+
this.timeToFade = 500;
|
34
|
+
// The primary element (i.e. the one with the background image).
|
35
|
+
this.el = $(el);
|
36
|
+
// Background image CSS property must be present.
|
37
|
+
if (this.el.css('background-image') == 'none') {
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
// Prepare the element and its container for optimization.
|
41
|
+
this.initEl();
|
42
|
+
// Kick off the optimization process.
|
43
|
+
this.initOptimization();
|
44
|
+
// Listen for window resize events.
|
45
|
+
this.initEventListeners();
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Load an image in memory (not within the DOM) with the same source as the
|
50
|
+
* placeholder image. Once that has completed, we know we're safe to begin
|
51
|
+
* processing.
|
52
|
+
*/
|
53
|
+
|
54
|
+
|
55
|
+
createClass(ImgixBgImage, [{
|
56
|
+
key: 'initOptimization',
|
57
|
+
value: function initOptimization() {
|
58
|
+
var _this = this;
|
59
|
+
|
60
|
+
$('<img>').on('load', function () {
|
61
|
+
return _this.renderTmpPlaceholderEl();
|
62
|
+
}).attr('src', this.placeholderImgUrl);
|
63
|
+
}
|
64
|
+
|
65
|
+
// ---------------------------------------- | Main Element
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Prepare the main element and its container for optimization.
|
69
|
+
*/
|
70
|
+
|
71
|
+
}, {
|
72
|
+
key: 'initEl',
|
73
|
+
value: function initEl() {
|
74
|
+
this.setPlaceholderImgUrl();
|
75
|
+
this.setContainerTmpCss();
|
76
|
+
this.setElTmpCss();
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Set reference to original image URL, which is expected to be a small
|
81
|
+
* placeholder.
|
82
|
+
*/
|
83
|
+
|
84
|
+
}, {
|
85
|
+
key: 'setPlaceholderImgUrl',
|
86
|
+
value: function setPlaceholderImgUrl() {
|
87
|
+
this.placeholderImgUrl = this.el.css('background-image').replace('url(', '').replace(')', '').replace(/\"/gi, "").replace(/\'/gi, "").split(', ')[0];
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* The parent of our jumbotron container should be relatively positioned
|
92
|
+
* (temporarily) so that we can absolutely position the temp image in the
|
93
|
+
* correct location.
|
94
|
+
*/
|
95
|
+
|
96
|
+
}, {
|
97
|
+
key: 'setContainerTmpCss',
|
98
|
+
value: function setContainerTmpCss() {
|
99
|
+
this.el.parent().css('position', 'relative');
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* The main element must have a position set for it to be rendered on top of
|
104
|
+
* the temporary full-size image. We assume that if the element is not
|
105
|
+
* explicitly positioned absolutely, then it can safely be positioned
|
106
|
+
* relatively.
|
107
|
+
*/
|
108
|
+
|
109
|
+
}, {
|
110
|
+
key: 'setElTmpCss',
|
111
|
+
value: function setElTmpCss() {
|
112
|
+
if (this.el.css('position') != 'absolute') {
|
113
|
+
this.el.css('position', 'relative');
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
// ---------------------------------------- | Placeholder Image (Temp)
|
118
|
+
|
119
|
+
/**
|
120
|
+
* Render a clone of the element with the background image directly behind
|
121
|
+
* itself.
|
122
|
+
*/
|
123
|
+
|
124
|
+
}, {
|
125
|
+
key: 'renderTmpPlaceholderEl',
|
126
|
+
value: function renderTmpPlaceholderEl() {
|
127
|
+
this.initTmpPlaceholderEl();
|
128
|
+
this.setTmpPlaceholderElCss();
|
129
|
+
this.addTmpPlaceholderElToDom();
|
130
|
+
this.renderFullSizeImg();
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Create a clone of the element with the background image. Remove content
|
135
|
+
* from the clone -- often elements with a background image contain content.
|
136
|
+
*/
|
137
|
+
|
138
|
+
}, {
|
139
|
+
key: 'initTmpPlaceholderEl',
|
140
|
+
value: function initTmpPlaceholderEl() {
|
141
|
+
this.tmpPlaceholderEl = this.el.clone();
|
142
|
+
this.tmpPlaceholderEl.html('');
|
143
|
+
}
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Position the clone directly behind the main element
|
147
|
+
*/
|
148
|
+
|
149
|
+
}, {
|
150
|
+
key: 'setTmpPlaceholderElCss',
|
151
|
+
value: function setTmpPlaceholderElCss() {
|
152
|
+
this.tmpPlaceholderEl.css({
|
153
|
+
position: 'absolute',
|
154
|
+
top: this.el.position().top,
|
155
|
+
left: this.el.position().left,
|
156
|
+
width: this.el.outerWidth(),
|
157
|
+
height: this.el.outerHeight(),
|
158
|
+
backgroundColor: 'transparent'
|
159
|
+
});
|
160
|
+
}
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Add temporary element to the DOM, directly before the main element
|
164
|
+
* containing the background image.
|
165
|
+
*/
|
166
|
+
|
167
|
+
}, {
|
168
|
+
key: 'addTmpPlaceholderElToDom',
|
169
|
+
value: function addTmpPlaceholderElToDom() {
|
170
|
+
this.tmpPlaceholderEl.insertBefore(this.el);
|
171
|
+
}
|
172
|
+
|
173
|
+
// ---------------------------------------- | Full-Size Image (Temp)
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Create another clone, this time of the temporary placeholder image. This
|
177
|
+
* new element sits behind the other two and is responsible for loading the
|
178
|
+
* full-size image.
|
179
|
+
*/
|
180
|
+
|
181
|
+
}, {
|
182
|
+
key: 'renderFullSizeImg',
|
183
|
+
value: function renderFullSizeImg() {
|
184
|
+
this.removeElBgImg();
|
185
|
+
this.initTmpFullSizeEl();
|
186
|
+
this.setTmpFullSizeElImg();
|
187
|
+
this.addTmpFullSizeElToDom();
|
188
|
+
this.initTransition();
|
189
|
+
}
|
190
|
+
|
191
|
+
/**
|
192
|
+
* Remove the background color and image from the main element. The user won't
|
193
|
+
* notice this transition because the temp duplicate image is already set and
|
194
|
+
* is sitting behind the primary element.
|
195
|
+
*
|
196
|
+
* This also stores a reference to the original background color so we can put
|
197
|
+
* it back when the transition is complete.
|
198
|
+
*/
|
199
|
+
|
200
|
+
}, {
|
201
|
+
key: 'removeElBgImg',
|
202
|
+
value: function removeElBgImg() {
|
203
|
+
this.elBgColor = this.el.css('background-color');
|
204
|
+
this.el.css('background-color', 'transparent');
|
205
|
+
this.el.css('background-image', '');
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* The temporary full-size element is a clone of the temporary placeholder
|
210
|
+
* image element.
|
211
|
+
*/
|
212
|
+
|
213
|
+
}, {
|
214
|
+
key: 'initTmpFullSizeEl',
|
215
|
+
value: function initTmpFullSizeEl() {
|
216
|
+
this.tmpFullSizeEl = this.tmpPlaceholderEl.clone();
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Sets a reference to the full-size image URL based on the current dimensions
|
221
|
+
* of the main element.
|
222
|
+
*/
|
223
|
+
|
224
|
+
}, {
|
225
|
+
key: 'setFullSizeImgUrl',
|
226
|
+
value: function setFullSizeImgUrl() {
|
227
|
+
// Work with the placeholdler image URL, which has been pulled from the
|
228
|
+
// background-image css property of the main elements.
|
229
|
+
var url = this.placeholderImgUrl.split('?');
|
230
|
+
// q is an array of querystring parameters as ["k=v", "k=v", ...].
|
231
|
+
var q = url[url.length - 1].split('&');
|
232
|
+
// Mapping q converts the array to an object of querystring parameters as
|
233
|
+
// { k: v, k: v, ... }.
|
234
|
+
var args = {};
|
235
|
+
q.map(function (x) {
|
236
|
+
return args[x.split('=')[0]] = x.split('=')[1];
|
237
|
+
});
|
238
|
+
// If the image's container is wider than it is tall, we only set width and
|
239
|
+
// unset height, and vice versa.
|
240
|
+
if (this.el.width() >= this.el.height()) {
|
241
|
+
args['w'] = this.el.width();
|
242
|
+
delete args['h'];
|
243
|
+
} else {
|
244
|
+
args['h'] = this.el.height();
|
245
|
+
delete args['w'];
|
246
|
+
}
|
247
|
+
// Redefine q and go the other direction -- take the args object and convert
|
248
|
+
// it back to an array of querystring parameters, as ["k=v", "k=v", ...].
|
249
|
+
q = [];
|
250
|
+
for (var k in args) {
|
251
|
+
q.push(k + '=' + args[k]);
|
252
|
+
}
|
253
|
+
// Store the result and return.
|
254
|
+
return this.fullSizeImgUrl = url[0] + '?' + q.join('&');
|
255
|
+
}
|
256
|
+
|
257
|
+
/**
|
258
|
+
* Change the URL of this temporary element's background image to be the
|
259
|
+
* full-size image.
|
260
|
+
*/
|
261
|
+
|
262
|
+
}, {
|
263
|
+
key: 'setTmpFullSizeElImg',
|
264
|
+
value: function setTmpFullSizeElImg() {
|
265
|
+
this.setFullSizeImgUrl();
|
266
|
+
this.tmpFullSizeEl.css('background-image', 'url("' + this.fullSizeImgUrl + '")');
|
267
|
+
}
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Add the temporary full-size element direct before the temporary placeholder
|
271
|
+
* element.
|
272
|
+
*/
|
273
|
+
|
274
|
+
}, {
|
275
|
+
key: 'addTmpFullSizeElToDom',
|
276
|
+
value: function addTmpFullSizeElToDom() {
|
277
|
+
this.tmpFullSizeEl.insertBefore(this.tmpPlaceholderEl);
|
278
|
+
}
|
279
|
+
|
280
|
+
// ---------------------------------------- | Transition
|
281
|
+
|
282
|
+
/**
|
283
|
+
* Load full-size image in memory. When it has loaded we can confidentally
|
284
|
+
* fade out the placeholder, knowing the full-size image will be in its place.
|
285
|
+
*/
|
286
|
+
|
287
|
+
}, {
|
288
|
+
key: 'initTransition',
|
289
|
+
value: function initTransition() {
|
290
|
+
$('<img>').on('load', $.proxy(this.transitionImg, this)).attr('src', this.fullSizeImgUrl);
|
291
|
+
}
|
292
|
+
|
293
|
+
/**
|
294
|
+
* Fade out the temporary placeholder, set the background-image on the main
|
295
|
+
* element to the full-size URL, then remove the temporary elements behind the
|
296
|
+
* main element
|
297
|
+
*/
|
298
|
+
|
299
|
+
}, {
|
300
|
+
key: 'transitionImg',
|
301
|
+
value: function transitionImg() {
|
302
|
+
var _this2 = this;
|
303
|
+
|
304
|
+
this.fadeOutTmpPlaceholderEl();
|
305
|
+
setTimeout(function () {
|
306
|
+
_this2.updateElImg();
|
307
|
+
_this2.replaceElTmpCss();
|
308
|
+
_this2.removeTmpEls();
|
309
|
+
}, this.timeToFade);
|
310
|
+
}
|
311
|
+
|
312
|
+
/**
|
313
|
+
* Fade out the placeholder element. This was the temporary clone of the main
|
314
|
+
* element that has a placeholder background image.
|
315
|
+
*
|
316
|
+
* Rememeber the main element's background image was unset and its color set
|
317
|
+
* to transparent. That is why fading out this temporary image will work
|
318
|
+
* properly.
|
319
|
+
*/
|
320
|
+
|
321
|
+
}, {
|
322
|
+
key: 'fadeOutTmpPlaceholderEl',
|
323
|
+
value: function fadeOutTmpPlaceholderEl() {
|
324
|
+
this.tmpPlaceholderEl.fadeTo(this.timeToFade, 0);
|
325
|
+
}
|
326
|
+
|
327
|
+
/**
|
328
|
+
* Reset the image URL (this helps if the size of the element has changed),
|
329
|
+
* then set the background image to the new source.
|
330
|
+
*/
|
331
|
+
|
332
|
+
}, {
|
333
|
+
key: 'updateElImg',
|
334
|
+
value: function updateElImg() {
|
335
|
+
this.setFullSizeImgUrl();
|
336
|
+
this.el.css('background-image', 'url(\'' + this.fullSizeImgUrl + '\')');
|
337
|
+
}
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Set the background color back to what it was before the transition.
|
341
|
+
*/
|
342
|
+
|
343
|
+
}, {
|
344
|
+
key: 'replaceElTmpCss',
|
345
|
+
value: function replaceElTmpCss() {
|
346
|
+
this.el.css('background-color', this.elBgColor);
|
347
|
+
}
|
348
|
+
|
349
|
+
/**
|
350
|
+
* Remove both temporary elements from the DOM.
|
351
|
+
*/
|
352
|
+
|
353
|
+
}, {
|
354
|
+
key: 'removeTmpEls',
|
355
|
+
value: function removeTmpEls() {
|
356
|
+
this.tmpPlaceholderEl.remove();
|
357
|
+
this.tmpFullSizeEl.remove();
|
358
|
+
this.tmpPlaceholderEl = undefined;
|
359
|
+
this.tmpFullSizeEl = undefined;
|
360
|
+
}
|
361
|
+
|
362
|
+
// ---------------------------------------- | Event Listeners
|
363
|
+
|
364
|
+
/**
|
365
|
+
* Listener for window resize events and update the image when the event ends.
|
366
|
+
*/
|
367
|
+
|
368
|
+
}, {
|
369
|
+
key: 'initEventListeners',
|
370
|
+
value: function initEventListeners() {
|
371
|
+
var _this3 = this;
|
372
|
+
|
373
|
+
this.initResizeEnd();
|
374
|
+
$(window).on('resizeEnd', function (event) {
|
375
|
+
return _this3.updateElImg();
|
376
|
+
});
|
377
|
+
}
|
378
|
+
|
379
|
+
/**
|
380
|
+
* Trigger "resizeEnd" event on the window object after resizing has ceased
|
381
|
+
* for at least 0.5 seconds.
|
382
|
+
*/
|
383
|
+
|
384
|
+
}, {
|
385
|
+
key: 'initResizeEnd',
|
386
|
+
value: function initResizeEnd() {
|
387
|
+
$(window).resize(function () {
|
388
|
+
if (this.resizeTo) {
|
389
|
+
clearTimeout(this.resizeTo);
|
390
|
+
}
|
391
|
+
this.resizeTo = setTimeout(function () {
|
392
|
+
$(this).trigger('resizeEnd');
|
393
|
+
}, 500);
|
394
|
+
});
|
395
|
+
}
|
396
|
+
}]);
|
397
|
+
return ImgixBgImage;
|
398
|
+
}();
|
399
|
+
|
400
|
+
var ImgixImage = function () {
|
401
|
+
function ImgixImage(img) {
|
402
|
+
classCallCheck(this, ImgixImage);
|
403
|
+
|
404
|
+
// Length of crossfade transition.
|
405
|
+
this.timeToFade = 500;
|
406
|
+
// Main (pixellated placeholder) image.
|
407
|
+
this.placeholderImg = $(img);
|
408
|
+
// Kick off the optimization process.
|
409
|
+
this.initOptimization();
|
410
|
+
}
|
411
|
+
|
412
|
+
/**
|
413
|
+
* Load an image in memory (not within the DOM) with the same source as the
|
414
|
+
* placeholder image. Once that has completed, we know we're safe to begin
|
415
|
+
* processing.
|
416
|
+
*/
|
417
|
+
|
418
|
+
|
419
|
+
createClass(ImgixImage, [{
|
420
|
+
key: 'initOptimization',
|
421
|
+
value: function initOptimization() {
|
422
|
+
$('<img>').on('load', $.proxy(this.renderFullSizeImg, this)).attr('src', this.placeholderImg.attr('src'));
|
423
|
+
}
|
424
|
+
|
425
|
+
// ---------------------------------------- | Full-Size Image
|
426
|
+
|
427
|
+
/**
|
428
|
+
* Render the full-size image behind the placeholder image.
|
429
|
+
*/
|
430
|
+
|
431
|
+
}, {
|
432
|
+
key: 'renderFullSizeImg',
|
433
|
+
value: function renderFullSizeImg() {
|
434
|
+
this.initFullSizeImg();
|
435
|
+
this.setFullSizeImgTempCss();
|
436
|
+
this.setFullSizeImgSrc();
|
437
|
+
this.addFullSizeImgToDom();
|
438
|
+
this.initTransition();
|
439
|
+
}
|
440
|
+
|
441
|
+
/**
|
442
|
+
* The full-size image is a clone of the placeholder image. This enables us to
|
443
|
+
* easily replace it without losing any necessary styles or attributes.
|
444
|
+
*/
|
445
|
+
|
446
|
+
}, {
|
447
|
+
key: 'initFullSizeImg',
|
448
|
+
value: function initFullSizeImg() {
|
449
|
+
this.fullSizeImg = this.placeholderImg.clone();
|
450
|
+
}
|
451
|
+
|
452
|
+
/**
|
453
|
+
* Give the full-size image a temporary set of CSS rules so that it can sit
|
454
|
+
* directly behind the placeholder image while loading.
|
455
|
+
*/
|
456
|
+
|
457
|
+
}, {
|
458
|
+
key: 'setFullSizeImgTempCss',
|
459
|
+
value: function setFullSizeImgTempCss() {
|
460
|
+
this.fullSizeImg.css({
|
461
|
+
position: 'absolute',
|
462
|
+
top: this.placeholderImg.position().top,
|
463
|
+
left: this.placeholderImg.position().left,
|
464
|
+
width: this.placeholderImg.width(),
|
465
|
+
height: this.placeholderImg.height()
|
466
|
+
});
|
467
|
+
}
|
468
|
+
|
469
|
+
/**
|
470
|
+
* Prep the full-size image with the attributes necessary to become its full
|
471
|
+
* size. Right now it is still just a replica of the placeholder, sitting
|
472
|
+
* right behind the placeholder.
|
473
|
+
*
|
474
|
+
* We set the src directly even though we're using imgix.js because older
|
475
|
+
* browsers don't support the srcset attribute which is what imgix.js relies
|
476
|
+
* upon.
|
477
|
+
*/
|
478
|
+
|
479
|
+
}, {
|
480
|
+
key: 'setFullSizeImgSrc',
|
481
|
+
value: function setFullSizeImgSrc() {
|
482
|
+
var newSrc = this.placeholderImg.attr('src').replace(/(\?|\&)(w=)(\d+)/i, '$1$2' + this.placeholderImg.width()).replace(/(\?|\&)(h=)(\d+)/i, '$1$2' + this.placeholderImg.height());
|
483
|
+
this.fullSizeImg.attr('ix-src', newSrc);
|
484
|
+
// TODO: Make this a configurable option or document it as a more semantic temporary class
|
485
|
+
this.fullSizeImg.addClass('img-responsive tmp-img-placeholder');
|
486
|
+
// TODO: This should respect the option from the Optimizer class for the select
|
487
|
+
this.fullSizeImg.removeAttr('data-optimize-img');
|
488
|
+
}
|
489
|
+
|
490
|
+
/**
|
491
|
+
* Render the full-size image in the DOM.
|
492
|
+
*/
|
493
|
+
|
494
|
+
}, {
|
495
|
+
key: 'addFullSizeImgToDom',
|
496
|
+
value: function addFullSizeImgToDom() {
|
497
|
+
this.fullSizeImg.insertBefore(this.placeholderImg);
|
498
|
+
}
|
499
|
+
|
500
|
+
// ---------------------------------------- | Image Transition
|
501
|
+
|
502
|
+
/**
|
503
|
+
* Once the full-size image is loaded, begin the transition. This is the
|
504
|
+
* critical piece of this process. Imgix.js uses the ix-src attribute to build
|
505
|
+
* out the srcset attribute. Then, based on the sizes attribute, the browser
|
506
|
+
* determines which source to render. Therefore we can't preload in memory
|
507
|
+
* because we need imgix to do its thing directly in the DOM.
|
508
|
+
*/
|
509
|
+
|
510
|
+
}, {
|
511
|
+
key: 'initTransition',
|
512
|
+
value: function initTransition() {
|
513
|
+
var _this = this;
|
514
|
+
|
515
|
+
this.fullSizeImg.on('load', function () {
|
516
|
+
return _this.transitionImg();
|
517
|
+
});
|
518
|
+
imgix.init();
|
519
|
+
}
|
520
|
+
|
521
|
+
/**
|
522
|
+
* Fade out the placeholder image, effectively showing the image behind it.
|
523
|
+
*
|
524
|
+
* Once the fade out transition has completed, remove any temporary properties
|
525
|
+
* from the full-size image (so it gets back to being a clone of the
|
526
|
+
* placeholder, with the full-size src).
|
527
|
+
*
|
528
|
+
* Finally, remove the placeholder image from the DOM since we don't need it
|
529
|
+
* any more.
|
530
|
+
*/
|
531
|
+
|
532
|
+
}, {
|
533
|
+
key: 'transitionImg',
|
534
|
+
value: function transitionImg() {
|
535
|
+
var _this2 = this;
|
536
|
+
|
537
|
+
if (!this.placeholderImg) return true;
|
538
|
+
this.fadeOutPlaceholder();
|
539
|
+
setTimeout(function () {
|
540
|
+
_this2.removeFullSizeImgProperties();
|
541
|
+
_this2.removeImg();
|
542
|
+
}, this.timeToFade);
|
543
|
+
}
|
544
|
+
|
545
|
+
/**
|
546
|
+
* Fade out the placeholder image.
|
547
|
+
*/
|
548
|
+
|
549
|
+
}, {
|
550
|
+
key: 'fadeOutPlaceholder',
|
551
|
+
value: function fadeOutPlaceholder() {
|
552
|
+
this.placeholderImg.fadeTo(this.timeToFade, 0);
|
553
|
+
}
|
554
|
+
|
555
|
+
/**
|
556
|
+
* Remove temporary styles and class from the full-size image, which
|
557
|
+
* effectively means it has replaced the placeholder image.
|
558
|
+
*/
|
559
|
+
|
560
|
+
}, {
|
561
|
+
key: 'removeFullSizeImgProperties',
|
562
|
+
value: function removeFullSizeImgProperties() {
|
563
|
+
this.fullSizeImg.removeAttr('style');
|
564
|
+
// TODO: Update this with how the class is handled above.
|
565
|
+
this.fullSizeImg.removeClass('tmp-img-placeholder');
|
566
|
+
}
|
567
|
+
|
568
|
+
/**
|
569
|
+
* Remove the placeholder image from the DOM since we no longer need it.
|
570
|
+
*/
|
571
|
+
|
572
|
+
}, {
|
573
|
+
key: 'removeImg',
|
574
|
+
value: function removeImg() {
|
575
|
+
if (!this.placeholderImg) {
|
576
|
+
return;
|
577
|
+
}
|
578
|
+
this.placeholderImg.remove();
|
579
|
+
this.placeholderImg = undefined;
|
580
|
+
}
|
581
|
+
}]);
|
582
|
+
return ImgixImage;
|
583
|
+
}();
|
584
|
+
|
585
|
+
var Optimizer = function () {
|
586
|
+
function Optimizer() {
|
587
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
588
|
+
classCallCheck(this, Optimizer);
|
589
|
+
|
590
|
+
this.initOptions(options);
|
591
|
+
this.optimizeImages();
|
592
|
+
this.optimizeBgImages();
|
593
|
+
}
|
594
|
+
|
595
|
+
// ---------------------------------------- | Options
|
596
|
+
|
597
|
+
createClass(Optimizer, [{
|
598
|
+
key: 'initOptions',
|
599
|
+
value: function initOptions() {
|
600
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
601
|
+
|
602
|
+
this.options = options;
|
603
|
+
var defaultOptions = {
|
604
|
+
parent: 'body'
|
605
|
+
};
|
606
|
+
for (var key in defaultOptions) {
|
607
|
+
if (defaultOptions.hasOwnProperty(key) && !this.options[key]) {
|
608
|
+
this.options[key] = defaultOptions[key];
|
609
|
+
}
|
610
|
+
}
|
611
|
+
}
|
612
|
+
|
613
|
+
// ---------------------------------------- | Inline Images
|
614
|
+
|
615
|
+
}, {
|
616
|
+
key: 'optimizeImages',
|
617
|
+
value: function optimizeImages() {
|
618
|
+
$(this.options.parent + ' img[data-optimize-img]').each(function (idx, img) {
|
619
|
+
new ImgixImage(img);
|
620
|
+
});
|
621
|
+
}
|
622
|
+
|
623
|
+
// ---------------------------------------- | Background Images
|
624
|
+
|
625
|
+
}, {
|
626
|
+
key: 'optimizeBgImages',
|
627
|
+
value: function optimizeBgImages() {
|
628
|
+
$(this.options.parent + ' [data-optimize-bg-img]').each(function (idx, img) {
|
629
|
+
new ImgixBgImage(img);
|
630
|
+
});
|
631
|
+
return true;
|
632
|
+
}
|
633
|
+
}]);
|
634
|
+
return Optimizer;
|
635
|
+
}();
|
636
|
+
|
637
|
+
window['Imgix'] = window['Imgix'] || {};
|
638
|
+
|
639
|
+
Imgix.ImgixBgImage = ImgixBgImage;
|
640
|
+
Imgix.ImgixImage = ImgixImage;
|
641
|
+
Imgix.Optimizer = Optimizer;
|
642
|
+
|
643
|
+
}());
|
data/dist/index.html
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8" />
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
7
|
+
<title>IMGIX Optimizer</title>
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
9
|
+
|
10
|
+
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css">
|
11
|
+
<link rel="stylesheet" href="main.css">
|
12
|
+
|
13
|
+
<script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
|
14
|
+
<script src="https://rawgit.com/imgix/imgix.js/master/dist/imgix.min.js"></script>
|
15
|
+
<script src="imgix-optimizer-0.0.1.min.js"></script>
|
16
|
+
</head>
|
17
|
+
|
18
|
+
<body>
|
19
|
+
|
20
|
+
<div class="jumbotron" style="background-image: url('https://images.unsplash.com/photo-1525489196064-0752fa4e16f2?fit=crop&auto=compress,format&w=16&h=9');" data-optimize-bg-img>
|
21
|
+
<h1 class="display-4">Hello, world!</h1>
|
22
|
+
<p class="lead">This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
|
23
|
+
<hr class="my-4">
|
24
|
+
<p>It uses utility classes for typography and spacing to space content out within the larger container.</p>
|
25
|
+
<a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="container my-5">
|
29
|
+
<div class="card-columns">
|
30
|
+
<div class="card">
|
31
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/1lYeb5zu9OyYsYKI6EQKI4/7334b00d65c03a9c4d643ef626aa5504/kinga-cichewicz-513031-unsplash.jpg?auto=format,compress&w=16&h=9&fit=crop" data-optimize-img>
|
32
|
+
</div>
|
33
|
+
<div class="card">
|
34
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/1lYeb5zu9OyYsYKI6EQKI4/7334b00d65c03a9c4d643ef626aa5504/kinga-cichewicz-513031-unsplash.jpg?auto=format,compress&w=12&h=9&fit=crop" data-optimize-img>
|
35
|
+
</div>
|
36
|
+
<div class="card">
|
37
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/1lYeb5zu9OyYsYKI6EQKI4/7334b00d65c03a9c4d643ef626aa5504/kinga-cichewicz-513031-unsplash.jpg?auto=format,compress&w=10&h=10&fit=crop" data-optimize-img>
|
38
|
+
</div>
|
39
|
+
<div class="card">
|
40
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/1lYeb5zu9OyYsYKI6EQKI4/7334b00d65c03a9c4d643ef626aa5504/kinga-cichewicz-513031-unsplash.jpg?auto=format,compress&w=10" data-optimize-img>
|
41
|
+
</div>
|
42
|
+
<div class="card">
|
43
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/6d8DPphRCgSiKKkoSUUkQa/68b3fd217241551f47e17a58a57dbd46/eleni-afiontzi-506015-unsplash.jpg?auto=format,compress&w=16&h=9&fit=crop" data-optimize-img>
|
44
|
+
</div>
|
45
|
+
<div class="card">
|
46
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/6d8DPphRCgSiKKkoSUUkQa/68b3fd217241551f47e17a58a57dbd46/eleni-afiontzi-506015-unsplash.jpg?auto=format,compress&w=12&h=9&fit=crop" data-optimize-img>
|
47
|
+
</div>
|
48
|
+
<div class="card">
|
49
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/6d8DPphRCgSiKKkoSUUkQa/68b3fd217241551f47e17a58a57dbd46/eleni-afiontzi-506015-unsplash.jpg?auto=format,compress&w=10&h=10&fit=crop" data-optimize-img>
|
50
|
+
</div>
|
51
|
+
<div class="card">
|
52
|
+
<img class="card-img" alt="Card image cap" src="https://crds-media.imgix.net/6d8DPphRCgSiKKkoSUUkQa/68b3fd217241551f47e17a58a57dbd46/eleni-afiontzi-506015-unsplash.jpg?auto=format,compress&w=10" data-optimize-img>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<script>
|
58
|
+
(function() {
|
59
|
+
new Imgix.Optimizer();
|
60
|
+
})();
|
61
|
+
|
62
|
+
</script>
|
63
|
+
</body>
|
64
|
+
|
65
|
+
</html>
|