canvas马赛克 -Kaiqisan

canvas马赛克

对于canvas处理像素功能的一项利用,把图像马赛克化,并且可以调整模糊度。

主要思路

下图为思路解析

masaiek-size

先获取马赛克颗粒程度size,然后把整张图片分成很多个size大小的块,然后在每一块里面随机获取一个像素点的rgba信息(也可以固定获取某个点的坐标(相对于一个小块)),然后把整个小块都变成这个颜色,最后形成整张马赛克。

虽然这听起来可能简单,但需要您有一定的数学能力,就说说上面的像素吧,我们获取来的像素信息只是一个一维数组,他从左上角的第一个像素开始,然后向右推进,不断获取像素信息,到了最右端之后再换行,从第二行开始接着向右遍历,直到最后一行的最右边的像素,最后组成一个很长的一维数组。但理想的状态下它应该是一个二维数组,学过数据结构的人应该会对这个数据存储结构有更好的理解,它更像二维数组的顺序结构的存储。所以,我们就需要单独地编写接口,通过坐标来获取当前坐标的颜色信息,也能修改颜色。

还有一个问题事关这个程序的兼容性,它一定要兼容所有尺寸的图片,即马赛克的大小size在无法被长宽整除的情况下也能对边角区域做出很好的处理,上图也是这样,边角的长方形区域也需要马赛克处理。

参考源码

	window.onload = () => {
        let canvas = document.getElementById('demo')
        let ctx = canvas.getContext('2d')

        let image = new Image()
        image.src = './imgs/cat2.png' // 图片文件路径自己定义

        image.onload = () => {
            ctx.drawImage(image, 0, 0, image.width * 0.3, image.height * 0.3) // 绘制一张参考图片
            // 存储原图的像素信息,便于调用
            let oldImage = ctx.getImageData(0, 0, image.width * 0.3, image.height * 0.3)

            // 创建一个新的像素数据
            let newImage = ctx.createImageData(image.width * 0.3, image.height * 0.3)

            // 自定义马赛克的大小 ,然后把横向的像素分为 int(width / size)(如果有余数就 + 1)块,纵向同理
            // cross line分别代表横纵的马赛克块的数量(边角料的长方体也包括!)
            let size = 7 // 最重要的参数,可以调整马赛克图片的模糊度。
            let cross = oldImage.width % size === 0 ? oldImage.width / size : Math.floor(oldImage.width / size) + 1
            let line = oldImage.height % size === 0 ? oldImage.height / size : Math.floor(oldImage.height / size) + 1

			// 按照像素块为单位,然后遍历。一个像素块一个像素块处理
            for (let j = 0; j < line; j++) {
                for (let i = 0; i < cross; i++) {
                    // 然后随机从一块里找到某个像素点,获取像素点的颜色
                    
                    // 随机像素坐标的声明
                    let x
                    let y

                    // 判断宽是否能被size整除
                    if (oldImage.width % size !== 0) {
                        // 判断是不是边角料,如果是,就做特殊处理,取的随机坐标值不可超过边缘。
                        if (i === cross - 1) {
                            x = doRandom(cross, size, oldImage.width)
                        } else {
                            x = Math.floor(Math.random() * size)
                        }
                    } else {
                        x = Math.floor(Math.random() * size)
                    }
                    
                    // 这俩方法其实可以二次封装成一个函数,减少重复代码量,为了大家方便理解,所以分成两个
                    if (oldImage.height % size !== 0) {
                        if (j === line - 1) {
                            y = doRandom(line, size, oldImage.height)
                        } else {
                            y = Math.floor(Math.random() * size)
                        }
                    } else {
                        y = Math.floor(Math.random() * size)
                    }

                    let color = getImageColor(oldImage, i * size + x, j * size + y)

                    // 把获取的颜色整到一小块里面去, k,l代表马赛克块的坐标。
                    for (let k = 0; k < size; k++) {
                        for (let l = 0; l < size; l++) {
                            // 越界就不渲染,否则就会出现重复渲染(最右端的像素跑到最左端)
                           if (k + i * size >= oldImage.width || j * size + l >= oldImage.height){
                            } else {
                                setImageColor(newImage, k + i * size, j * size + l, color)
                            }
                        }
                    }
                }
            }
            // 处理完之后最后放回
            ctx.putImageData(newImage, image.width * 0.3 + 20, 0)
        }

        // 给一个坐标,获取该坐标的颜色
        function getImageColor(imageData, x, y) {
            let width = imageData.width
            let color = []
            color[0] = imageData.data[(y * width + x) * 4]
            color[1] = imageData.data[(y * width + x) * 4 + 1]
            color[2] = imageData.data[(y * width + x) * 4 + 2]
            color[3] = imageData.data[(y * width + x) * 4 + 3]
            return color
        }

        // 给一个坐标,设置该坐标的颜色
        function setImageColor(imageData, x, y, color) {
            let width = imageData.width
            imageData.data[(y * width + x) * 4] = color[0]
            imageData.data[(y * width + x) * 4 + 1] = color[1]
            imageData.data[(y * width + x) * 4 + 2] = color[2]
            imageData.data[(y * width + x) * 4 + 3] = color[3]
        }

        // 获取一个随机像素点(假如马赛克块在图片的最右端或最下端且 width(height) % size != 0的时候会用到)
        let doRandom = (max, size, border) => {
            return Math.floor(Math.random() * (border - size * (max - 1)))
        }

    }

显示效果

effect

总结

马赛克处理方法是画布像素处理的一个重要应用,应用的数学原理也较为简单,易于理解。大家也可以根据这个原理来绘制通过鼠标来控制马赛克区域(绘制区域变成马赛克),还是那句话,没有代码做不出来的效果,人们大众的需求的无穷尽的,我们的工作就是去尽可能地满足他们的需求。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kaiqisan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值