• 利用box-shadow绘图


    上篇博客提到过,box-shadow属性的本质是对形状的复制,那么如果我设置一个1*1px的i标签,利用box-shadow可以叠加的特性,给每一个1*1px的阴影赋上颜色,那么最后不就是一幅图片了么。

    html代码很简单:

    <!doctype html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <title>canvas</title>
    <style>
        #shadow {
            position: relative;
        }
        #shadow i {
            display: block;
             1px;
            height: 1px;
        }
    </style>
    <script src="shadowShow.class.js" defer="defer"></script>
    </head>
    <body>
    <input id="file" class="file-upload" type="file" accept="image/png, image/gif" />
    <canvas id="canvas">
        您的浏览器不支持canvas标签
    </canvas>
    <div id="shadow"><i></i></div>
    <script>
    var init = {
        fileObj: 'file',  //input控件的id
        canvas: 'canvas',  //canvas的标签的id
        shadowBox: 'shadow',  //用于阴影显示的标签的id
    };
    </script>
    
    </body>
    </html>
    

      做了一个简单的图片上传控件,页面的js里读取了一些简单的控件信息。

      最初在直接用控件获取图片路径时,碰到了路径显示为fakepath的问题(直接给控件赋路径src='#'有效果),所以没法读取图片的路径。

      查询后使用了FileReader()解决了这个问题,FileReader()读取图片信息的使用很简单,请自行百度。

      重点还是用canvas的getImageData()获取了图片每个像素点的rgba信息。图片上的每个点都有rgb三原色加上一个透明度的信息,这些信息都放在getImageData()的data数组里,换句话说,data数组里每4个值组合起来就是一个像素点的颜色信息。而每个像素点的位置可以根据这是第几个像素点(每个像素点都是1*1px),加上原图片的长宽信息来计算得出,宽度上data数组里每4个值宽度加1px,如果像素点的宽度超过了图片的宽度,那么就说明该像素点是下一行的,即将高度加1px,宽度重置。

      又像素点的信息都是保存在一个名叫data的一维数据里,且每4条数据是一个像素点的颜色信息,所以可以每4条数据一读,比如 if (i % 4 === 0),那么像素点的red通道即是image.data[index],green通道是imageData.data[index + 1],blue通道是imageData.data[index + 2],alpha通道是imageData.data[index + 3],最后把这些颜色信息处理下通通push到一个数组里,赋给i标签的box-shadow属性就大功告成。

      下面给出完整的js代码:

    (function() {
        /**
        最初我的init方法是这样的,但是在对象的属性里添加监听事件貌似无效,所以挪到了对象外
        init: function() {
                console.log(this.fileObj);
                 this.fileObj.addEventListener('change', function() {
                    shadowShow.upPic();
                 });
            },
        */
        document.getElementById(init.fileObj).addEventListener('change', function() {
            shadowShow.init();
        });
    
        var shadowShow =  {
            fileObj: document.getElementById(init.fileObj),
            canvas: document.getElementById(init.canvas),
            ctx: this.canvas.getContext('2d'),
            shadowBox: document.getElementById(init.shadowBox),
    
            init: function() {
                shadowShow.upPic();
            },
    
        /**
        *用于获取上传的图片的信息并在canvas1中显示
        *@method upPic
        */
            upPic: function() {
                image = new Image();
                this.setImageUrl(image, this.fileObj);
                image.onload = function() {
                    // cavas的宽高用setAttribute设置,用canvas.width也可以,注意值不能加上单位
                    width = this.width;
                    height = this.height;
                    shadowShow.canvas.setAttribute('width', width);
                    shadowShow.canvas.setAttribute('height', height);
                    shadowShow.ctx.drawImage(this, 0, 0, width, height);
                    shadowShow.toShadowShow(this, shadowShow.ctx);
                }
            },
    
        /**
        *用于设置创建的iamge对象的url
        *@method setImageUrl
        *@param {obj} image 创建的iamge对象
        *@param {obj} fileQuery 上传图片的file控件对象
        */
            setImageUrl: function(image, fileQuery) { 
                var file = fileQuery.files[0]; 
                var reader = new FileReader();  
                reader.onload = function(e) { 
                    image.src = e.target.result;
                }
                reader.readAsDataURL(file);  
            },
    
        /**
        *用于将上传的图片的用box-shadow表现出来
        *@method toBoxShadow
        *@param {obj} image 创建的iamge对象
        *@param {obj} context 创建的绘图的上下文对象
        */
            toShadowShow: function(image, context) {
                width = image.width;
                height = image.height;
                this.shadowBox.style.width = width + 'px';
                this.shadowBox.style.height = height + 'px'; 
                var imageData = context.getImageData(0, 0, width, height);
                var arrShadow = new Array();
                var index = 0;
                var interval = setInterval(function() {
                    if (index < imageData.data.length) {
                        // 每4个参数处理一次
                        if (index % 4 === 0) {
                            var x = index / 4 % width + 1,  //x坐标,以index=4为例,index/4=1,表示x是第1个像素点,1%width是为了获得第n行的像素点的x坐标,又index是从0开始的(index=0是真正的第一个像素点),所以x的坐标需要加上1
                                y = Math.floor(index / 4 / width) + 1, //y坐标,
                                alpha = Math.round(imageData.data[index + 3] / 255 * 100) / 100;  //透明度,imageData.data[index+3]/255需要乘以100是因为(imageData.data[index+3]/255)肯定是小于1的,为了避免round()取整值为1,故需要乘以100
                            if (alpha == 255) {  //像素点没有透明度
                                //将rgb转为16进制
                                var redToHex = imageData.data[index].toString(16),
                                    greenToHex = imageData.data[index + 1].toString(16),
                                    blueToHex = imageData.data[index + 2].toString(16);
                                redToHex = redToHex.length == 1 ? ('0' + redToHex) : redToHex;
                                greenToHex = greenToHex.length == 1 ? ('0' + greenToHex) : greenToHex;
                                blueToHex = blueToHex.length == 1 ? ('0' + blueToHex) : blueToHex;
                                var hex = redToHex + greenToHex + blueToHex;
                                //对形如#FFF的十六进制颜色作处理
                                if (redToHex.slice(0,1) == redToHex.slice(-1) && greenToHex.slice(0,1) == greenToHex(-1) && blueToHex.slice(0,1) == blueToHex.slice(-1)) {
                                    hex = redToHex.slice(-1) + greenToHex.slice(-1) + blueToHex.slice(-1);
                                }
                                arrShadow.push(x + 'px ' + y + 'px #' + hex);
                            } else if (alpha > 0) {
                                a = [imageData.data[index], imageData.data[index + 1], imageData.data[index + 2], alpha].join();
                                arrShadow.push(x + 'px ' + y + 'px rgba(' + a + ')');
                            } 
                        }
                        shadowShow.shadowBox.querySelector('i').style.boxShadow = arrShadow.join(); 
                    } else {
                        clearInterval(interval);  //清除setInterval
                    }
                    index++;
                });    
            }
        };
    })();
    

      这段代码我封装了一下,注释写的比较详细了,同时,加了一个定时器,这样就有了一个像素一个像素显示的效果,就是如果图片的width和height值比较大的话,完整的图片显示出来会比较慢。setInterval()的默认值查了下,如果设置的时间间隔小于10,则以10来算,即10ms,但是还是感觉图片显示的挺慢的,当然了,这要看图片的像素点的数量了。

      补充:

      当然了,如果仅需遍历每个像素点其实也不用像上面那么麻烦(上面主要是为了获得每个点的坐标(x, y)),如下就可以了:

      

    //简单的灰阶过滤器
    for (i = 0; i < imageData.data.length; i += 4) {
        var r = data[i],
             g = data[i + 1],
             b = data[i + 2],
             a = data[i + 3];
    
        var average = Math.floor((r + g + b) / 3);
        data[i] = data[i + 1] = data[i + 2] = average;
    }
    

      

      最后更新于2016年11月22日

  • 相关阅读:
    当算法提升到哲学层面—小议验证码识别
    2014总结
    [脚本无敌2]python获取cocos 2dx项目文件列表
    单幅图构建三维图
    [思考]画个圈圈诅咒你
    Mybatis2
    Mybatis1
    淘淘商城虚拟机启动命令
    Zookeeper集群搭建zookeeper01启动不成功解决方案
    Mybatis的xml文件的相关配置
  • 原文地址:https://www.cnblogs.com/youyouluo/p/5839709.html
Copyright © 2020-2023  润新知