Ich hatte kürzlich Zeit, mich mit den verschiedenen APIs von HTML5 vertraut zu machen, und stellte fest, dass die Avatar-Einstellung von Sina Weibo Canvas verwendet, um Screenshots zu erstellen. Außerdem habe ich vor einiger Zeit etwas über die Datei-API von HTML5 erfahren und die Datei-APIs verwendet FileReader zum Hochladen von Dateien 《JavaScript File API File Upload Preview》, Ich denke, dass HTML5 noch mehr Spaß macht, und ich möchte versuchen, diese Funktion zu schreiben und Canvas zu lernen.
Unten ist eine Demo, die ich selbst geschrieben habe. Der Code ist relativ klein und ich weiß nicht, wie ich mit vielen Details umgehen soll. Wenn etwas unangemessen ist, sagen Sie es mir bitte, danke ^_^ ^_^
Schritte zur Funktionsimplementierung:
PS: Da ich die Demo zuerst geschrieben habe, bevor ich diesen Artikel geschrieben habe, wird der in Abschnitten veröffentlichte Code Abschnitt für Abschnitt direkt aus dem Code kopiert. Bitte achten Sie auf dieses Objekt
Schritt 1: Holen Sie sich die Datei, lesen Sie die Datei und generieren Sie die URL
Hier verwende ich die Datei-API in HTML5, um den lokalen Datei-Upload abzuwickeln, da dadurch das Hochladen des Bildes auf den Server entfällt und der Server dann die Bildadresse zur Vorschau zurückgibt. Weitere Informationen finden Sie unter: Verwenden des FileReader der Datei-API Datei-Upload implementieren
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传给函数 }; }
Schritt 2: Verwenden Sie die Leinwand, um Bilder entsprechend der Größe des Behälters zu zeichnen
Im vorherigen Schritt hat der FileReader mithilfe der Datei-API die Adresse des hochzuladenden Bilds erhalten. Als Nächstes müssen Sie Canvas verwenden, um das Bild zu zeichnen. Warum das Bild nicht direkt hier einfügen und mit Canvas neu zeichnen? Ist das nicht unnötig? Nicht wirklich. Wenn Sie „img“ verwenden, um es direkt in die Seite einzufügen, wird es nicht adaptiv zentriert. Wenn Sie Canvas zum Zeichnen des Bildes verwenden, ermöglicht dies nicht nur die adaptive Zentrierung und Skalierung des Bildes, sondern erleichtert auch die Anpassung Übergeben Sie die Koordinaten und die Größe des Bildes an die nachfolgende Maskenebene. Dadurch kann die Maskenebene basierend auf den Koordinaten des Bildes und der Größe des Bildes gezeichnet werden.
Hier müssen Sie der drawImage-Methode von Canvas ein wenig Aufmerksamkeit schenken.
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(); }; },
Das Ergebnis ist wie folgt:
Schritt 3: Zeichnen Sie die Maskenebene mit der Leinwand
Im vorherigen Schritt haben Sie das zuzuschneidende Hintergrundbild gezeichnet. Jetzt müssen Sie eine Maskenebene basierend auf den Koordinaten und der Größe des Hintergrundbilds zeichnen, um den Hintergrund abzudecken, und die Methode „clearRect“ von Canvas verwenden um einen Zuschneidebereich zu löschen, um ihn mit den nicht zugeschnittenen Bereichen zu kontrastieren.
(Die Maskenebene wird hier nur für Anzeigeeffekte verwendet und übernimmt nicht das Zuschneiden des Bildes. Ich frage mich, ob dieser Schritt direkt entfernt werden kann? Wenn es jemand weiß, sagen Sie es mir bitte.)
//绘制遮罩层: 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);
Schritt 4: Zeichnen Sie das zugeschnittene Bild auf der Leinwand
Im dritten Schritt wird die Maskenebene gezeichnet, die Maskenebene verfügt jedoch nicht über die Möglichkeit zum Zuschneiden. Sie wird nur zum Anzeigen des Vergleichs zwischen dem zugeschnittenen Bereich und dem nicht zugeschnittenen Bereich verwendet, sodass die Funktion zum Zuschneiden des Bildes beginnt Hier. Verwenden Sie auch die drawImage-Methode von Canvas.
//绘制剪切图片: 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标签显示出来 }
Schritt 5: Ziehen Sie das Zuschneidefeld und schneiden Sie das Bild erneut zu
Wenn wir die Screenshot-Upload-Avatar-Funktion verwenden, hoffen wir, das Bild zufriedenstellend zuschneiden zu können. Daher muss der Zuschneiderahmen ständig geändert werden, um das perfekte Bild zuzuschneiden. Die Grundfunktion zum Zuschneiden von Bildern wurde in den vorherigen Schritten abgeschlossen. Jetzt müssen Sie nur noch der Bewegung der Maus folgen, um das Bild in Echtzeit zuzuschneiden.
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=""></span> </p> <script type="text/javascript" src="../../lib/seajs/sea.js"></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; });
以上就是本文的全部内容,希望对大家的学习有所帮助。