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

       

  • 相关阅读:
    php实现求链表中倒数第k个节点
    在python正在使用mysql
    1002. 写这个号码 (20)(数学啊 ZJU_PAT)
    Lua 环境结构 --Linux
    Java程序猿JavaScript学习笔记(4——关闭/getter/setter)
    C/C++数据对齐汇总
    多线程
    11gRAC CHM 管理
    hdu 4059 The Boss on Mars(纳入和排除)
    模板方法模式分析、图表和基本代码
  • 原文地址:https://www.cnblogs.com/muamaker/p/11581183.html
Copyright © 2011-2022 走看看