JS+Canvas實現(xiàn)自定義頭像功能
寫在最前
前兩天老大跟我說老虎官網(wǎng)上那個自定義頭像的功能是flash實現(xiàn)的,沒有安裝過的還得手動去“允許”falsh的運行。所以讓我用canvas實現(xiàn)一個一樣的功能,嘿嘿,剛好最近也在研究canvas,所以欣然答應(其實,你沒研究過難道就不答應么,哈哈哈哈哈~)
成果展示
Git地址
https://github.com/ry928330/portraitDIY.git
功能說明
拖拽左側(cè)小方框,或者是鼠標放在小方框右下角,點擊拉伸方框,方框覆蓋部分的圖片被自動截取下來,然后再在右側(cè)的多個容器里面重繪。
輸入寬高,自定義你需要訂制的頭像大小,目前只支持寬高相同的頭像圖片。
實現(xiàn)細節(jié)
因為你要對圖片所在的區(qū)域進行截圖,所以你得制作一張canvas,蓋在圖片所在的區(qū)域。這里,我們給出了一個函數(shù),根據(jù)傳入的DOM里面元素的類名創(chuàng)建相同位置的canvas,蓋在原來的DOM元素上面:
function createCanvasByClassName(tag) { var canvasInitialWidth = $('.' + tag).width(); var canvasInitialHeight = $('.' + tag).height(); var left = $('.' + tag).offset().left - $('.' + tag).parent('.portraitContainer').offset().left + 1; var top = $('.' + tag).offset().top - $('.' + tag).parent('.portraitContainer').offset().top + 1; //var left = $('.' + tag).offset().left + 1; //var top = $('.' + tag).offset().top + 1; clearCanvasObj.left = $('.' + tag).offset().left + 1; clearCanvasObj.top = $('.' + tag).offset().top + 1; // clearCanvasObj.left = left; // clearCanvasObj.top = top; var canvasElement = $('<canvas></canvas>'); var randomNum = Math.floor(getRandom(0, 10000)); clearCanvasObj.canvasId = randomNum; canvasElement.attr({ id: 'canvas', width: canvasInitialWidth, height: canvasInitialHeight }); canvasElement.css({ position: 'absolute', top: top, left: left }); //$('body').append(canvasElement); var appendEle = $('.portraitContainer').append(canvasElement); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); //ctx.fillStyle = "rgba(211,211,216,0.5)"; ctx.clearRect(0, 0, canvasInitialWidth, canvasInitialHeight); ctx.fillStyle = "rgba(0,0,0, 0.4)"; ctx.fillRect(0, 0, canvasInitialWidth, canvasInitialHeight); return canvas; }
有了這張canvas你就可以在你圖片所在區(qū)域肆意的操作了。首先,降整個區(qū)域畫上一個淺黑色的陰影,然后再擦除初始小方框區(qū)域里面的顏色。然后給整個頁面添加mousedown,mousemove,mouseup事件,他們所做的功能就跟你在頁面中實現(xiàn)一個拖拽的功能類似,這里重點說下mousemove里面做的操作,代碼如下:
function mousemoveFunc(event) { /* Act on the event */ var nowMouseX = event.clientX - clearCanvasObj.left; var nowMouseY = event.clientY - clearCanvasObj.top; if (nowMouseX >= clearCanvasObj.xStart && nowMouseX <= clearCanvasObj.xStart + clearCanvasObj.width && nowMouseY >= clearCanvasObj.yStart && nowMouseY <= clearCanvasObj.yStart + clearCanvasObj.height) { clearCanvasObj.isCanvasArea = true; //clearCanvasObj.isRightCorner = false; imgContainerCanvas.style.cursor = 'move'; } else if ((nowMouseX >= clearCanvasObj.xStart + clearCanvasObj.width - 10) && (nowMouseX <= clearCanvasObj.xStart+ clearCanvasObj.width + 10) && (nowMouseY >= clearCanvasObj.yStart + clearCanvasObj.height - 10) && (nowMouseY <= clearCanvasObj.yStart + clearCanvasObj.height + 10)) { clearCanvasObj.isCanvasArea = true; //clearCanvasObj.beginDraw = false; imgContainerCanvas.style.cursor = 'se-resize'; } else { clearCanvasObj.isCanvasArea = false; //clearCanvasObj.isRightCorner = false; imgContainerCanvas.style.cursor = 'default'; } var outerDomWidth = $(".imgContainer").width(); var outerDomHeight = $(".imgContainer").height(); var xDistance = event.clientX - clearCanvasObj.mouseX; var yDistance = event.clientY - clearCanvasObj.mouseY; //var outerCTX = canvas.getContext('2d'); //移動小方框 if (clearCanvasObj.beginDraw && clearCanvasObj.isCanvasArea && !clearCanvasObj.isRightCorner) { ry_CTX.fillStyle = clearCanvasObj.color; // console.log('1', clearCanvasObj.xStart, clearCanvasObj.yStart) ry_CTX.fillRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height); //outerCTX.fillRect(0, 0, canvas.width, canvas.height); clearCanvasObj.xStart += xDistance; clearCanvasObj.yStart += yDistance; //判斷方框是否達到邊界 if (clearCanvasObj.xStart <= 0) { clearCanvasObj.xStart = 0; } if (clearCanvasObj.yStart <= 0) { clearCanvasObj.yStart = 0; } if ((clearCanvasObj.xStart + clearCanvasObj.width) >= outerDomWidth) { clearCanvasObj.xStart = outerDomWidth - clearCanvasObj.width; } if ((clearCanvasObj.yStart + clearCanvasObj.height) >= outerDomHeight) { clearCanvasObj.yStart = outerDomHeight - clearCanvasObj.height; } // console.log('2', clearCanvasObj.xStart, clearCanvasObj.yStart) ry_CTX.clearRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height); produceSmallPic(clearCanvasObj.xStart+clearCanvasObj.left, clearCanvasObj.yStart+clearCanvasObj.top, clearCanvasObj.width, clearCanvasObj.height, imageURL) clearCanvasObj.mouseX = event.clientX; clearCanvasObj.mouseY = event.clientY; } //拖拽小方框 if (clearCanvasObj.isRightCorner) { ry_CTX.fillStyle = clearCanvasObj.color; ry_CTX.fillRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height); var realDistance = Math.min(xDistance, yDistance) clearCanvasObj.width += realDistance; clearCanvasObj.height += realDistance; //拖動時邊界條件的判斷 if (clearCanvasObj.xStart + clearCanvasObj.width >= outerDomWidth) { clearCanvasObj.width = outerDomWidth - clearCanvasObj.xStart; clearCanvasObj.height = outerDomWidth - clearCanvasObj.xStart; } if (clearCanvasObj.yStart + clearCanvasObj.height >= outerDomHeight) { clearCanvasObj.width = outerDomHeight - clearCanvasObj.yStart; clearCanvasObj.height = outerDomHeight - clearCanvasObj.yStart; } if (clearCanvasObj.width <= 10) { clearCanvasObj.width = 10; } if (clearCanvasObj.height <= 10) { clearCanvasObj.height = 10; } ry_CTX.clearRect(clearCanvasObj.xStart, clearCanvasObj.yStart, clearCanvasObj.width, clearCanvasObj.height); produceSmallPic(clearCanvasObj.xStart+clearCanvasObj.left, clearCanvasObj.yStart+clearCanvasObj.top, clearCanvasObj.width, clearCanvasObj.height, imageURL); clearCanvasObj.mouseX = event.clientX; clearCanvasObj.mouseY = event.clientY; } }
函數(shù)里面,你需要注意拖拽的邊界條件,一個是方框不能拖到圖片所在DOM外的邊界;另外一個就是當你鼠標放在小方框所在的區(qū)域改變鼠標的樣式。方框在拖動的過程中,我們不斷重繪方框移動的區(qū)域(也就是不斷的畫上陰影),然后在新的位置調(diào)用clearRect函數(shù),重新擦出一個小方框出來。在拖拽或是拉伸的過程中,我們會不斷調(diào)用produceSmallPic函數(shù),在右邊的容器(每個容器都是一個canvas)里面不斷根據(jù)容器大小重繪出所需的頭像。代碼如下:
function produceSmallPic(imageURL,left, top, width, height) { var img = new Image(); img.src = imageURL; var targetCtx = new Array(); var targetCanvas = null; img.onload = function() { portraitGroupsArr.forEach(function(item, index) { targetCanvas = document.getElementById(item.class); targetCtx.push(targetCanvas.getContext('2d')); targetCtx[index].clearRect(0,0, item.width, item.height); targetCtx[index].drawImage(img, left - clearCanvasObj.left, top - clearCanvasObj.top, width, height, 0, 0 , item.width, item.height); }) } }
我們說下這個函數(shù)的作用,這里我們要注意一個參數(shù)imageURL,這個URL是由圖片所在的DOM轉(zhuǎn)化來的。因為你要把DOM所在的區(qū)域變成一張圖片,這樣你才能在利用drawImage函數(shù)截取你所需要的區(qū)域。所以我們先利用html2canvas庫函數(shù)講圖片所在的DOM轉(zhuǎn)化為canvas,這張canvas的內(nèi)容是包含你所要截取的圖片的,然后把這張canvas轉(zhuǎn)化為圖片取得圖片地址imageURL,代碼如下:
html2canvas(document.getElementById('imgContainer'), { onrendered: function(canvas) { var imageURL = canvasTransToImage(canavs); ... } }) function canvasTransToImage(canvas) { var imageURL = canvas.toDataURL('image/png'); return imageURL; }
接著,你就可以便利右側(cè)的canvas容器,講圖片重回到里面了,整個過程就這樣結(jié)束,回頭看來是不是很簡單。
相關依賴
<script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.min.js"></script>
寫在最后
canvas的操作,要多多注意那些邊界條件,什么時候該重繪什么時候該清除,這些是比較重要的。邏輯清晰了,canvas本身的API也就那么幾個,操作起來也就沒那么麻煩了
到此這篇關于JS+Canvas實現(xiàn)自定義頭像功能的文章就介紹到這了,更多相關JS Canvas自定義頭像內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript之json_動力節(jié)點Java學院整理
這篇文章主要介紹了JavaScript之json,JSON它是一種數(shù)據(jù)交換格式。有興趣的可以了解一下2017-06-06layui實現(xiàn)根據(jù)table數(shù)據(jù)判斷按鈕顯示情況的方法
今天小編就為大家分享一篇layui實現(xiàn)根據(jù)table數(shù)據(jù)判斷按鈕顯示情況的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09Javascript中八種遍歷方法的執(zhí)行速度深度對比
關于數(shù)組或?qū)ο蟊闅v,相信很多人都沒有深入觀察過執(zhí)行效率。這是一個曾在群里吵翻天的話題,讀懂后你將成為遍歷效率話題的大師。下面這篇文章主要介紹了Javascript中八種遍歷方法執(zhí)行速度深度對比的相關資料,需要的朋友可以參考下。2017-04-04js實現(xiàn)類bootstrap模態(tài)框動畫
這篇文章主要為大家詳細介紹了js實現(xiàn)類bootstrap模態(tài)框動畫的相關資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02js獲取html參數(shù)及向swf傳遞參數(shù)應用介紹
HTML頁面是在客戶端執(zhí)行的,這樣要獲取參數(shù)必須使用客戶端腳本如JavaScript,在這點上與服務器端腳本獲取參數(shù)方式有所不同接下來將詳細介紹下感興趣的你可不要錯過了哈2013-02-02JavaScript在網(wǎng)頁中畫圓的函數(shù)arc使用方法
這篇文章主要介紹了JavaScript在網(wǎng)頁中畫圓的函數(shù)arc使用方法的相關資料,需要的朋友可以參考下2015-11-11javascript實現(xiàn)多級聯(lián)動下拉菜單的方法
這篇文章主要介紹了javascript實現(xiàn)多級聯(lián)動下拉菜單的方法,通過javascript自定義函數(shù)實現(xiàn)對多級聯(lián)動下拉菜單的操作,是非常實用的技巧,需要的朋友可以參考下2015-02-02