JavaScript 马赛克

来源:转载

<html> <head> <meta charset="utf8" /> <title>马赛克(mosaic)</title> <style> body { background: gray; color: white; text-align: center; } .marginTop { margin-top: 10px; } #mosaicPixelResolutionRange { width: 350px; } </style> </head> <body> <div id="mosaicType" class="marginTop">马赛克类型(mosaic type):</div> <input type="radio" name="mosaicType" value="square" checked />square <input type="radio" name="mosaicType" value="triangle"/>triangle <input type="radio" name="mosaicType" value="circle"/>circle <input type="radio" name="mosaicType" value="diamond"/>diamond <input type="radio" name="mosaicType" value="start1"/>start1 <input type="radio" name="mosaicType" value="start2"/>start2 <div id="mosaicPixelResolution" class="marginTop">马赛克像素分辨率(mosaic pixel resolution):</div> <input id="mosaicPixelResolutionRange" type="range" min="1" max="30" step="0.1" value="0" /> <p>注意: 该demo若在本地运行, 则必须修改"chrome快捷方式"->"属性"->"目标",<br/>添加启动参数"--allow-file-access-from-files", 允许跨域访问.</p> <div id="imgContainer" class="marginTop"></div> <script> var mosaicClassName = "mosaicImg"; var doublePI = Math.PI * 2; var quarterPI = Math.PI / 4; //方括号([])表示字符范围 //点(.)匹配除“/r/n”之外的任何单个字符。要匹配包括“/r/n”在内的任何字符,请使用像“[/s/S]”的模式。 //var reg = new RegExp("[object.Array]"); ///s是匹配所有空白字符,/S是匹配所有非空白字符搜索,那么[/s/S]这个组合就可以匹配所有字符了。 //var reg = new RegExp("[object[//s//S]*Array]"); ///w匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。/W匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。 var arrayReg = new RegExp("[object[//w//W]*Array]"); //判断是否是数字类型 function isNumber(obj) { return typeof obj === "number"; } //判断是否是数组, typeof Array = "object".普通数组是[object Array], 图像数据的数组是[object Uint8ClampedArray] function isArray(obj) { return arrayReg.test(Object.prototype.toString.call(obj)); } //判断是否是对象, typeof Object = "object" function isObject(obj) { return Object.prototype.toString.call(obj) === "[object Object]"; } //获取打马的类型 function getMosaicType() { var mosaicTypes = document.getElementsByName("mosaicType"); for(var i=0; i < mosaicTypes.length; i++) { if(mosaicTypes[i].checked) { return mosaicTypes[i].value; } } } //获取马赛克像素分辨率 function getMosaicPixelResolution() { var mosaicPixelResolutionRange = document.getElementById("mosaicPixelResolutionRange"); //从input type="range" 控件中拿出的value是string类型的 return Number(mosaicPixelResolutionRange.value); } //设置马赛克类型提示文本 function setMosaicTypeText(type) { var mosaicType = document.getElementById("mosaicType"); var strArr = mosaicType.innerHTML.split(":"); if(strArr.length > 1) { mosaicType.innerHTML = strArr[0] + ":" + type; } else { mosaicType.innerHTML += ":" + type; } } //设置马赛克像素分辨率滑动条提示文本 function setMosaicPixelResolutionRangeText(resolution) { var mosaicPixelResolution = document.getElementById("mosaicPixelResolution"); var strArr = mosaicPixelResolution.innerHTML.split(":"); if(strArr.length > 1) { mosaicPixelResolution.innerHTML = strArr[0] + ":" + resolution; } else { mosaicPixelResolution.innerHTML += (":" + resolution); } } //马赛克类型单选按钮被点击时触发 function onMosaicTypeRadioClick() { var type = event.target.value; setMosaicTypeText(type); renderAll(type, undefined); } //马赛克像素分辨率滑动条的值改变时触发 function onMosaicPixelResolutionRangeChange() { var resolution = event.target.value; setMosaicPixelResolutionRangeText(resolution); renderAll(undefined, Number(resolution)); } //渲染所有图像 function renderAll(mosaicType, mosaicPixelResolution) { if(mosaicType === undefined) { mosaicType = getMosaicType(); } if(mosaicPixelResolution === undefined) { mosaicPixelResolution = getMosaicPixelResolution(); } var elems = document.getElementsByClassName(mosaicClassName); for(var i=0; i<elems.length; i++) { elems[i].render(mosaicType, mosaicPixelResolution); } } //创建img,然后将其隐藏,用同等大小的canvas替代 function createImg(id, cls, src) { var imgContainer = document.getElementById("imgContainer"); var img = new Image(); img.onload = function() { var canvas = document.createElement("canvas"); //id必须直接设置,而class必须通过attribute属性来设置 canvas.id = id; if(cls) { canvas.setAttribute("class", cls); } //如果style中有设置width或height,就用它们的设置 var width, height; if(canvas.style.width) { width = canvas.style.width; } else { width = img.width; } if(canvas.style.height) { height = canvas.style.height; } else { height = img.height; } canvas.width = width; canvas.height = height; imgContainer.appendChild(canvas); var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); canvas.imageData = ctx.getImageData(0, 0, width, height); } img.src = src; } //(x,y)处绘制长度为dx,dy的形状 function drawRegion(ctx, imageData, x, y, type, resolution) { //每个点用4个字节存储, 分别是RGBA, alpha通道(0-255; 0是透明的,255是完全可见的), 数据按行排列。 //注意: 数组的索引必须是一个整数 var halfResolution = resolution * .5; var cx = x + halfResolution; var cy = y + halfResolution; var tmpX = cx; var tmpY = cy; if(cx > imageData.width || cy > imageData.height) { if(cx > imageData.width) { tmpX = cx - halfResolution } if(cy > imageData.height) { tmpY = cy - halfResolution; } } tmpX = Math.floor(tmpX); tmpY = Math.floor(tmpY); var index = (tmpY * imageData.width + tmpX) * 4; var r = imageData.data[index]; var g = imageData.data[index + 1]; var b = imageData.data[index + 2]; var a = imageData.data[index + 3]; ctx.fillStyle = "rgba(" + [r,g,b,a].join(",") + ")"; switch(type) { case "square": ctx.fillRect(x, y, resolution, resolution); break; case "triangle": ctx.beginPath(); ctx.moveTo(cx, y); ctx.lineTo(x + resolution, y + resolution); ctx.lineTo(x, y + resolution); ctx.closePath(); ctx.fill(); break; case "circle": ctx.beginPath(); ctx.arc(cx, cy, halfResolution, 0, doublePI); ctx.fill(); break; case "diamond": var diamondResolution = Math.SQRT2 * halfResolution; var halfDiamondResolution = diamondResolution / 2; ctx.save(); ctx.translate(cx, cy); ctx.rotate(quarterPI); ctx.fillRect(-halfDiamondResolution, -halfDiamondResolution, diamondResolution, diamondResolution); ctx.restore(); break; case "start1": var oneThirdResolution = resolution / 3; ctx.beginPath(); ctx.moveTo(cx, y); ctx.lineTo(x + resolution, y + resolution); ctx.lineTo(x, y + oneThirdResolution); ctx.lineTo(x + resolution, y + oneThirdResolution); ctx.lineTo(x, y + resolution); ctx.closePath(); ctx.fill(); break; case "start2": var oneThirdResolution = resolution / 3; ctx.beginPath(); ctx.moveTo(cx, y); ctx.lineTo(x + resolution, y + 2 * oneThirdResolution); ctx.lineTo(x, y + 2 * oneThirdResolution); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx, y + resolution); ctx.lineTo(x + resolution, y + oneThirdResolution); ctx.lineTo(x, y + oneThirdResolution); ctx.closePath(); ctx.fill(); break; } } function init() { setMosaicTypeText(getMosaicType()); setMosaicPixelResolutionRangeText(getMosaicPixelResolution()); var mosaicTypes = document.getElementsByName("mosaicType"); for(var i=0; i<mosaicTypes.length; i++) { mosaicTypes[i].addEventListener("click", onMosaicTypeRadioClick); } mosaicPixelResolutionRange.addEventListener("change", onMosaicPixelResolutionRangeChange); HTMLCanvasElement.prototype.render = function(mosaicType, mosaicPixelResolution) { if(!this.imageData || !isArray(this.imageData.data) || !isNumber(this.imageData.width) || !isNumber(this.imageData.height)) { return; } var ctx = this.getContext("2d"); ctx.clearRect(0, 0, this.imageData.width, this.height); if(mosaicPixelResolution <= 1) { //马赛克像素分辨率等于1的情况不需要处理, 否则太慢了. 一张42KB的图要执行1.3秒. 而直接putImage只要0.5ms //ctx.putImageData(this.imageData, 0, 0); var dirtyX = 0; var dirtyY = 0; var dirtyWidth = this.width; var dirtyHeight = this.height; ctx.putImageData(this.imageData, 0, 0, dirtyX, dirtyY, dirtyWidth, dirtyHeight); } else { for(var x=0; x <= this.imageData.width; x += mosaicPixelResolution) { for(var y=0; y <= this.height; y += mosaicPixelResolution) { drawRegion(ctx, this.imageData, x, y, mosaicType, mosaicPixelResolution); } } } } createImg("img1", mosaicClassName, "1.jpg"); } init(); </script> </body></html>

chrome允许跨域访问的方式如下:
Microsof Edge、360安全浏览器的配置方式也基本相同。

火狐上不用配置就可以直接预览。

分享给朋友:
您可能感兴趣的文章:
随机阅读: