JavaScript 马赛克

版权声明:转载请注明出处—— https://blog.csdn.net/chy555chy/article/details/54648318
<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安全浏览器的配置方式也基本相同。

这里写图片描述

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

猜你喜欢

转载自blog.csdn.net/chy555chy/article/details/54648318