diff --git a/assets/gaussholder.js b/assets/gaussholder.js index 3cad36c..bf3cfee 100644 --- a/assets/gaussholder.js +++ b/assets/gaussholder.js @@ -1,33 +1,34 @@ -window.Gaussholder = (function (header) { +/* global StackBlur */ +window.Gaussholder = ( function ( header ) { // Fade duration in ms when the image loads in. var fadeDuration = 800; - var arrayBufferToBase64 = function( buffer ) { + var arrayBufferToBase64 = function ( buffer ) { var binary = ''; var bytes = new Uint8Array( buffer ); var len = bytes.byteLength; - for (var i = 0; i < len; i++) { + for ( var i = 0; i < len; i++ ) { binary += String.fromCharCode( bytes[ i ] ); } return window.btoa( binary ); }; - var reconstituteImage = function (header, image) { + var reconstituteImage = function ( header, image ) { var image_data = image[0], width = parseInt( image[1] ), height = parseInt( image[2] ); var full = atob( header.header ) + atob( image_data ); var bytes = new Uint8Array( full.length ); - for (var i = 0; i < full.length; i++) { - bytes[i] = full.charCodeAt(i); + for ( var i = 0; i < full.length; i++ ) { + bytes[i] = full.charCodeAt( i ); } // Poke the bits. - bytes[ header.height_offset ] = ( (height >> 8) & 0xFF); - bytes[ header.height_offset + 1 ] = (height & 0xFF); - bytes[ header.length_offset ] = ( (width >> 8) & 0xFF); - bytes[ header.length_offset + 1] = (width & 0xFF); + bytes[ header.height_offset ] = ( ( height >> 8 ) & 0xFF ); + bytes[ header.height_offset + 1 ] = ( height & 0xFF ); + bytes[ header.length_offset ] = ( ( width >> 8 ) & 0xFF ); + bytes[ header.length_offset + 1] = ( width & 0xFF ); // Back to a full JPEG now. return arrayBufferToBase64( bytes ); @@ -40,8 +41,8 @@ window.Gaussholder = (function (header) { * @param {list} image 3-tuple of base64-encoded image data, width, height * @param {list} final Final width and height */ - var render = function (canvas, image, final, cb) { - var ctx = canvas.getContext('2d'), + var render = function ( canvas, image, final, cb ) { + var ctx = canvas.getContext( '2d' ), width = parseInt( final[0] ), height = parseInt( final[1] ), radius = parseInt( final[2] ); @@ -53,63 +54,77 @@ window.Gaussholder = (function (header) { ctx.imageSmoothingEnabled = false; var img = new Image(); - img.src = 'data:image/jpg;base64,' + reconstituteImage(header, image); + img.src = 'data:image/jpg;base64,' + reconstituteImage( header, image ); img.onload = function () { canvas.width = width; canvas.height = height; - ctx.drawImage(img, 0, 0, width, height); + ctx.drawImage( img, 0, 0, width, height ); StackBlur.canvasRGB( canvas, 0, 0, width, height, radius ); cb(); }; }; + /** + * Recalculate image ratio when an image renders on the page. + * + * @param {HTMLImageElement} Image element to recalculate dimensions for. + */ + var calculateDimensionStyle = function ( element ) { + var actual = element.getBoundingClientRect(); + var size = element.dataset.gaussholderSize.split( ',' ); + var width = parseInt( size[0], 10 ), height = parseInt( size[1], 10 ); + + if ( actual.width < width ) { + // Rescale, keeping the aspect ratio + height = height * ( actual.width / width ); + width = actual.width; + } else if ( actual.height < height ) { + // Rescale, keeping the aspect ratio + width = width * ( actual.height / height ); + height = actual.height; + } + + element.style.cssText += 'width: ' + width + 'px; height: ' + height + 'px;'; + }; + /** * Render placeholder for an image * * @param {HTMLImageElement} element Element to render placeholder for */ - var handleElement = function (element) { + var handleElement = function ( element ) { if ( ! ( 'gaussholder' in element.dataset ) ) { return; } - var canvas = document.createElement('canvas'); - var final = element.dataset.gaussholderSize.split(','); + var canvas = document.createElement( 'canvas' ); + var final = element.dataset.gaussholderSize.split( ',' ); // Set the dimensions... element.style.width = final[0] + 'px'; element.style.height = final[1] + 'px'; // ...then recalculate based on what it actually renders as - var original = [ final[0], final[1] ]; - if ( element.width < final[0] ) { - // Rescale, keeping the aspect ratio - final[0] = element.width; - final[1] = final[1] * ( final[0] / original[0] ); - } else if ( element.height < final[1] ) { - // Rescale, keeping the aspect ratio - final[1] = element.height; - final[0] = final[0] * ( final[1] / original[1] ); - } - - // Set dimensions, _again_ - element.style.width = final[0] + 'px'; - element.style.height = final[1] + 'px'; + calculateDimensionStyle( element ); - render(canvas, element.dataset.gaussholder.split(','), final, function () { + // Schedule an observer to update on any rendering changes. + render( canvas, element.dataset.gaussholder.split( ',' ), final, function () { // Load in as our background image element.style.backgroundImage = 'url("' + canvas.toDataURL() + '")'; element.style.backgroundRepeat = 'no-repeat'; - }); + setTimeout( function () { + calculateDimensionStyle( element ); + }, 200 ); + } ); }; - var loadOriginal = function (element) { + var loadOriginal = function ( element ) { if ( ! ( 'originalsrc' in element.dataset ) && ! ( 'originalsrcset' in element.dataset ) ) { return; } - var data = element.dataset.gaussholderSize.split(','), + var data = element.dataset.gaussholderSize.split( ',' ), radius = parseInt( data[2] ); // Load our image now @@ -147,7 +162,7 @@ window.Gaussholder = (function (header) { element.style.backgroundRepeat = ''; var start = 0; - var anim = function (ts) { + var anim = function ( ts ) { if ( ! start ) start = ts; var diff = ts - start; if ( diff > fadeDuration ) { @@ -160,9 +175,9 @@ window.Gaussholder = (function (header) { var effectiveRadius = radius * ( 1 - ( diff / fadeDuration ) ); element.style[ filterProp ] = 'blur(' + effectiveRadius * 0.5 + 'px)'; - window.requestAnimationFrame(anim); + window.requestAnimationFrame( anim ); }; - window.requestAnimationFrame(anim); + window.requestAnimationFrame( anim ); }; }; @@ -177,26 +192,26 @@ window.Gaussholder = (function (header) { if ( loopTimeout ) { return; } - loopTimeout = window.setTimeout(scrollHandler, 40); + loopTimeout = window.setTimeout( scrollHandler, 40 ); return; } lastRun = now; - loopTimeout && (loopTimeout = null); + loopTimeout && ( loopTimeout = null ); var next = []; - for (var i = loadLazily.length - 1; i >= 0; i--) { + for ( var i = loadLazily.length - 1; i >= 0; i-- ) { var img = loadLazily[i]; var shouldShow = img.getBoundingClientRect().top <= ( window.innerHeight + threshold ); if ( ! shouldShow ) { - next.push(img); + next.push( img ); continue; } - loadOriginal(img); + loadOriginal( img ); } loadLazily = next; - if (loadLazily.length < 1) { - window.removeEventListener('scroll', scrollHandler); + if ( loadLazily.length < 1 ) { + window.removeEventListener( 'scroll', scrollHandler ); } }; @@ -204,17 +219,17 @@ window.Gaussholder = (function (header) { * Render all placeholders on the page */ var handleAll = function () { - var images = document.getElementsByTagName('img'); + var images = document.getElementsByTagName( 'img' ); - for (var i = images.length - 1; i >= 0; i--) { + for ( var i = images.length - 1; i >= 0; i-- ) { var img = images[i]; // Ensure the blank GIF has loaded first if ( img.complete ) { - handleElement(img); + handleElement( img ); } else { img.onload = function () { - handleElement(this); + handleElement( this ); } } } @@ -222,10 +237,10 @@ window.Gaussholder = (function (header) { loadLazily = images; scrollHandler(); - if (loadLazily.length > 0) { - window.addEventListener('scroll', scrollHandler); + if ( loadLazily.length > 0 ) { + window.addEventListener( 'scroll', scrollHandler ); } }; return handleAll; -})(window.GaussholderHeader); +} )( window.GaussholderHeader ); diff --git a/assets/gaussholder.min.js b/assets/gaussholder.min.js index c24b542..c95ed78 100644 --- a/assets/gaussholder.min.js +++ b/assets/gaussholder.min.js @@ -1 +1 @@ -var StackBlur=function(){function a(a,b,c,d,e){if("string"==typeof a)var a=document.getElementById(a);else if(!a instanceof HTMLCanvasElement)return;var f,g=a.getContext("2d");try{f=g.getImageData(b,c,d,e)}catch(a){throw new Error("unable to access image data: "+a)}return f}function b(b,d,e,f,g,h){if(!(isNaN(h)||h<1)){h|=0;var i=a(b,d,e,f,g);i=c(i,d,e,f,g,h),b.getContext("2d").putImageData(i,d,e)}}function c(a,b,c,g,h,i){var j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D=a.data,E=i+i+1,F=g-1,G=h-1,H=i+1,I=H*(H+1)/2,J=new d,K=J;for(l=1;l>P,D[o+1]=r*O>>P,D[o+2]=s*O>>P,q-=t,r-=u,s-=v,t-=M.r,u-=M.g,v-=M.b,m=p+((m=j+i+1)>P,D[m+1]=r*O>>P,D[m+2]=s*O>>P,q-=t,r-=u,s-=v,t-=M.r,u-=M.g,v-=M.b,m=j+((m=k+H)>8&255,h[a.height_offset+1]=255&f,h[a.length_offset]=e>>8&255,h[a.length_offset+1]=255&e,c(h)},e=function(b,c,e,f){var g=b.getContext("2d"),h=parseInt(e[0]),i=parseInt(e[1]),j=parseInt(e[2]);g.mozImageSmoothingEnabled=!1,g.webkitImageSmoothingEnabled=!1,g.msImageSmoothingEnabled=!1,g.imageSmoothingEnabled=!1;var k=new Image;k.src="data:image/jpg;base64,"+d(a,c),k.onload=function(){b.width=h,b.height=i,g.drawImage(k,0,0,h,i),StackBlur.canvasRGB(b,0,0,h,i,j),f()}},f=function(a){if("gaussholder"in a.dataset){var b=document.createElement("canvas"),c=a.dataset.gaussholderSize.split(",");a.style.width=c[0]+"px",a.style.height=c[1]+"px";var d=[c[0],c[1]];a.widthb)return a.style[c]="",a.style.clipPath="",void(a.style.webkitClipPath="");var i=d*(1-h/b);a.style[c]="blur("+.5*i+"px)",window.requestAnimationFrame(g)};window.requestAnimationFrame(g)}}},h=[],i=1200,j=0,k=null,l=function(){var a=Date.now();if(j+40>a){if(k)return;return void(k=window.setTimeout(l,40))}j=a,k&&(k=null);for(var b=[],c=h.length-1;c>=0;c--){var d=h[c],e=d.getBoundingClientRect().top<=window.innerHeight+i;e?g(d):b.push(d)}h=b,h.length<1&&window.removeEventListener("scroll",l)},m=function(){for(var a=document.getElementsByTagName("img"),b=a.length-1;b>=0;b--){var c=a[b];c.complete?f(c):c.onload=function(){f(this)}}h=a,l(),h.length>0&&window.addEventListener("scroll",l)};return m}(window.GaussholderHeader); \ No newline at end of file +var StackBlur=function(){var N=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],U=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function q(){this.r=0,this.g=0,this.b=0,this.a=0,this.next=null}return{canvasRGB:function(t,e,n,a,r,i){if(!(isNaN(i)||i<1)){i|=0;var s=function(t,e,n,a,r){if("string"==typeof t)t=document.getElementById(t);else if(!t instanceof HTMLCanvasElement)return;var i,s=t.getContext("2d");try{i=s.getImageData(e,n,a,r)}catch(t){throw new Error("unable to access image data: "+t)}return i}(t,e,n,a,r);s=function(t,e,n,a,r,i){var s,o,l,g,d,c,u,h,f,w,m,p,v,b,y,x,I,k,C,E,S=t.data,B=i+i+1,R=a-1,A=r-1,L=i+1,P=L*(L+1)/2,T=new q,z=T;for(l=1;l>H,S[c+1]=f*_>>H,S[c+2]=w*_>>H,h-=m,f-=p,w-=v,m-=F.r,p-=F.g,v-=F.b,g=u+((g=s+i+1)>H,S[g+1]=f*_>>H,S[g+2]=w*_>>H,h-=m,f-=p,w-=v,m-=F.r,p-=F.g,v-=F.b,g=s+((g=o+L)>8&255,s[t.height_offset+1]=255&r,s[t.length_offset]=a>>8&255,s[t.length_offset+1]=255&a,function(t){for(var e="",n=new Uint8Array(t),a=n.byteLength,r=0;r