國(guó)慶節(jié)到了,利用JS實(shí)現(xiàn)一個(gè)生成國(guó)慶風(fēng)頭像的小工具 詳解實(shí)現(xiàn)過程
這里用到的技術(shù):
- HTML+ CSS+ JavaScript;
- download.js庫(kù);
- fabric.js庫(kù);
先上體驗(yàn)鏈接:g.cuggz.com/ 。
注:可以點(diǎn)擊上方的連接進(jìn)行使用,不過我的域名被TX屏蔽了,還在申訴中,所以無(wú)法在QQ、微信中打開,需要復(fù)制鏈接到瀏覽器進(jìn)行查看、使用。
下面是這個(gè)小工具的截圖:

1. 頁(yè)面布局
這部分不多說(shuō),直接上代碼:
<div class="wrapper">
<!-- 選擇框 -->
<div class="main-box">
<a class="prev" onClick='changeHat()'></a>
<div class="main-img">
<div id="content">
<canvas id='cvs'></canvas>
</div>
</div>
<a class="next" onClick='changeHat()'></a>
</div>
<!-- 導(dǎo)出圖 -->
<img id='export-img' alt='國(guó)慶專屬頭像' src='' crossorigin="anonymous"/>
<!-- 操作按鈕 -->
<div class="operation-btns">
<a class="upload-btn">
<input id='upload' type='file' onchange='viewer()' style='opacity: 0;'/>
</a>
<a class="export-btn" onClick='exportFunc()'></a>
</div>
</div>
<!-- 模板 -->
<div style='display: none'>
<img id='img' src='' alt='' />
<img class='hide' id='hat0' src='img/1.png' />
<img class='hide' id='hat1' src='img/2.png' />
<img class='hide' id='hat2' src='img/3.png' />
<img class='hide' id='hat3' src='img/4.png' />
<img class='hide' id='hat4' src='img/5.png' />
<img class='hide' id='hat5' src='img/6.png' />
<img class='hide' id='hat6' src='img/7.png' />
</div>
這個(gè)頁(yè)面比較簡(jiǎn)單,外面就是一個(gè)大的背景圖,中間就是一個(gè)頭像的展示框以及模板的切換按鈕,下面就是一個(gè)上傳按鈕和一個(gè)下載按鈕。頁(yè)面布局完之后,就是寫樣式了,CSS代碼如下:
body,
html {
min-height: 100%;
width: 100%;
user-select: none;
font-size: 18px;
}
.wrapper {
width: 100%;
height: 100%;
max-width: 620px;
max-height: 800px ;
margin: 0 auto;
background-image: url('../img/bg.png');
background-repeat: no-repeat;
background-size: 100% 100%;
padding-top: 13em;
}
#export-img {
display:none;
margin:0 auto;
width:250px;
height:250px;
}
.main-box {
display: flex;
align-items: center;
justify-content: center;
}
.main-box .next,
.main-box .prev {
background-image: url('../img/next.png');
background-size: contain;
border-radius: 50%;
width: 2.275rem;
height: 2.275rem
}
.main-box .prev {
transform: rotate(180deg)
}
.main-box .main-img {
margin: 0 .75rem;
background: #fff;
border: .25rem solid #fbe6b5;
border-radius: .75rem;
font-size: 0
}
#content {
border-radius: .5rem;
position: relative;
width: 9.5rem;
height: 9.5rem;
margin-left: 50%;
transform: translateX(-50%);
overflow: hidden
}
.operation-btns {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-top: .75rem
}
.operation-btns .upload-btn {
width: 11.6rem;
height: 3.6rem;
background-size: 100% 100%;
background-image: url('../img/upload.png')
}
.operation-btns .export-btn {
display: none;
width: 11.6rem;
height: 3.75rem;
background-size: 100% 100%;
background-image: url('../img/export.png')
}
這里只是簡(jiǎn)單的實(shí)現(xiàn),僅供參考。還有很多可以優(yōu)化的地方,這里不在修改,有興趣的可以自己進(jìn)行個(gè)性化定制。
2. 圖片上傳和展示
接下來(lái)就是邏輯部分的實(shí)現(xiàn)了。首先,有幾個(gè)全局變量需要先定義,后面會(huì)用到:
let canvasFabric; // 畫布實(shí)例
let hat = "hat0"; // 當(dāng)前的模板class
let hatInstance; // 模板圖層實(shí)例
const screenWidth = document.getElementById("content").scrollHeight; // 內(nèi)容框的高度
之后就需要處理上傳的圖片并將他展示在頁(yè)面上:
function viewer() {
// 獲取上傳的圖片文件
const file = document.getElementById("upload").files[0];
// 獲取需要展示圖片的標(biāo)簽
const img = document.getElementById("img");
// 創(chuàng)建文件讀取文件對(duì)象
const reader = new FileReader();
if (file) {
// 將文件轉(zhuǎn)化為Base64
reader.readAsDataURL(file);
// 當(dāng)文件讀取成功之后觸發(fā)
reader.onload = () => {
// 將base64的url賦值給要展示圖片的標(biāo)簽
img.src = reader.result;
// 圖片加載完成觸發(fā)
img.onload = () => img2Cvs(img);
}
} else {
img.src = ""
}
}
這里使用到了HTML5的FileReader對(duì)象,它提供了讀取文件的方法和包含讀取結(jié)果的事件模型??梢允褂胣ew來(lái)初始化對(duì)象,FileReader對(duì)象包含四個(gè)方法,其中 3 個(gè)用以讀取文件,另一個(gè)用來(lái)中斷讀取。需要注意的是 ,無(wú)論讀取成功或失敗,方法并不會(huì)返回讀取結(jié)果,這一結(jié)果存儲(chǔ)在 result 屬性中。這里用到了readAsDataURL方法,MDN對(duì)該方法的介紹如下:
readAsDataURL方法會(huì)讀取指定的 Blob 或 File 對(duì)象。讀取操作完成的時(shí)候,readyState會(huì)變成已完成DONE,并觸發(fā)loadend (en-US)事件,同時(shí) result 屬性將包含一個(gè)data:URL格式的字符串(base64編碼)以表示所讀取文件的內(nèi)容。
也就是說(shuō)將上傳的圖片轉(zhuǎn)化為了一個(gè)Base64格式的URL,并賦值給了展示圖片的標(biāo)簽,這樣這個(gè)標(biāo)簽就顯示出現(xiàn)這個(gè)頭像了,效果如下:

