zoukankan      html  css  js  c++  java
  • 别再@官方啦,普天同庆加国旗

    别再@官方啦,普天同庆加国旗

    国庆就要来了!今年是新中国成立70周年,大家的热情都很高涨。这不,我今天一翻朋友圈,被齐刷刷地带国旗的头像给刷屏了!

    不过还有些朋友不明就里,还在不停地@微信官方,希望能自动给头像加上一面国旗。然而等了半天,还是毫无反应……

    当然,程序员朋友,也开始了一顿操作。

    python 用 python-opencv 库,直接调。

    go 用 image 库,直接调。

    那么前端呢? 咱们前端不用库,原生的 js + canvas  简单快捷。

    一、思路分析

        大致有两种办法可以实现。

    1.   使用 将图片画在 canvas 上面,然后通过 api 获取 图片的像素值,进行叠加,然后再写入 canvas ,最后导出图片。好处是:可以对透明的图像进行处理,自定义程度高。缺点:控制起来复杂度高,计算量大。
    2.    直接将两张图片分两次画在 canvas 上面,然后导出图片 。 好处:简单快捷。 缺点:透明图像不能处理,可控性小。

    这里我采用第二种办法,简单点。。

    二、技术点

        1、  获取画布:

    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    

      

       2、将图片画入canvas

    context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);  

      3、导出图片

    canvas.toDataURL("image/png");
    

     4、有了这些方法,我们的图片从哪里来?

     通过  <input type="file" name="" id="flag" value="" /> 能拿到文件 file ,我们只需要将 file 转化为 图片的 url链接即可。

    这里有两种办法:1、使用 FileReader  读取出 base64图片,是一个异步操作。 2、使用 URL.createObjectURL 读取出 blob 的协议链接,是一个同步操作。这里我们采用第二种办法

    三、正式开始

    1、结构和样式

            <style type="text/css">
                * {
                    padding: 0;
                    margin: 0;
                    font-size: 14px;
                }
     
                #box {
                     400px;
                    height: 400px;
                    border: 1px solid red;
                    margin: 20px auto;
                    position: relative;
                }
     
                #operate {
                    text-align: center;
                    ;
                }
     
                #operate p {
                    margin-bottom: 6px;
                }
     
                input[type=file] {
                    display: none;
                }
     
                label {
                    display: inline-block;
                    cursor: pointer;
                    background: #38f;
                    color: #fff;
                     102px;
                    height: 38px;
                    line-height: 38px;
                    border-radius: 4px;
                }
     
            </style>
    

      

    <div id="box">
                <canvas id="canvas" width="400" height="400"></canvas>
            </div>
            <div id="operate">
                <p><label><input type="file" name="" id="bg" value="" />选择头像</label></p>
                <p><label><input type="file" name="" id="flag" value="" />选择上层</label></p>
    			<p><label id="create">直接生成</label></p>
            </div>
            <a href="" download="logo.png" title="点击下载" id="down">
                <img src="" id="result">
            </a>
        </body>
    

      

     2、js代码如下

            const canvas = document.getElementById("canvas"); //获取canvas
            const ctx = canvas.getContext("2d"); //获取画布
            const baseW = 400; //头像的最大宽高
            const flagW = 100;	//旗的最大宽高
            let bgConfig;		//画 头像的参数
            let flagConfig;		//画  旗的参数
            document.getElementById("bg").onchange = async function() {
                const file = this.files[0];
                try {
                    const img = await getImageObj(file);
                    const rate = compress(img, baseW);
                    bgConfig = [img, 0, 0, img.width, img.height, 0, 0, rate.w, rate.h];
                    drawn();
                } catch (e) {
                    console.error(e);
                }
            };
     
            document.getElementById("flag").onchange = async function() {
                const file = this.files[0];
                try {
                    const img = await getImageObj(file);
                    const rate = compress(img, flagW);
                    flagConfig = [img, 0, 0, img.width, img.height, 0, 0, rate.w, rate.h];
                    drawn();
                } catch (e) {
                    console.error(e);
                }
            };
    		//画
            function drawn() {
                ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
                if (bgConfig) {
                    ctx.drawImage(...bgConfig);
                }
                if (flagConfig) {
                    ctx.drawImage(...flagConfig);
                }
            }
     
            //图片压缩,获取等比缩放后的结果
            function compress(img, base) {
                let w = img.width;
                let h = img.height;
                if (img.width > img.height) {
                    if (img.width > base) {
                        //要将宽度缩放
                        w = base;
                        h = (w / img.width) * img.height; // 新的 宽比 高 = 旧的宽比高  h / w = img.heigth/img.width  ;
                    }
                } else {
                    if (img.height > base) {
                        h = base;
                        w = (h / img.height) * img.width;
                    }
                }
                return {
                    w,
                    h
                };
            }
    		//通过 file 获取到图片对象
            function getImageObj(file) {
                const url = getObjectURL(file);
                const img = new Image();
                img.src = url;
                return new Promise((resolve, reject) => {
                    img.onload = function() {
                        resolve(img);
                    }
                    img.onerror = function(e) {
                        reject(e);
                    }
                });
            }
     
            //取得该文件的url
            function getObjectURL(file) {
                var url = null;
                if (window.createObjectURL != undefined) {
                    url = window.createObjectURL(file);
                } else if (window.URL != undefined) {
                    url = window.URL.createObjectURL(file);
                } else if (window.webkitURL != undefined) {
                    url = window.webkitURL.createObjectURL(file);
                }
                return url;
            }
    		//直接生成
    		document.getElementById("create").onclick = function(){
    		    exportImg({
    		        x:0,
    		        y:0,
    		        w:canvas.clientWidth,
    		        h:canvas.clientHeight,
    		    });
    		}
    		//导出头像的范围
    		 function startClip(area) {
    			 var canvas = document.createElement("canvas");
    			 canvas.width = area.w;
    			 canvas.height = area.h;
    			 var data = ctx.getImageData(area.x, area.y, area.w, area.h);
    			 var context = canvas.getContext("2d");
    			 context.putImageData(data, 0, 0);
    			 return canvas.toDataURL("image/png");
    		 }
    		  //导出头像,点击可以下载
    		 function exportImg(clipArea){
    			 var url = startClip(clipArea);
    			 document.getElementById("result").src = url;
    			 document.getElementById("down").href = url;
    		 }
    

      

    其中: bgConfig 存了 头像的 logo 图  ctx.drawImage 的参数。 flagConfig 存了 旗子 参数。

    结果如下:

    很显然,这并不是我们要的效果,需要控制旗子的位置,也简单。

    调整  flagConfig 组数里面的 5 和 6 的位置元素 值,即 x 、y ,例如:

    flagConfig[5] = 100;
    flagConfig[6] = 200;
    

      

    为了方便修改。增加两个输入框:

     在js 里面增加如下代码:

    document.getElementById("posX").oninput = function() {
    		    let val = Number(this.value) || 0;
    		    flagConfig[5] = val;
    		    drawn();
    		}
    		document.getElementById("posY").oninput = function() {
    		    let val = Number(this.value) || 0;
    		    flagConfig[6] = val;
    		    drawn();
    		}
    

      

    大功告成:

    四、问题

     以为这样就完了吗?还早,,如果传一张不规则的头像,如下:

    如果遇见这样不规则的图片,最后生成的图片,会出现一片白边。

    不过,这可难不到程序员,截掉就可以啦。

    后面截取 canvas的代码就不一 贴出来了,最后附近加入完整版的吧!

    直接用鼠标截取想要的部分:

    完整版源码:

    链接:https://pan.baidu.com/s/1c4Rj6zmILpJW-x5yjGtSzg
    提取码:ol0y

       

  • 相关阅读:
    learning scala view collection
    scala
    learning scala dependency injection
    learning scala implicit class
    learning scala type alise
    learning scala PartialFunction
    learning scala Function Recursive Tail Call
    learning scala Function Composition andThen
    System.Threading.Interlocked.CompareChange使用
    System.Threading.Monitor的使用
  • 原文地址:https://www.cnblogs.com/muamaker/p/11581183.html
Copyright © 2011-2022 走看看