1/* jshint unused:vars, undef:true, browser:true, jquery:true */ 2 3;(function(global, $) { 4 'use strict'; 5 6 /** 7 * Parallax class 8 * 9 * var parallax = new Parallax(container_div, { speed: 0.25 }); 10 * parallax.render(); 11 * 12 * Settings takes: 13 * * speed: float between 0 and 1, 0 has no effect, 1 appears stationary. Sweet spot is between 0.2 and 0.7 14 * * window: The nearest scrolling parent element to calculate the parallax against, defaults to the window 15 * 16 * @param HTMLElement element The container 17 * @param object settings The settings 18 * @constructor 19 */ 20 var Parallax = function Parallax(element, settings) { 21 this.$element = $(element); 22 this.$image = null; 23 this.image = null; 24 25 // Apply the default settings 26 this.settings = $.fn.extend({ 27 variation: 0, 28 speed: 0.25, 29 window: window 30 }, settings); 31 32 /** 33 * 34 */ 35 if (this.settings.variation) { 36 // Always override speed if the variation is set. 37 this.settings.speed = this.settings.variation / this.$element.height() / 5; 38 } 39 40 this.$window = $(this.settings.window); 41 }; 42 43 Parallax.prototype = { 44 45 /** 46 * Initialize the parallax, this method must be called for the parallax to begin loading. 47 */ 48 init: function () { 49 50 var my = this, 51 outer = $('<div/>').addClass('parallax-image-container'), 52 image = new Image(); 53 54 image.onload = function () { 55 my.image = image; 56 my.$image = $(image).addClass('parallaxic-image'); 57 58 my.bindListeners(); 59 60 window.setTimeout(function() { 61 // defer because some browsers fire the event before populating the image attributes 62 my.handleResize(); 63 my.render(); 64 65 outer.append(my.$image.hide().fadeIn()); 66 }, 0); 67 }; 68 69 image.src = this.$element.data('background-image'); 70 71 this.$element.addClass('parallaxic-container').prepend(outer); 72 }, 73 74 /** 75 * Bind event listeners 76 */ 77 bindListeners: function () { 78 var obj = this; 79 this.$window.on('resize', function (e) { 80 obj.handleResize(); 81 obj.render(); 82 }).on('scroll', function (e) { 83 obj.handleScroll(); 84 obj.render(); 85 }); 86 }, 87 88 /** 89 * Handle browser resize 90 */ 91 handleResize: function () { 92 var frame = this.getFrame(); 93 frame.determineScale(this); 94 frame.determineOffsetLeft(this); 95 this.handleScroll(); 96 }, 97 98 /** 99 * Handle the browser scroll 100 */ 101 handleScroll: function () { 102 var frame; 103 if (this.isVisible()) { 104 frame = this.getFrame(); 105 106 frame.determineOffsetTop(this); 107 frame.determineParallax(this); 108 } 109 }, 110 111 /** 112 * Is this parallax visible 113 * @returns {boolean} 114 */ 115 isVisible: function () { 116 var window_top = this.$window.scrollTop(), 117 window_height = this.$window.height(), 118 container_top = this.$element.offset().top, 119 container_height = this.$element.height(); 120 121 return (window_top + window_height > container_top && window_top < container_top + container_height); 122 }, 123 124 /** 125 * Render the parallax 126 */ 127 render: function () { 128 this.getFrame().render(this); 129 }, 130 131 /** 132 * Get the ParallaxFrame object 133 * @returns ParallaxFrame 134 */ 135 getFrame: function () { 136 if (!this.frame) { 137 this.frame = new ParallaxFrame(); 138 } 139 140 return this.frame; 141 } 142 143 }; 144 145 /** 146 * The class used for managing parallax state change 147 * @constructor 148 */ 149 var ParallaxFrame = function ParallaxFrame() { 150 this.scale = 1; 151 this.offset = {x: 0, y: 0}; 152 }; 153 154 ParallaxFrame.prototype = { 155 156 /** 157 * Determine the images scale to properly satisfy the requirements 158 * @param parallax 159 */ 160 determineScale: function (parallax) { 161 var image_height = parallax.image.height, 162 image_width = parallax.image.width, 163 window_height = parallax.$window.height(), 164 container_height = parallax.$element.height(), 165 container_width = parallax.$element.width(), 166 speed = parallax.settings.speed, 167 required_padding = speed * (window_height + container_height), 168 padded_container_height = container_height + required_padding, 169 ratio; 170 171 // If we aren't wide enough, or we are too tall 172 if (image_width < container_width || image_height > padded_container_height) { 173 // Get the ratio of the container width to the base image width 174 ratio = container_width / image_width; 175 176 // If we scale by the ratio, will we be tall enough? 177 if (image_height * ratio >= padded_container_height) { 178 this.scale = ratio; 179 } else { 180 // Since we weren't tall enough, scale by the height ratio 181 this.scale = padded_container_height / image_height; 182 } 183 } else {
184 // We're too wide and not tall enough, lets just scale by the height ratio 185 this.scale = padded_container_height / image_height; 186 } 187 188 this.scale = Math.ceil(this.scale * 1000) / 1000; 189 }, 190 191 /** 192 * Figure out how far the image needs to be from the left 193 * We need to do this because the image scales away from the center, not the top left 194 * @param parallax 195 */ 196 determineOffsetLeft: function (parallax) { 197 var image_width = parallax.image.width, 198 container_width = parallax.$element.width(), 199 scaled_width = image_width * this.scale, 200 width_difference = (scaled_width - image_width) + (container_width - scaled_width); 201 202 this.offset.x = width_difference / 2; 203 }, 204 205 /** 206 * Figure out how far the image needs to be from the top 207 * We need to do this because the image scales away from the center, not the top left 208 * @param parallax 209 */ 210 determineOffsetTop: function (parallax) { 211 var image_height = parallax.image.height, 212 scaled_height = image_height * this.scale, 213 height_difference = scaled_height - image_height; 214 215 this.offset.y = height_difference / 2; 216 }, 217 218 /** 219 * Modify the offset to simulate parallax effect 220 * @param parallax 221 */ 222 determineParallax: function (parallax) { 223 var speed = parallax.settings.speed, 224 $container = parallax.$element, 225 container_offset = $container.offset(), 226 window_scroll = parallax.$window.scrollTop(), 227 distance_to_top; 228 229 distance_to_top = window_scroll - container_offset.top; 230 this.offset.y += distance_to_top * speed; 231 }, 232 233 /** 234 * Set the actual CSS to change the positioning of this image 235 * @param parallax 236 */ 237 render: function (parallax) { 238 var transformation = ""; 239 240 // Apply translation 241 transformation += "translate3d(" + this.offset.x + "px," + this.offset.y + "px, 0) "; 242 243 // Apply scale 244 transformation += "scale3d(" + this.scale + "," + this.scale + "," + this.scale + ")"; 245 246 parallax.$image.get(0).style.transform = transformation; 247 } 248 }; 249 250 /** 251 * Jquery parallaxize extension, easily create or retrieve a parallax instance with jQuery. 252 * 253 * @param object settings 254 * @return Parallax 255 */ 256 $.fn.parallaxize = function Parallaxize(settings) { 257 var me = $(this), parallax; 258 if (!me.data('parallax')) { 259 parallax = new Parallax(this, settings); 260 $(this).data('parallax', parallax); 261 parallax.init(); 262 } else { 263 parallax = me.data('parallax'); 264 } 265 266 return parallax; 267 }; 268 269})(this, jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.