國慶就要來了!今年是新中國成立70週年,你們的熱情都很高漲。這不,我今天一翻朋友圈,被齊刷刷地帶國旗的頭像給刷屏了!javascript
不過還有些朋友不明就裏,還在不停地@微信官方,但願能自動給頭像加上一面國旗。然而等了半天,仍是毫無反應……css
固然,程序員朋友,也開始了一頓操做。前端
python 用 python-opencv 庫,直接調。java
go 用 image 庫,直接調。python
那麼前端呢? 我們前端不用庫,原生的 js + canvas 簡單快捷。程序員
1、思路分析web
大體有兩種辦法能夠實現。canvas
這裏我採用第二種辦法,簡單點。。api
2、技術點微信
一、 獲取畫布:
const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d");
二、將圖片畫入canvas
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
三、導出圖片
canvas.toDataURL("image/png");
四、有了這些方法,咱們的圖片從哪裏來?
經過 <input type="file" name="" id="flag" value="" /> 能拿到文件 file ,咱們只須要將 file 轉化爲 圖片的 url連接便可。
這裏有兩種辦法:一、使用 FileReader
讀取出 base64圖片,是一個異步操做。 二、使用 URL.createObjectURL 讀取出 blob 的協議連接,是一個同步操做。這裏咱們採用第二種辦法
3、正式開始
一、結構和樣式
<style type="text/css"> * { padding: 0; margin: 0; font-size: 14px; } #box { width: 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; width: 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>
二、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(); }
大功告成:
4、問題
覺得這樣就完了嗎?還早,,若是傳一張不規則的頭像,以下:
若是碰見這樣不規則的圖片,最後生成的圖片,會出現一片白邊。
不過,這可難不到程序員,截掉就能夠啦。
後面截取 canvas的代碼就不一 貼出來了,最後附近加入完整版的吧!
直接用鼠標截取想要的部分:
完整版源碼:
連接:https://pan.baidu.com/s/1c4Rj6zmILpJW-x5yjGtSzg 提取碼:ol0y