1/** 2 * main.js 3 * http://www.codrops.com 4 * 5 * Licensed under the MIT license. 6 * http://www.opensource.org/licenses/mit-license.php 7 * 8 * Copyright 2016, Codrops 9 * http://www.codrops.com 10 */ 11;(function(window) { 12 13 'use strict'; 14 15 // Helper vars and functions. 16 function extend( a, b ) { 17 for( var key in b ) { 18 if( b.hasOwnProperty( key ) ) { 19 a[key] = b[key]; 20 } 21 } 22 return a; 23 } 24 // From https://davidwalsh.name/javascript-debounce-function. 25 function debounce(func, wait, immediate) { 26 var timeout; 27 return function() { 28 var context = this, args = arguments; 29 var later = function() { 30 timeout = null; 31 if (!immediate) func.apply(context, args); 32 }; 33 var callNow = immediate && !timeout; 34 clearTimeout(timeout); 35 timeout = setTimeout(later, wait); 36 if (callNow) func.apply(context, args); 37 }; 38 }; 39 // Creates a sorted array of random numbers between minVal and maxVal with a length = size. 40 function createRandIntOrderedArray(minVal, maxVal, size) { 41 var arr = []; 42 for(var i=0; i<size; ++i) { 43 arr.push(anime.random(minVal, maxVal)); 44 } 45 arr.sort(function(a, b){ return a-b }); 46 return arr; 47 } 48 // Checks if an object is empty. 49 function isObjEmpty(obj) { 50 return Object.getOwnPropertyNames(obj).length > 0; 51 } 52 // Concatenate JS objs. 53 // From http://stackoverflow.com/a/2454315. 54 function collect() { 55 var ret = {}; 56 var len = arguments.length; 57 for (var i=0; i<len; i++) { 58 for (var p in arguments[i]) { 59 if (arguments[i].hasOwnProperty(p)) { 60 ret[p] = arguments[i][p]; 61 } 62 } 63 } 64 return ret; 65 } 66 // Check if clip-path is supported. From http://stackoverflow.com/a/30041538. 67 function areClipPathShapesSupported() { 68 var base = 'clipPath', 69 prefixes = [ 'webkit', 'moz', 'ms', 'o' ], 70 properties = [ base ], 71 testElement = document.createElement( 'testelement' ), 72 attribute = 'polygon(50% 0%, 0% 100%, 100% 100%)'; 73 74 // Push the prefixed properties into the array of properties. 75 for ( var i = 0, l = prefixes.length; i < l; i++ ) { 76 var prefixedProperty = prefixes[i] + base.charAt( 0 ).toUpperCase() + base.slice( 1 ); // remember to capitalize! 77 properties.push( prefixedProperty ); 78 } 79 80 // Interate over the properties and see if they pass two tests. 81 for ( var i = 0, l = properties.length; i < l; i++ ) { 82 var property = properties[i]; 83 84 // First, they need to even support clip-path (IE <= 11 does not)... 85 if ( testElement.style[property] === '' ) { 86 87 // Second, we need to see what happens when we try to create a CSS shape... 88 testElement.style[property] = attribute; 89 if ( testElement.style[property] !== '' ) { 90 return true; 91 } 92 } 93 } 94 return false; 95 }; 96 97 /** 98 * Segmenter obj. 99 */ 100 function Segmenter(el, options) { 101 this.el = el; 102 this.options = extend({}, this.options); 103 extend(this.options, options); 104 // Preload main image. 105 var self = this; 106 imagesLoaded(this.el, { background: true }, function() { 107 self._init(); 108 self._initEvents(); 109 self.options.onReady(); 110 }); 111 } 112 113 Segmenter.prototype.options = { 114 // Number of pieces. 115 pieces: 4, 116 // Show pieces already styled. 117 renderOnLoad: false, 118 // Add an element for the shadow. 119 shadows: true, 120 // Animations for the shadow (valid properties: opacity, translateX, translateY). 121 shadowsAnimation: { 122 opacity: 0.5, 123 // translateX: 20, 124 // translateY: 20 125 }, 126 // Parrallax effect for the pieces. 127 parallax: false, 128 // Movements for the parallax effect. 129 parallaxMovement: {min: 10, max: 40}, 130 // Animation for the pieces (valid properties: duration, easing, delay, opacity, translate[XYZ]). 131 animation: { 132 duration: 1500, 133 easing: 'easeOutQuad', 134 delay: 0, // Delay increment per piece. 135 // opacity: 0.5, 136 translateZ: {min: 10, max: 65}, // We can also use an integer for a specific value. 137 // translateX: {min: -100, max: 100}, // We can also use an integer for a specific value. 138 // translateY: {min: -100, max: 100} // We can also use an integer for a specific value. 139 }, 140 animated_effects : { 141 effects:[{'effect':'scale'},{'effect':'float'},{'effect':'rotate'},{'effect':'flash'}], 142 animatespeed: [{'duration':'1s'},{'duration':'2s'},{'duration':'1.1s'}
142,{'duration':'2.2s'}], 143 }, 144 // Callbacks 145 onReady: function() { return false; }, 146 onAnimationComplete: function() { return false; }, 147 onAnimationStart: function() { return false; }, 148 // The positions of the pieces in percentage values. 149 // We can also use random values by setting options.positions to "random". 150 positions: [ 151 {top: 80, left: 10, width: 30, height: 20}, 152 {top: 2, left: 2, width: 40, height: 40}, 153 {top: 30, left: 60, width: 30, height: 60}, 154 {top: 10, left: 20, width: 50, height: 60} 155 ] 156 }; 157 158 /** 159 * Init! 160 */ 161 Segmenter.prototype._init = function() { 162 // The dimensions of the main element. 163 this.dimensions = { 164 width: this.el.offsetWidth, 165 height: this.el.offsetHeight 166 }; 167 168 // The source of the main image. 169 var background = this.el.style.backgroundImage; 170 this.imgsrc = background.replace('url(','').replace(')','').replace(/\"/gi, "");; 171 172 // Create the layout. 173 this._layout(); 174 175 var self = this; 176 this.pieces = [].slice.call(this.el.querySelectorAll('.segmenter__piece-wrap')); 177 this.pieces.forEach(function(piece, pos) { 178 // Bugfix for Chrome. The translateZ needs an initial value otherwise the animation will not work. (this seems to be a anime.js bug - let´s wait for the next version..) 179 piece.style.WebkitTransform = piece.style.transform = 'translateZ(0.0001px)'; 180 181 // If we want to render the different pieces on load then: 182 if( self.options.renderOnLoad ) { 183 self._renderPiece(piece); 184 } 185 }); 186 // Flag to indicate the pieces are already rendered/styled either on load or after the animation. 187 if( this.options.renderOnLoad ) { 188 this.active = true; 189 } 190 }; 191 192 /** 193 * Creates the layout. 194 */ 195 Segmenter.prototype._layout = function() { 196 // clip-path support 197 var clipPath = areClipPathShapesSupported(); 198 199 var segBgEl = document.createElement('div'); 200 segBgEl.className = 'segmenter__background'; 201 segBgEl.style.backgroundImage = 'url(' + this.imgsrc + ')'; 202 203 var segPieces = document.createElement('div'), 204 segPiecesHTML = '', 205 positionsTotal = this.options.positions.length; 206 207 segPieces.className = 'segmenter__pieces'; 208 209 for(var i = 0, len = this.options.pieces; i < len; ++i) { 210 if( this.options.parallax ) { 211 segPiecesHTML += '<div class="segmenter__piece-parallax">'; 212 } 213 214var effect_css=''; 215if( this.options.animated_effects.animatespeed!='undefined' && this.options.animated_effects.effects!='undefined') { 216var duration_speed = this.options.animated_effects.animatespeed[i].duration; 217var effect_anim=this.options.animated_effects.effects[i].effect; 218var rand_no= anime.random(10,100000); 219if(effect_anim=="scale"){ 220 effect_css ="animation: piece-"+effect_anim+"-"+rand_no+" "+duration_speed+" cubic-bezier(0.785, 0.135, 0.15, 0.86) 0s infinite alternate none running"; 221} 222if(effect_anim=="float"){ 223effect_css ="animation: piece-"+effect_anim+"-"+rand_no+" "+duration_speed+" ease 0s infinite alternate none running"; 224} 225if(effect_anim=="rotate"){ 226effect_css ="animation: piece-"+effect_anim+"-"+rand_no+" "+duration_speed+" ease 0s infinite alternate none running"; 227} 228if(effect_anim=="flash"){ 229effect_css ="animation: piece-"+effect_anim+"-"+rand_no+" "+duration_speed+" ease 0s infinite normal none running"; 230} 231 232if(effect_anim=="scale"){ 233(function($) { 234'use strict'; 235 $(document).ready(function() { 236$('head').append("<style type='text/css'>@keyframes piece-"+effect_anim+"-"+rand_no+"{from{transform: translate3d(0px, 0px, 10.000"+i+"px) scale(1)}to{transform: translate3d(0px, 0px, 10.000"+i+"px) scale(1.1)}}@-webkit-keyframes piece-"+effect_anim+"-"+rand_no+"{from{transform: translate3d(0px, 0px, 10.000"+i+"px) scale(1)}to{transform: translate3d(0px, 0px, 10.000"+i+"px) scale(1.1)}}</style>"); 237}); 238})(jQuery); 239} 240if(effect_anim=="float"){ 241(function($) { 242'use strict'; 243 $(document).ready(function() { 244$('head').append("<style type='text/css'>@keyframes piece-"+effect_anim+"-"+rand_no+"{from{transform: translate3d(0px,0px,10.000"+i+"px);}to{transform: translate3d(0px,-15px,10px);}}@-webkit-keyframes piece-"+effect_anim+"-"+rand_no+"{from{transform: translate3d(0px,0px,10px);}to{transform: translate3d(0px,-15px,10.000"+i+"px);}}</style>"); 245}); 246})(jQuery); 247} 248 249if(effect_anim=="rotate"){ 250(function($) { 251'use strict'; 252 $(document).ready(function() { 253$('head').append("<style type='text/css'>@keyframes piece-"+effect_anim+"-"+rand_no+"{from{transform: translate3d(0px, 0px, 10.000"+i+"px) rotateY(0deg)}to{transform: translate3d(0px, 0px, 10.000"+i+"px) rotateY(180deg)}}@-webkit-keyframes piece-"+effect_anim+"-"+rand_no+"{from{transform: translate3d(0px, 0px, 10.000"+i+"px) rotateY(0deg)}to{transform: translate3d(0px, 0px, 10.000"+i+"px) rotateY(180deg)}}</style>"); 254}); 255})(jQuery); 256} 257 258if(effect_anim=="flash"){ 259(function($) { 260'use strict'; 261 $(document).ready(function() { 262$('head').append("<style type='text/css'>@keyframes piece-"+effect_anim+"-"+rand_no+"{from, 50%, to{opacity: 1;}25%, 75%{opacity: 0;}}@-webkit-keyframes piece-"+effect_anim+"-"+rand_no+"{from, 50%, to{opacity: 1;}25%, 75%{opacity: 0;}}</style>"); 263}); 264})(jQuery); 265} 266 267} 268 segPiecesHTML += '<div class="segmenter__piece-wrap" style="'+ effect_css +'">'; 269 270 var top, left, width, height, clipTop, clipLeft, clipRight, clipBottom, 271 pos = i <= positionsTotal - 1 ? i : 0, 272 isRandom = this.options.positions === 'random'; 273 274 top = isRandom ? anime.random(0,100) : this.options.positions[pos].top; 275 left = isRandom ? anime.random(0,100) : this.options.positions[pos].left; 276 width = isRandom ? anime.random(0,100) : this.options.positions[pos].width; 277 height = isRandom ? anime.random(0,100) : this.options.positions[pos].height; 278 279 if( !clipPath ) { 280 clipTop = isRandom ? top/100 * this.dimensions.height : this.options.positions[pos].top/100 * this.dimensions.height; 281 clipLeft = isRandom ? left/100 * this.dimensions.width : this.options.positions[pos].left/100 * this.dimensions.width; 282 clipRight = isRandom ? width/100 * this.dimensions.width + clipLeft : this.options.positions[pos].width/100 * this.dimensions.width + clipLeft; 283 clipBottom = isRandom ? height/100 * this.dimensions.height + clipTop : this.options.positions[pos].height/100 * this.dimensions.height + clipTop; 284 } 285 286 if( this.options.shadows ) { 287 segPiecesHTML += '<div class="segmenter__shadow" style="top: ' + top + '%; left: ' + left + '%; width: ' + width + '%; height: ' + height + '%"></div>'; 288 } 289 290 segPiecesHTML += clipPath ? 291 '<div class="segmenter__piece" style="background-image: url(' + this.imgsrc + '); -webkit-clip-path: polygon(' + left + '% ' + top + '%, ' + (left + width) + '% ' + top + '%, ' + (left + width) + '% ' + (top + height) + '%, ' + left + '% ' + (top + height) + '%); clip-path: polygon(' + left + '% ' + top + '%, ' + (left + width) + '% ' + top + '%, ' + (left + width) + '% ' + (top + height) + '%, ' + left + '% ' + (top + height) + '%)"></div>' : 292 '<div class="segmenter__piece" style="background-image: url(' + this.imgsrc + '); clip: rect(' + clipTop + 'px,' + clipRight + 'px,' + clipBottom + 'px,' + clipLeft + 'px)"></div>'; 293 294 segPiecesHTML += '</div>' 295 if( this.options.parallax ) { 296 segPiecesHTML += '</div>'; 297 } 298 } 299 300 segPieces.innerHTML = segPiecesHTML; 301 302 this.el.innerHTML = ''; 303 this.el.appendChild(segBgEl); 304 this.el.appendChild(segPieces); 305 }; 306 307 /** 308 * Renders/Styles a piece with the options that are passed in the initialization. 309 */ 310 Segmenter.prototype._renderPiece = function(piece) { 311 var idx = this.pieces.indexOf(piece); 312 313 if( self.options.animation.translateZ != undefined ) { 314 if( typeof self.options.animation.translateZ === 'object' ) { 315 var randArr = createRandIntOrderedArray(self.options.animation.translateZ.min, self.options.animation.translateZ.max, self.options.pieces); 316 piece.style.transform = piece.style.WebkitTransform = 'translateZ(' + randArr[idx] + 'px)'; 317 } 318 else { 319 piece.style.transform = piece.style.WebkitTransform = 'translateZ(' + self.options.animation.translateZ + 'px)'; 320 } 321 } 322 if( self.options.animation.translateY != undefined ) { 323 if( typeof self.options.animation.translateY === 'object' ) { 324 var randArr = createRandIntOrderedArray(self.options.animation.translateY.min, self.options.animation.translateY.max, self.options.pieces); 325 piece.style.transform = piece.style.WebkitTransform = 'translateY(' + randArr[idx] + 'px)'; 326 } 327 else { 328 piece.style.transform = piece.style.WebkitTransform = 'translateY(' + self.options.animation.translateY + 'px)'; 329 } 330 } 331 if( self.options.animation.translateX != undefined ) { 332 if( typeof self.options.animation.translateX === 'object' ) { 333 var randArr = createRandIntOrderedArray(self.options.animation.translateX.min, self.options.animation.translateX.max, self.options.pieces); 334 piece.style.transform = piece.style.WebkitTransform = 'translateX(' + randArr[idx] + 'px)'; 335 } 336 else { 337 piece.style.transform = piece.style.WebkitTransform = 'translateX(' + self.options.animation.translateX + 'px)'; 338 } 339 } 340 if( self.options.animation.opacity != undefined ) { 341 piece.style.opacity = self.options.animation.opacity; 342 } 343 344 if( self.options.shadows && isObjEmpty(self.options.shadowsAnimation) ) { 345 var shadowEl = piece.querySelector('.segmenter__shadow'); 346 shadowEl.style.opacity = self.options.shadowsAnimation.opacity != undefined ? self.options.shadowsAnimation.opacity : 0; 347 shadowEl.style.transform = shadowEl.style.WebkitTransform = 'translateX(' + (self.options.shadowsAnimation.translateX != undefined ? self.options.shadowsAnimation.translateX : 0) + 'px) translateY(' + (self.options.shadowsAnimation.translateY != undefined ? self.options.shadowsAnimation.translateY : 0) + 'px)'; 348 } 349 }; 350 351 /** 352 * Animates the pieces with the options passed (with anime.js). 353 */ 354 Segmenter.prototype.animate = function() { 355 if( this.active ) { 356 return false;
357 } 358 this.active = true; 359 360 var self = this, 361 animProps = { 362 targets: this.pieces, 363 duration: this.options.animation.duration, 364 delay: function(el, index) { return (self.options.pieces - index - 1) * self.options.animation.delay; }, 365 easing: this.options.animation.easing, 366 begin: this.options.onAnimationStart, 367 complete: this.options.onAnimationComplete 368 }; 369 370 if( this.options.animation.translateZ != undefined ) { 371 var randArr = createRandIntOrderedArray(this.options.animation.translateZ.min, this.options.animation.translateZ.max, this.options.pieces); 372 animProps.translateZ = typeof this.options.animation.translateZ === 'object' ? function(el, index) { 373 return randArr[index]; 374 } : this.options.animation.translateZ; 375 } 376 if( this.options.animation.translateX != undefined ) { 377 animProps.translateX = typeof this.options.animation.translateX === 'object' ? function(el, index) { 378 return anime.random(self.options.animation.translateX.min, self.options.animation.translateX.max); 379 } : this.options.animation.translateX; 380 } 381 if( this.options.animation.translateY != undefined ) { 382 animProps.translateY = typeof this.options.animation.translateY === 'object' ? function(el, index) { 383 return anime.random(self.options.animation.translateY.min, self.options.animation.translateY.max); 384 } : this.options.animation.translateY; 385 } 386 if( this.options.animation.opacity != undefined ) { 387 animProps.opacity = this.options.animation.opacity; 388 } 389 390 anime(animProps); 391 392 // Also animate the shadow element. 393 if( this.options.shadows && isObjEmpty(this.options.shadowsAnimation) ) { 394 anime(collect({ 395 targets: this.el.querySelectorAll('.segmenter__shadow'), 396 duration: this.options.animation.duration, 397 delay: function(el, index) { return (self.options.pieces - index - 1) * self.options.animation.delay; }, 398 easing: this.options.animation.easing 399 }, this.options.shadowsAnimation)); 400 } 401 }; 402 403 /** 404 * Init/Bind events. 405 */ 406 Segmenter.prototype._initEvents = function() { 407 var self = this; 408 409 // Window resize. 410 this.debounceResize = debounce(function(ev) { 411 var positionsTotal = self.options.positions.length; 412 413 // Recalculate dimensions. 414 self.dimensions = { 415 width: self.el.offsetWidth, 416 height: self.el.offsetHeight 417 }; 418 419 // Recalculate clip values.. 420 // todo: DRY 421 // todo: If random is true then save the initial values. Should not recalculate for this case. 422 self.pieces.forEach(function(piece, position) { 423 var clipTop, clipLeft, clipRight, clipBottom, 424 segmenterPiece = piece.querySelector('.segmenter__piece'); 425 426 if( self.options.positions === 'random' ) { 427 var randT = anime.random(0,100), randL = anime.random(0,100), randW = anime.random(0,100), randH = anime.random(0,100); 428 clipTop = randT/100 * self.dimensions.height; 429 clipLeft = randL/100 * self.dimensions.width; 430 clipRight = randW/100 * self.dimensions.width + clipLeft; 431 clipBottom = randH/100 * self.dimensions.height + clipTop; 432 } 433 else { 434 var pos = position <= positionsTotal - 1 ? position : 0; 435 clipTop = self.options.positions[pos].top/100 * self.dimensions.height; 436 clipLeft = self.options.positions[pos].left/100 * self.dimensions.width; 437 clipRight = self.options.positions[pos].width/100 * self.dimensions.width + clipLeft; 438 clipBottom = self.options.positions[pos].height/100 * self.dimensions.height + clipTop; 439 } 440 441 segmenterPiece.style.clip = 'rect(' + clipTop + 'px,' + clipRight + 'px,' + clipBottom + 'px,' + clipLeft + 'px)'; 442 }); 443 }, 10); 444 window.addEventListener('resize', this.debounceResize); 445 446 // Mousemove and Deviceorientation: 447 if( this.options.parallax ) { 448 var arrRand = createRandIntOrderedArray(this.options.parallaxMovement.min, this.options.parallaxMovement.max, this.options.pieces); 449 this.pieces.forEach(function(piece, pos) { 450 piece.setAttribute('data-parallax-translation', typeof self.options.parallaxMovement === 'object' ? arrRand[pos] : self.options.parallaxMovement ); 451 }); 452 this.mousemove = function(ev) { 453 if( !self.active ) { 454 return false;
455 } 456 requestAnimationFrame(function() { 457 self.pieces.forEach(function(piece) { 458 var t = piece.getAttribute('data-parallax-translation'), 459 transX = t/(self.dimensions.width)*ev.clientX - t/2, 460 transY = t/(self.dimensions.height)*ev.clientY - t/2; 461 462 piece.parentNode.style.transform = piece.parentNode.style.WebkitTransform = 'translate3d(' + transX + 'px,' + transY + 'px,0)'; 463 }); 464 }); 465 }; 466 window.addEventListener('mousemove', this.mousemove); 467 468 this.handleOrientation = function() { 469 if( !self.active ) { 470 return false; 471 } 472 var y = event.gamma; 473 // To make computation easier we shift the range of x and y to [0,180] 474 y += 90; 475 476 requestAnimationFrame(function() { 477 self.pieces.forEach(function(piece) { 478 var t = piece.getAttribute('data-parallax-translation'), 479 transX = t/(self.dimensions.width)*y - t/2, 480 transY = t/(self.dimensions.height)*y - t/2; 481 482 piece.parentNode.style.transform = piece.parentNode.style.WebkitTransform = 'translate3d(' + transX + 'px,' + transY + 'px,0)'; 483 }); 484 }); 485 } 486 window.addEventListener('deviceorientation', this.handleOrientation); 487 } 488 }; 489 490 window.Segmenter = Segmenter; 491 492})(window);
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.