-
Notifications
You must be signed in to change notification settings - Fork 0
/
hidpi.js
186 lines (158 loc) · 5.92 KB
/
hidpi.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
* HiDPI.js
*
* Substitute low-resolution images on elements with "hidpi-ready" class and a "data-hidpi-source" attribute.
*
* Expects a full URL to the HiDPI version within "data-hidpi-source" attribute in the HTML tag. Works on background
* images as well as IMG tags.
*
* Overlays the low-resolution version above the hiDPI version once loaded, and performs an x-fade.
*
* There are 3 different Preloading schemes:
* * "lazy" - (requires zg.waypoints.js) only load the HiDPI version when we scroll it into view
* * "queued" - (not implemented) Load the 1st image immediately; queue the rest upon completion
* * "immediate" - Load all images as soon as the DOM is ready. Could overwhelm the server with too many requests.
*
* @version 1.2
* @author Tom Auger c/o Zeitguys
*
* @changelog
* 1.2 - First commit to GitHub repo
* 1.1 - we're no longer cloning the element if it's a container with a bg image. We still clone IMG tags though.
*/
;( function( $ ){
var PRELOAD_SCHEME = "lazy", // "lazy", "queued", "immediate"
DOMCHECK_INTERVAL = 200, // amount of time, in ms, between polls of the DOM
CROSSFADE_TIME = 700, // amount of time it takes to xfade the hidpi version after it's been loaded
HIDPI_BREAKPOINT = 1024, // elements beyond this width will get substituted
DEBOUNCE_TIMEOUT = 500, // length of debounce delay (used in resize event handler)
// Setup a timer to periodically check for HiDPI elements until the DOM is fully ready
// This replaces a .ready() type event (and saves us loading a plugin).
readyTimer = setInterval( checkForHiDPIElements, DOMCHECK_INTERVAL ),
refreshTimer;
// Check for any elements we can substitute right away
checkForHiDPIElements();
// Once the DOM has loaded, check one final time.
$( function(){
clearInterval( readyTimer );
checkForHiDPIElements();
} );
// Also check on page refresh
$( window ).on( "resize", function(){
refreshTimer && clearTimeout( refreshTimer);
refreshTimer = setTimeout( checkForHiDPIElements, DEBOUNCE_TIMEOUT );
} );
/**
* Looks through the DOM for any elements with a class of "hidpi-ready", whose
* width exceeds the HIDPI_BREAKPOINT and starts the HiDPI image preload.
*
* Also replaces the "hidpi-ready" class with "hidpi-processing", which would
* eventually resolve to "hidpi-loaded" or "hidpi-error", depending.
*
* @returns {undefined}
*/
function checkForHiDPIElements(){
$( ".hidpi-ready" )
// Only affect items larger than our Breakpoint
.filter( function(){ return $( this ).width() > HIDPI_BREAKPOINT; } )
.removeClass( "hidpi-ready" )
// If image is in the viewport, preload the image right away,
// otherwise, hold off
.each( function( index, item ){
var $item = $( item ),
top = $item.offset().top,
bottom = top + $item.height();
if ( "immediate" == PRELOAD_SCHEME || ZG_Waypoints.inView( top, bottom ) ){
preloadHiDPIImage( $item );
} else {
if ( "queued" == PRELOAD_SCHEME ){
// @TODO: enqueue and start loading he suckers
} else {
ZG_Waypoints.add( top, bottom, preloadHiDPIImage, { oneShot : true }, $item.addClass( "hidpi-pending" ) );
}
}
} );
}
function preloadHiDPIImage( $item ){
var $imageLoader = $( new Image );
$item.removeClass( "hidpi-queued hidpi-pending" ).addClass( "hidpi-loading" );
$imageLoader.on( "load", $item, hiDPILoaded );
$imageLoader.on( "error", $item, hiDPIError );
$imageLoader.attr( "src", $item.data( "hidpi-src" ) );
}
/**
* Image load success event handler.
*
* Overlays a clone of the current image/element and fades it out,
* revealing the original element with the new HiDPI image instead of the
* original lower-resolution version.
*
* Adds the "hidpi-animating" and "hidpi-loaded" classes.
*
* @param {Object} event
* @returns {undefined}
*/
function hiDPILoaded( event ){
var $item = event.data,
$parent = $item.parent,
$overlay;
// Then swap in the HiDPI image into the original container.
// If it's an IMG tag, it's easy: we just clone it.
if ( "IMG" === $item.prop( "tagName" ) ){
// Parent needs to have position for this to work
if( "static" === $parent.css( "position" ) ){
$parent.css( "position", "relative" );
}
// Overlay a clone
$overlay = $item.removeClass( "hidpi-loading" ).clone()
.addClass( "hidpi-clone hidpi-overlay" )
.css( {
position: "absolute",
top: 0,
left: 0
} )
.insertAfter( $item );
$item.attr( "src", this.src );
}
// Otherwise change the background image of the original container,
// and create a new element that floats just over the background.
else {
// Give item positioning if it doesn't have it.
if ( "static" === $item.css( "position" ) ){
$item.css( "position", "relative" );
}
$overlay = $( '<div class="hidpi-overlay"></div>' ),
$overlay.css( {
position : "absolute",
top : 0,
left : 0,
width : $item.outerWidth() + "px",
height : $item.outerHeight() + "px",
background : $item.css( "background" )
} ).prependTo( $item );
$item.css( "backgroundImage", "url(" + this.src + ")" )
.removeClass( "hidpi-loading" );
}
// Then fade out the clone
$overlay.fadeOut( CROSSFADE_TIME, hiDPIAnimationComplete );
$item.addClass( "hidpi-animating hidpi-loaded" )
// Clean up our source code a little.
.removeAttr( "data-hidpi-src" );
}
function hiDPIAnimationComplete(){
var $this = $( this );
// Remove the "animating" class. The hierarchy is different depending
// on what kind of element we were dealing with.
if ( "IMG" === $this.prop( "tagName" ) ){
$this.prev().removeClass( "hidpi-animating" );
} else {
$this.parent().removeClass( "hidpi-animating" );
}
$this.remove();
}
function hiDPIError( event ){
var $item = event.data;
console.log( "HiDPI load error", this.src );
$item.removeClass( "hidpi-processing" ).addClass( "hidpi-error" );
}
} )( jQuery );