這樣就完成了圖片的上傳和展示,接下來(lái)就該初始化一個(gè)畫布了。
3. 初始化畫布
在上面的代碼的最后執(zhí)行了 img.load,這里的 onload 事件會(huì)在圖片加載完成后立即執(zhí)行。在圖片展示完成之后會(huì)執(zhí)行img2Cvs方法,這個(gè)方法主要用來(lái)初始化一塊畫布,順便展示和隱藏頁(yè)面的部分元素。
img2Cvs方法中使用到了 fabric庫(kù),F(xiàn)abric.js是一個(gè)可以簡(jiǎn)化Canvas程序編寫的庫(kù)。 Fabric.js為Canvas提供所缺少的對(duì)象模型, svg parser, 交互和一整套其他不可或缺的工具。Canvas提供一個(gè)好的畫布能力, 但是Api不夠友好。Fabric.js就是為此而開發(fā),它主要就是用對(duì)象的方式去編寫代碼。Fabric.js能做的事情如下:
- 在
Canvas上創(chuàng)建、填充圖形(包括圖片、文字、規(guī)則圖形和復(fù)雜路徑組成圖形)。 - 給圖形填充漸變顏色。
- 組合圖形(包括組合圖形、圖形文字、圖片等)。
- 設(shè)置圖形動(dòng)畫集用戶交互。
- 生成JSON, SVG數(shù)據(jù)等。
- 生成Canvas對(duì)象自帶拖拉拽功能。
可以通過npm命令來(lái)安裝fabric.js庫(kù):
npm install fabric --save
也可以通過cdn來(lái)引用:
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>
下面就來(lái)看看img2Cvs方法是如何實(shí)現(xiàn)的:
function img2Cvs(img) {
// 獲取并展示canvas畫布,并將畫布的大小設(shè)置為圖片的大小
const cvs = document.getElementById("cvs");
cvs.width = img.width;
cvs.height = img.height;
cvs.style.display = "block";
// 創(chuàng)建一個(gè)畫布,并設(shè)置其位置和背景圖
canvasFabric = new fabric.Canvas("cvs", {
width: screenWidth,
height: screenWidth,
backgroundImage: new fabric.Image(img, {
scaleX: screenWidth / img.width,
scaleY: screenWidth / img.height
})
});
// 切換模板
changeHat();
// 隱藏上傳圖片按鈕,展示下載圖片按鈕
document.getElementsByClassName("upload-btn")[0].style.display = "none";
document.getElementsByClassName("export-btn")[0].style.display = "block";
}
這里面的fabric.Canvas()方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是canvas畫布的id,第二個(gè)參數(shù)是初始化畫布時(shí)的配置項(xiàng),這里我們?cè)O(shè)置了初始的畫布的大小以及背景圖,使用我們上傳的頭像作為背景圖。這里的背景圖是實(shí)例化的fabric.Image對(duì)象,該對(duì)象初始化時(shí)的第一個(gè)參數(shù)是圖片對(duì)象,第二個(gè)參數(shù)是圖片的樣式設(shè)置配置對(duì)象。
當(dāng)創(chuàng)建好畫布之后,就需要切換出第一個(gè)模板,并將上傳圖片按鈕隱藏,以及將下載頭像按鈕顯示出來(lái)。這樣就完成了第一步的工作。下面就在來(lái)看看如何切換已有的模板。
4. 切換模板
接下來(lái)就來(lái)看看切換模板是如何實(shí)現(xiàn)的:
function changeHat() {
// 隱藏當(dāng)前的模板
document.getElementById(hat).style.display = "none";
// 獲取所有的模板
const hats = document.getElementsByClassName("hide");
hat = "hat" + (+hat.replace("hat", "") + 1) % hats.length;
// 獲取當(dāng)前的模板并展示出來(lái)
const hatImage = document.getElementById(hat);
hatImage.style.display = "block";
// 如果當(dāng)前存在圖層,就將其移除
if (hatInstance) {
canvasFabric.remove(hatInstance)
}
// 將當(dāng)前的模板添加為圖層對(duì)象
hatInstance = new fabric.Image(hatImage, {
top: 0,
left: 0,
scaleX: screenWidth / hatImage.width,
scaleY: screenWidth / hatImage.height,
cornerColor: "#0b3a42",
cornerStrokeColor: "#fff",
cornerStyle: "circle",
transparentCorners: false,
rotatingPointOffset: 30
});
// 將圖層對(duì)象設(shè)置為不可拉伸
hatInstance.setControlVisible({
mt: false,
mb: false,
ml: false,
mr: false,
bl: false,
br: false,
tl: false,
tr: false,
mtr: false,
})
// 將圖層添加到畫布中
canvasFabric.add(hatInstance);
}
在默認(rèn)情況下,fabric.js元素帶有八個(gè)點(diǎn)來(lái)縮放任何對(duì)象,這里我們是不希望用戶在水平或垂直方向?qū)?code>fabric對(duì)象進(jìn)行拉伸的,可以通過setControlsVisibility()方法來(lái)設(shè)置其不可拉伸,該方法需要傳入一個(gè)配置對(duì)象,該對(duì)象包含八個(gè)縮放點(diǎn),都設(shè)置為false即可。
最后我們將使用模板生成的圖層添加到了畫布中,這里使用到了add方法,這是fabric提供的事件,以下為fabric.js官方提供的常用事件:
object:added添加圖層object:modified編輯圖層object:removed移除圖層selection:created初次選中圖層selection:updated圖層選擇變化selection:cleared清空?qǐng)D層選中
5. 輸出圖片
經(jīng)過上面的步驟,我們就初始化了一個(gè)畫布,畫布的背景是我們上傳的圖片,畫布上還有一個(gè)圖層,這個(gè)圖層是我們自己選擇的模板。最后一步就是輸出合成之后的圖片了。下面來(lái)看看點(diǎn)擊下載圖片按鈕之后會(huì)執(zhí)行哪些操作:
function exportFunc() {
// 隱藏選擇框、上傳下載按鈕、canvas畫布
document.getElementsByClassName("main-box")[0].style.display = "none";
document.getElementsByClassName("operation-btns")[0].style.display = "none";
document.getElementById("cvs").style.display = "none";
// 將畫布生成URL,并賦值給對(duì)應(yīng)標(biāo)簽進(jìn)行展示
const exportImage = document.getElementById("export-img");
exportImage.style.display = "block";
exportImage.src = canvasFabric.toDataURL({
width: screenWidth,
height: screenWidth
});
// 下載生成的圖片
window.confirm("是否下載頭像") ? download(exportImage.src, "國(guó)慶風(fēng)頭像", 'image/png') : void 0
}
這里我們使用toDataURL方法來(lái)將畫布實(shí)例對(duì)象生成圖片,這是fabric對(duì)象的方法,可以將畫布導(dǎo)出為圖片,導(dǎo)出的一個(gè)Base64格式的URL。這樣img標(biāo)簽獲取到這個(gè)URL之后就能顯示出來(lái)最終的圖片了。
最后,還添加了一個(gè)可有可無(wú)的功能,就是下載生成的頭像,這里使用到了download.js庫(kù),該方法的第一個(gè)參數(shù)是圖片的URL,第二個(gè)參數(shù)是下載圖片的名稱,第三個(gè)參數(shù)是圖片的格式。
這就是這個(gè)小應(yīng)用的所有功能了,只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),還存在一個(gè)BUG,主要提供一個(gè)實(shí)現(xiàn)的思路。我之前是沒有接觸過canvas以及畫布的概念的,這次漲知識(shí)了。以后有時(shí)間多學(xué)習(xí)了一下相關(guān)的使用,interesting!
到此這篇關(guān)于國(guó)慶節(jié)到了,利用JS實(shí)現(xiàn)一個(gè)生成國(guó)慶風(fēng)頭像的小工具 詳解實(shí)現(xiàn)過程的文章就介紹到這了,更多相關(guān)利用JS實(shí)現(xiàn)一個(gè)生成國(guó)慶風(fēng)頭像內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序 網(wǎng)絡(luò)API發(fā)起請(qǐng)求詳解
這篇文章主要介紹了微信小程序 網(wǎng)絡(luò)API發(fā)起請(qǐng)求詳解的相關(guān)資料,需要的朋友可以參考下2016-11-11
一款功能強(qiáng)大的markdown編輯器tui.editor使用示例詳解
這篇文章主要為大家介紹了一款功能強(qiáng)大的markdown編輯器tui.editor使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
TypeScript對(duì)象解構(gòu)操作符在Spartacus實(shí)際項(xiàng)目開發(fā)中的應(yīng)用解析
這篇文章主要為大家介紹了TypeScript對(duì)象解構(gòu)操作符在Spartacus實(shí)際項(xiàng)目開發(fā)中的應(yīng)用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Dom-api MutationObserver使用方法詳解
這篇文章主要為大家介紹了Dom-api MutationObserver使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
如果文字過長(zhǎng),則將過長(zhǎng)的部分變成省略號(hào)顯示
如果文字過長(zhǎng),則將過長(zhǎng)的部分變成省略號(hào)顯示...2006-06-06

