得到一个需求,要求书籍封面的背景和书籍封面的颜色有一定的一致性,也就是如下这种情况:
这种情况,我们解决的思路就是:利用canvas的图像高斯模糊算法,通过高斯模糊书籍封面,获得一张背景图像,结果就是这样:
我们需要依次解决以下两个问题:
1.基于canvas的高斯模糊算法
2.书籍封面是取自阿里大数据服务器的,canvas不支持非本地图片的drawImage和getImageData
针对第一个问题。高斯模糊算法网上有,这就是算法源码:
1 function stackBlurCanvasRGBA( canvas, top_x, top_y, width, height, radius ) 2 { 3 if ( isNaN(radius) || radius < 1 ) return; 4 radius |= 0; 5 6 if (typeof(canvas) == "string") 7 var canvas = document.getElementById( canvas ); 8 else if (!canvas instanceof HTMLCanvasElement) 9 return; 10 11 var context = canvas.getContext("2d"); 12 var imageData; 13 try { 14 try { 15 imageData = context.getImageData( top_x, top_y, width, height ); 16 } catch(e) { 17 18 // NOTE: this part is supposedly only needed if you want to work with local files 19 // so it might be okay to remove the whole try/catch block and just use 20 // imageData = context.getImageData( top_x, top_y, width, height ); 21 try { 22 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 23 imageData = context.getImageData( top_x, top_y, width, height ); 24 } catch(e) { 25 //alert("Cannot access local image"); 26 throw new Error("unable to access local image data: " + e); 27 return; 28 } 29 } 30 } catch(e) { 31 //alert("Cannot access image"); 32 throw new Error("unable to access image data: " + e); 33 } 34 35 var pixels = imageData.data; 36 37 var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, 38 r_out_sum, g_out_sum, b_out_sum, a_out_sum, 39 r_in_sum, g_in_sum, b_in_sum, a_in_sum, 40 pr, pg, pb, pa, rbs; 41 42 var div = radius + radius + 1; 43 var w4 = width << 2; 44 var widthMinus1 = width - 1; 45 var heightMinus1 = height - 1; 46 var radiusPlus1 = radius + 1; 47 var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; 48 49 var stackStart = new BlurStack(); 50 var stack = stackStart; 51 for ( i = 1; i < div; i++ ) 52 { 53 stack = stack.next = new BlurStack(); 54 if ( i == radiusPlus1 ) var stackEnd = stack; 55 } 56 stack.next = stackStart; 57 var stackIn = null; 58 var stackOut = null; 59 60 yw = yi = 0; 61 62 var mul_sum = mul_table[radius]; 63 var shg_sum = shg_table[radius]; 64 65 for ( y = 0; y < height; y++ ) 66 { 67 r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; 68 69 r_out_sum = radiusPlus1 * ( pr = pixels[yi] ); 70 g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 71 b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 72 a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] ); 73 74 r_sum += sumFactor * pr; 75 g_sum += sumFactor * pg; 76 b_sum += sumFactor * pb; 77 a_sum += sumFactor * pa; 78 79 stack = stackStart; 80 81 for ( i = 0; i < radiusPlus1; i++ ) 82 { 83 stack.r = pr; 84 stack.g = pg; 85 stack.b = pb; 86 stack.a = pa; 87 stack = stack.next; 88 } 89 90 for ( i = 1; i < radiusPlus1; i++ ) 91 { 92 p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 93 r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i ); 94 g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs; 95 b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs; 96 a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs; 97 98 r_in_sum += pr; 99 g_in_sum += pg; 100 b_in_sum += pb; 101 a_in_sum += pa; 102 103 stack = stack.next; 104 } 105 106 107 stackIn = stackStart; 108 stackOut = stackEnd; 109 for ( x = 0; x < width; x++ ) 110 { 111 pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum; 112 if ( pa != 0 ) 113 { 114 pa = 255 / pa; 115 pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa; 116 pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa; 117 pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa; 118 } else { 119 pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0; 120 } 121 122 r_sum -= r_out_sum; 123 g_sum -= g_out_sum; 124 b_sum -= b_out_sum; 125 a_sum -= a_out_sum; 126 127 r_out_sum -= stackIn.r; 128 g_out_sum -= stackIn.g; 129 b_out_sum -= stackIn.b; 130 a_out_sum -= stackIn.a; 131 132 p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 133 134 r_in_sum += ( stackIn.r = pixels[p]); 135 g_in_sum += ( stackIn.g = pixels[p+1]); 136 b_in_sum += ( stackIn.b = pixels[p+2]); 137 a_in_sum += ( stackIn.a = pixels[p+3]); 138 139 r_sum += r_in_sum; 140 g_sum += g_in_sum; 141 b_sum += b_in_sum; 142 a_sum += a_in_sum; 143 144 stackIn = stackIn.next; 145 146 r_out_sum += ( pr = stackOut.r ); 147 g_out_sum += ( pg = stackOut.g ); 148 b_out_sum += ( pb = stackOut.b ); 149 a_out_sum += ( pa = stackOut.a ); 150 151 r_in_sum -= pr; 152 g_in_sum -= pg; 153 b_in_sum -= pb; 154 a_in_sum -= pa; 155 156 stackOut = stackOut.next; 157 158 yi += 4; 159 } 160 yw += width; 161 } 162 163 164 for ( x = 0; x < width; x++ ) 165 { 166 g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; 167 168 yi = x << 2; 169 r_out_sum = radiusPlus1 * ( pr = pixels[yi]); 170 g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]); 171 b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]); 172 a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]); 173 174 r_sum += sumFactor * pr; 175 g_sum += sumFactor * pg; 176 b_sum += sumFactor * pb; 177 a_sum += sumFactor * pa; 178 179 stack = stackStart; 180 181 for ( i = 0; i < radiusPlus1; i++ ) 182 { 183 stack.r = pr; 184 stack.g = pg; 185 stack.b = pb; 186 stack.a = pa; 187 stack = stack.next; 188 } 189 190 yp = width; 191 192 for ( i = 1; i <= radius; i++ ) 193 { 194 yi = ( yp + x ) << 2; 195 196 r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i ); 197 g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs; 198 b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs; 199 a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs; 200 201 r_in_sum += pr; 202 g_in_sum += pg; 203 b_in_sum += pb; 204 a_in_sum += pa; 205 206 stack = stack.next; 207 208 if( i < heightMinus1 ) 209 { 210 yp += width; 211 } 212 } 213 214 yi = x; 215 stackIn = stackStart; 216 stackOut = stackEnd; 217 for ( y = 0; y < height; y++ ) 218 { 219 p = yi << 2; 220 pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum; 221 if ( pa > 0 ) 222 { 223 pa = 255 / pa; 224 pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa; 225 pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa; 226 pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa; 227 } else { 228 pixels[p] = pixels[p+1] = pixels[p+2] = 0; 229 } 230 231 r_sum -= r_out_sum; 232 g_sum -= g_out_sum; 233 b_sum -= b_out_sum; 234 a_sum -= a_out_sum; 235 236 r_out_sum -= stackIn.r; 237 g_out_sum -= stackIn.g; 238 b_out_sum -= stackIn.b; 239 a_out_sum -= stackIn.a; 240 241 p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 242 243 r_sum += ( r_in_sum += ( stackIn.r = pixels[p])); 244 g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1])); 245 b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2])); 246 a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3])); 247 248 stackIn = stackIn.next; 249 250 r_out_sum += ( pr = stackOut.r ); 251 g_out_sum += ( pg = stackOut.g ); 252 b_out_sum += ( pb = stackOut.b ); 253 a_out_sum += ( pa = stackOut.a ); 254 255 r_in_sum -= pr; 256 g_in_sum -= pg; 257 b_in_sum -= pb; 258 a_in_sum -= pa; 259 260 stackOut = stackOut.next; 261 262 yi += width; 263 } 264 } 265 266 context.putImageData( imageData, top_x, top_y ); 267 268 }
而第二个问题也很简单,canvas虽然不支持处理外域图片,但是可以接收图像的base64
把图片处理成base64,后端服务器语言只需调用一个函数就能做到,但是大规模字符串传递会影响服务器效率,所以,将图片转换成base64只能通过前端解决
由于只考虑谷歌浏览器,所以这方面的转换十分简单,
XMLHttpRequest 2.0 可以设置responseType, 我们将其设置为 blob
然后将blob转换成base64
代码如下:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> 6 <meta name="apple-mobile-web-app-capable" content="yes"> 7 <meta name="format-detection" content="telephone=no"> 8 <title>Examples</title> 9 <meta name="description" content=""> 10 <meta name="keywords" content=""> 11 <link href="" rel="stylesheet"> 12 <style> 13 .hero { 14 padding: 3em 0; 15 position: relative; 16 } 17 .hero__background { 18 position: absolute; 19 top: 0; 20 left: 0; 21 width: 100%; 22 height: 200px; 23 z-index: 1; 24 } 25 .hero__title { 26 position: relative; 27 z-index: 2; 28 color: #fff; 29 text-align: center; 30 } 31 32 </style> 33 34 <body> 35 <div class="hero"> 36 <canvas class="hero__background" id="heroCanvas" width="200" height="200" data-canvas></canvas> 37 <!--<h1 class="hero__title"></h1>--> 38 </div> 39 40 <!-- Our image to be blurred http://oss-asq-img.11222.cn/cover/raw/2013081212197381.jpg--> 41 42 <img id="img" data-canvas-image style="top:200px; position:relative; "> 43 <canvas id="mc" style="height:200px; width:320px;"></canvas> 44 <div id="add" style="height:300px; width:400px;"> 45 46 </div> 47 </body> 48 <script type="text/javascript" src="js/stackBoxBlur.js"></script> 49 <script type="text/javascript"> 50 51 52 </script> 53 <script> 54 function ajax(url,method,funcs){ 55 var xhr=new XMLHttpRequest(); 56 xhr.open(method,url,true); 57 xhr.responseType='blob'; 58 xhr.onload=function(e){ 59 if(this.status==200){ 60 funcs(this.response); 61 } 62 } 63 xhr.send(null); 64 } 65 var url='http://xxxxxxxxxxx/demo/abc.jpg'; 66 ajax(url,'get',function(x){ 67 var bb=new Blob([x],{type:'image/jpeg'}); 68 var ak=btoa(bb); 69 var reader = new window.FileReader(); 70 reader.readAsDataURL(x); 71 reader.onloadend = function() { 72 ak = this.result; 73 document.querySelector('#img').src=ak; 74 var BLUR_RADIUS = 100; 75 var canvas = document.querySelector('[data-canvas]'); 76 var canvasContext = canvas.getContext('2d'); 77 var image = new Image(); 78 image.src = ak; 79 image.crossOrigin = "Anonymous"; 80 var drawBlur = function() { 81 var w = canvas.width; 82 var h = canvas.height; 83 canvasContext.drawImage(image, 0, 0, w, h); 84 stackBlurCanvasRGBA('heroCanvas', 0, 0, w, h, BLUR_RADIUS); 85 }; 86 image.onload = function() { 87 drawBlur(); 88 } 89 } 90 }); 91 92 </script> 93 </html>
问题解决