最近有時間了解了下html5的各API,發現新浪微博的頭像設定是使用canvas實現截圖的,加上之前段時間了解了下html5的File API使用File API 之FileReader實現文件上傳《JavaScript File API檔案上傳預覽》,更覺得html5好玩了,想著也試試看寫這功能權當學習canvas吧。
下面奉上我自己寫的一個demo,程式碼寫得比較少,很多細節不會處理。如果有不得當的地方懇請指教,謝謝啦 ^_^ ^_^
功能實現步奏:
- 一、取得文件,讀取檔案並產生url
- 二、依照容器的大小使用canvas繪製圖片
- 三、使用canvas繪製遮罩層
- 四、使用canvas繪製裁切後的圖片
-
五、拖曳裁切框,重新裁切圖片
PS:因為是先把demo寫好再寫這篇文章的,所以分段貼的程式碼是直接從程式碼裡面一段段拷的,要留意this物件喔
第一步:取得文件,讀取文件並產生url
這裡我是使用html5裡面的file api處理本地文件上傳的,因為這樣可以不需要把圖片上傳到伺服器,再由伺服器返回圖片地址來做預覽,詳細請看:使用File API 之FileReader實現文件上傳
document.getElementById('post_file').onchange = function() { var fileList = this.files[0]; var oFReader = new FileReader(); oFReader.readAsDataURL(fileList); oFReader.onload = function (oFREvent) { //当读取操作成功完成时调用. postFile.paintImage(oFREvent.target.result);//把预览图片url传给函数 }; }
第二步:依照容器的大小使用canvas繪製圖片
在上一個步驟使用File API的FileReader已經拿到了需要上傳圖片的位址了,接下來要用canvas把這個圖片繪製出來。這裡為什麼不直接插入img而用canvas重新繪製呢,這不是多此一舉了嗎?其實不然。如果用img直接插入頁面,就無法自適應居中了,如果使用canvas繪製圖片,不但能使圖片自適應居中以及能等比例縮放,並且方便把圖片的坐標,尺寸大小傳給後來的遮罩層,這樣能根據圖片的座標以及圖片的尺寸大小來繪製遮罩層。
這裡稍微要注意下canvas的drawImage方法。
paintImage: function(url) { var t = this; var createCanvas = t.getImage.getContext("2d"); var img = new Image(); img.src = url; img.onload = function(){ //等比例缩放图片(如果图片宽高都比容器小,则绘制的图片宽高 = 原图片的宽高。) //如果图片的宽度或者高度比容器大,则宽度或者高度 = 容器的宽度或者高度,另一高度或者宽度则等比例缩放 //t.imgWidth:绘制后图片的宽度;t.imgHeight:绘制后图片的高度;t.px:绘制后图片的X轴;t.py:绘制后图片的Y轴 if ( img.width < t.regional.offsetWidth && img.height < t.regional.offsetHeight) { t.imgWidth = img.width; t.imgHeight = img.height; } else { var pWidth = img.width / (img.height / t.regional.offsetHeight); var pHeight = img.height / (img.width / t.regional.offsetWidth); t.imgWidth = img.width > img.height ? t.regional.offsetWidth : pWidth; t.imgHeight = img.height > img.width ? t.regional.offsetHeight : pHeight; } //图片的坐标 t.px = (t.regional.offsetWidth - t.imgWidth) / 2 + 'px'; t.py = (t.regional.offsetHeight - t.imgHeight) / 2 + 'px'; t.getImage.height = t.imgHeight; t.getImage.width = t.imgWidth; t.getImage.style.left = t.px; t.getImage.style.top = t.py; createCanvas.drawImage(img,0,0,t.imgWidth,t.imgHeight);//没用直接插入背景图片而用canvas绘制图片,是为了调整所需框内图片的大小 t.imgUrl = t.getImage.toDataURL();//储存canvas绘制的图片地址 t.cutImage(); t.drag(); }; },
出來的效果是這樣的:
第三步:使用canvas繪製遮罩層
在上一步已經把需要裁剪的背景圖繪製出來了,現在需要根據背景圖的坐標和尺寸來繪製遮罩層覆蓋在背景上面,並且使用canvas的clearRect方法清空出一塊裁剪區域,使之與不裁剪的地方做明暗對比。
(這裡的遮罩層只是用來做顯示效果,並沒有做裁剪圖片的工作。不知道這一步能不能直接去掉?有知道的童鞋麻煩告訴下我。)
//绘制遮罩层: t.editBox.height = t.imgHeight; t.editBox.width = t.imgWidth; t.editBox.style.display = 'block'; t.editBox.style.left = t.px; t.editBox.style.top = t.py; var cover = t.editBox.getContext("2d"); cover.fillStyle = "rgba(0, 0, 0, 0.5)"; cover.fillRect (0,0, t.imgWidth, t.imgHeight); cover.clearRect(t.sx, t.sy, t.sHeight, t.sWidth);
第四步:使用canvas繪製裁切後的圖片
在第三步驟裡面,把遮罩層繪製好了,但是遮罩層並沒有裁剪的能力,僅僅是用來顯示裁剪區域與非裁剪區域的對比而已,所以這裡就開始做裁剪圖片的功能了。同樣使用到canvas的drawImage方法。
//绘制剪切图片: t.editPic.height = t.sHeight; t.editPic.width = t.sWidth; var ctx = t.editPic.getContext('2d'); var images = new Image(); images.src = t.imgUrl; images.onload = function(){ ctx.drawImage(images,t.sx, t.sy, t.sHeight, t.sWidth, 0, 0, t.sHeight, t.sWidth); //裁剪图片 document.getElementById('show_edit').getElementsByTagName('img')[0].src = t.editPic.toDataURL(); //把裁剪后的图片使用img标签显示出来 }
第五步:拖曳裁切框,重新裁切圖片
使用截圖上傳頭像功能時我們希望能裁切到滿意的圖片,所以裁切框就需要不停的變動才得以裁切出完美的圖片。前幾步已經把裁剪圖片的基本功能做出來了,所以現在需要做的就是裁剪框跟進滑鼠的移動來即時裁剪圖片。
drag: function() { var t = this; var draging = false; var startX = 0; var startY = 0; document.getElementById('cover_box').onmousemove = function(e) { //获取鼠标到背景图片的距离 var pageX = e.pageX - ( t.regional.offsetLeft + this.offsetLeft ); var pageY = e.pageY - ( t.regional.offsetTop + this.offsetTop ); //判断鼠标是否在裁剪区域里面: if ( pageX > t.sx && pageX < t.sx + t.sWidth && pageY > t.sy && pageY < t.sy + t.sHeight ) { this.style.cursor = 'move'; this.onmousedown = function(){ draging = true; //记录上一次截图的坐标 t.ex = t.sx; t.ey = t.sy; //记录鼠标按下时候的坐标 startX = e.pageX - ( t.regional.offsetLeft + this.offsetLeft ); startY = e.pageY - ( t.regional.offsetTop + this.offsetTop ); } window.onmouseup = function() { draging = false; } if (draging) { //移动时裁剪区域的坐标 = 上次记录的定位 + (当前鼠标的位置 - 按下鼠标的位置),裁剪区域不能超出遮罩层的区域; if ( t.ex + (pageX - startX) < 0 ) { t.sx = 0; } else if ( t.ex + (pageX - startX) + t.sWidth > t.imgWidth) { t.sx = t.imgWidth - t.sWidth; } else { t.sx = t.ex + (pageX - startX); }; if (t.ey + (pageY - startY) < 0) { t.sy = 0; } else if ( t.ey + (pageY - startY) + t.sHeight > t.imgHeight ) { t.sy = t.imgHeight - t.sHeight; } else { t.sy = t.ey + (pageY - startY); } t.cutImage(); } } else{ this.style.cursor = 'auto'; } }; }
大功告成,图片如下:
有童鞋指出,每移动一下鼠标就裁剪一张图片不是很耗性能吗,为什么不用background-position来做预览效果 保存的时候才用canvas裁出来?一听觉得这建议很有道理,所以就在第四步把代码稍微改动了一下。鼠标移动的时候的预览效果是改变图片的background-position,点击保存按钮的时候才裁剪图片,把裁剪下来的图片生成新的url就可以传给服务器啦~~
以下代码是改正过来的,大家有什么其它好的建议欢迎指出来喔 ^_^ ^_^
demo完整代码如下:
注意:因为用的是seajs写的,所以稍微留意下文件的加载情况啦
css:
body{text-align:center;} #label{border:1px solid #ccc;background-color:#fff;text-align:center;height:300px; width:300px;margin:20px auto;position:relative;} #get_image{position:absolute;} #edit_pic{position:absolute;display:none;background:#000;} #cover_box{position: absolute;z-index: 9999;display:none;top:0px;left:0px;} #show_edit{margin: 0 auto;display:inline-block;} #show_pic{height:100px;width:100px;border:2px solid #000;overflow:hidden;margin:0 auto;display:inline-block; }
html:
<input type="file" name="file" id="post_file"> <button id="save_button">保存</button> <div id="label"> <canvas id="get_image"></canvas> <p> <canvas id="cover_box"></canvas> <canvas id="edit_pic"></canvas> </p> </div> <p> <span id="show_edit"></span> <span id="show_pic"><img src="/static/imghwm/default1.png" data-src="../../lib/seajs/sea.js" class="lazy" alt="JavaScript+html5 canvas實作本地截圖教學_javascript技巧" ></span> </p> <script type="text/javascript"></script> <script type="text/javascript"> seajs.use(['_example/fileAPI/index_v2.js'], function(clipFile) { clipFile.init({ clipPos: { //裁剪框的默认尺寸与定位 x: 15, y: 15, height: 100, width: 100, }, }); }); </script>
js:
define(function(require, exports, module) { 'use strict'; var postFile = { init: function(options) { var t = this; t.regional = document.getElementById('label'); t.getImage = document.getElementById('get_image'); t.clipPic = document.getElementById('edit_pic'); t.coverBox = document.getElementById('cover_box'); t.achieve = document.getElementById('show_edit'); t.clipPos = options.clipPos; //初始化图片基本参数 t.bgPagePos = { x: 0, y: 0, height: 0, width: 0 }; //传进图片 document.getElementById('post_file').addEventListener("change", t.handleFiles, false); //点击保存按钮后再裁剪图片 document.getElementById('save_button').onclick = function() { //绘制剪切后的图片: t.clipPic.height = t.clipPos.height; t.clipPic.width = t.clipPos.width; var ctx = t.clipPic.getContext('2d'); var images = new Image(); images.src = t.imgUrl; images.onload = function(){ //drawImage(images,相对于裁剪图片的X, 相对于裁剪图片的Y, 裁剪的高度, 裁剪的宽度, 显示在画布的X, 显示在画布的Y, 显示在画布多高, 显示在画布多宽); ctx.drawImage(images,t.clipPos.x, t.clipPos.y, t.clipPos.height, t.clipPos.width, 0, 0, t.clipPos.height, t.clipPos.width); //裁剪图片 document.getElementById('show_pic').getElementsByTagName('img')[0].src = t.clipPic.toDataURL(); } }; t.drag(); }, handleFiles: function() { var fileList = this.files[0]; var oFReader = new FileReader(); //读取文件内容 oFReader.readAsDataURL(fileList); //当读取操作成功完成时调用. oFReader.onload = function (oFREvent) { //把预览图片URL传给函数 postFile.paintImage(oFREvent.target.result); }; }, paintImage: function(url) { var t = this; var createCanvas = t.getImage.getContext("2d"); var img = new Image(); img.src = url; //把传进来的图片进行等比例缩放 img.onload = function(){ //等比例缩放图片(如果图片宽高都比容器小,则绘制的图片宽高 = 原图片的宽高。) //如果图片的宽度或者高度比容器大,则宽度或者高度 = 容器的宽度或者高度,另一高度或者宽度则等比例缩放 //t.bgPagePos.width:绘制后图片的宽度; //t.bgPagePos.height:绘制后图片的高度; //t.bgPagePos.x:绘制后图片的X轴; //t.bgPagePos.y:绘制后图片的Y轴 if ( img.width < t.regional.offsetWidth && img.height < t.regional.offsetHeight) { t.bgPagePos.width = img.width; t.bgPagePos.height = img.height; } else { var pWidth = img.width / (img.height / t.regional.offsetHeight); var pHeight = img.height / (img.width / t.regional.offsetWidth); t.bgPagePos.width = img.width > img.height ? t.regional.offsetWidth : pWidth; t.bgPagePos.height = img.height > img.width ? t.regional.offsetHeight : pHeight; } //图片的坐标 t.bgPagePos.x = (t.regional.offsetWidth - t.bgPagePos.width) / 2 + 'px'; t.bgPagePos.y = (t.regional.offsetHeight - t.bgPagePos.height) / 2 + 'px'; t.getImage.height = t.bgPagePos.height; t.getImage.width = t.bgPagePos.width; t.getImage.style.left = t.bgPagePos.x; t.getImage.style.top = t.bgPagePos.y; createCanvas.drawImage(img,0,0,t.bgPagePos.width,t.bgPagePos.height);//没用直接插入背景图片而用canvas绘制图片,是为了调整所需框内图片的大小 t.imgUrl = t.getImage.toDataURL();//储存canvas绘制的图片地址 t.clipImg(); }; }, clipImg: function() { var t = this; //绘制遮罩层: t.coverBox.height = t.bgPagePos.height; t.coverBox.width = t.bgPagePos.width; t.coverBox.style.display = 'block'; t.coverBox.style.left = t.bgPagePos.x; t.coverBox.style.top = t.bgPagePos.y; var cover = t.coverBox.getContext("2d"); cover.fillStyle = "rgba(0, 0, 0, 0.5)"; cover.fillRect (0,0, t.bgPagePos.width, t.bgPagePos.height); cover.clearRect(t.clipPos.x, t.clipPos.y, t.clipPos.height, t.clipPos.width); t.achieve.style.background = 'url(' + t.imgUrl + ')' + -t.clipPos.x + 'px ' + -t.clipPos.y + 'px no-repeat'; t.achieve.style.height = t.clipPos.height + 'px'; t.achieve.style.width = t.clipPos.width + 'px'; }, drag: function() { var t = this; var draging = false; var _startPos = null; t.coverBox.onmousemove = function(e) { e = e || window.event; if ( e.pageX == null && e.clientX != null ) { var doc = document.documentElement, body = document.body; e.pageX = e.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); e.pageY = e.clientY + (doc && doc.scrollTop || body && body.scrollTop); } //获取鼠标到背景图片的距离 var _mousePos = { left: e.pageX - ( t.regional.offsetLeft + this.offsetLeft ), top: e.pageY - ( t.regional.offsetTop + this.offsetTop ) } //判断鼠标是否在裁剪区域里面: if ( _mousePos.left > t.clipPos.x && _mousePos.left < t.clipPos.x + t.clipPos.width && _mousePos.top > t.clipPos.y && _mousePos.top < t.clipPos.y + t.clipPos.height ) { this.style.cursor = 'move'; this.onmousedown = function(){ draging = true; //记录上一次截图的坐标 t.ex = t.clipPos.x; t.ey = t.clipPos.y; //记录鼠标按下时候的坐标 _startPos = { left: e.pageX - ( t.regional.offsetLeft + this.offsetLeft ), top: e.pageY - ( t.regional.offsetTop + this.offsetTop ) } } if (draging) { //移动时裁剪区域的坐标 = 上次记录的定位 + (当前鼠标的位置 - 按下鼠标的位置),裁剪区域不能超出遮罩层的区域; if ( t.ex + ( _mousePos.left - _startPos.left ) < 0 ) { t.clipPos.x = 0; } else if ( t.ex + ( _mousePos.left - _startPos.left ) + t.clipPos.width > t.bgPagePos.width ) { t.clipPos.x = t.bgPagePos.width - t.clipPos.width; } else { t.clipPos.x = t.ex + ( _mousePos.left - _startPos.left ); }; if (t.ey + ( _mousePos.top - _startPos.top ) < 0) { t.clipPos.y = 0; } else if ( t.ey + ( _mousePos.top - _startPos.top ) + t.clipPos.height > t.bgPagePos.height ) { t.clipPos.y = t.bgPagePos.height - t.clipPos.height; } else { t.clipPos.y = t.ey + ( _mousePos.top - _startPos.top ); } t.clipImg(); } document.body.onmouseup = function() { draging = false; document.onmousemove = null; document.onmouseup = null; } } else{ this.style.cursor = 'auto'; } }; } } return postFile; });
以上就是本文的全部内容,希望对大家的学习有所帮助。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...

在Tampermonkey中如何對多個鏈接進行並發GET請求並依次判斷返回結果?在Tampermonkey腳本中,我們經常需要對多個鏈...


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

禪工作室 13.0.1
強大的PHP整合開發環境

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中