Home  >  Article  >  Web Front-end  >  How to implement local screenshots with html5 canvas and JavaScript

How to implement local screenshots with html5 canvas and JavaScript

不言
不言Original
2018-06-22 15:38:125003browse

This article mainly introduces the JavaScript html5 canvas implementation of local screenshot tutorial. Friends who are interested in the screenshot function can refer to it.

I recently had time to learn about the various APIs of html5 and discovered Sina Weibo. The avatar setting is to use canvas to realize the screenshot. Some time ago, I learned about the File API of html5 and used the File API's FileReader to implement file upload. "JavaScript File API File Upload Preview", I feel that html5 is even more fun. I think Let's try writing this function and learn canvas.
Below is a demo I wrote myself. The code is relatively small and I don’t know how to deal with many details. If there is anything inappropriate, please give me some advice, thank you^_^ ^_^
Function implementation steps:

  • 1. Get the file , read the file and generate the url

  • 2. Use canvas to draw pictures according to the size of the container

  • 3. Use canvas to draw the mask layer

  • ##4. Use canvas to draw the cropped picture

  • ##5. Drag the cropping box and re- Cropped image

  • PS: Because I wrote the demo first and then wrote this article, the code posted in sections is directly from the code Copy piece by piece, pay attention to this object.

Step one: Get the file, read the file and generate the URL
Here I use the file api in html5 to handle local file uploads , because this eliminates the need to upload the image to the server, and then the server returns the image address for preview. For details, please see: Using the File API's FileReader to implement file upload

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传给函数
  };
}  

Second step: Use canvas to draw pictures according to the size of the container

In the previous step, the FileReader using the File API has already obtained the address of the image that needs to be uploaded. Next You need to use canvas to draw this picture. Why not directly insert the img here and redraw it with canvas? Isn't this unnecessary? actually not. If you use img to directly insert into the page, you will not be able to adaptively center. If you use canvas to draw the image, it will not only enable the image to be adaptively centered and scaled equally, but it will also make it easier to pass the coordinates and size of the image to the subsequent mask layer. This allows the mask layer to be drawn based on the coordinates of the image and the size of the image.

Pay a little attention to the drawImage method of canvas here.



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 + &#39;px&#39;;
    t.py = (t.regional.offsetHeight - t.imgHeight) / 2 + &#39;px&#39;;
     
    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();
  };
},
 

The effect is like this:


Step 3: Use canvas to draw the mask layer

In the previous step, you have drawn the background image that needs to be cropped. Now you need to draw the mask layer based on the coordinates and size of the background image to cover the background. And use the canvas's clearRect method to clear out a cropped area so that it can be contrasted with the non-cropped area.
(The mask layer here is only used for display effect, and does not do the work of cropping the image. I don’t know if this step can be removed directly? If anyone knows, please tell me.)


//绘制遮罩层:
t.editBox.height = t.imgHeight;
t.editBox.width = t.imgWidth;
t.editBox.style.display = &#39;block&#39;;
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);
 

Step 4: Use canvas to draw the cropped picture

In the third step, draw the mask layer. However, the mask layer does not have the ability to crop. It is only used to display the comparison between the cropped area and the non-cropped area, so here is the function of cropping the image. Also use the drawImage method of canvas.

//绘制剪切图片:
t.editPic.height = t.sHeight;
t.editPic.width = t.sWidth;
var ctx = t.editPic.getContext(&#39;2d&#39;);
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(&#39;show_edit&#39;).getElementsByTagName(&#39;img&#39;)[0].src = t.editPic.toDataURL(); //把裁剪后的图片使用img标签显示出来
}

Step 5: Drag the cropping box and re-crop the picture

When using the screenshot upload avatar function we I hope to be able to crop a satisfactory picture, so the cropping frame needs to be constantly changed to crop a perfect picture. The basic function of cropping pictures has been completed in the previous steps, so what needs to be done now is to follow the movement of the mouse to crop the picture in real time.

drag: function() {
  var t = this;
  var draging = false;
  var startX = 0;
  var startY = 0;
 
  document.getElementById(&#39;cover_box&#39;).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 = &#39;move&#39;;
       
      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 = &#39;auto&#39;;
    }
  };
}  

It’s done, the picture is as follows:


Some children’s shoes pointed out that every Isn’t it very performance-intensive to crop a picture just by moving the mouse? Why not use background-position to do the preview effect and then use canvas to crop it when saving? When I heard it, I thought this suggestion made sense, so I slightly changed the code in the fourth step. The preview effect when the mouse is moving is to change the background-position of the image. The image is cropped when the save button is clicked. Generate a new URL for the cropped image and then send it to the server~~

The following code has been corrected , if you have any other good suggestions, please point them out ^_^ ^_^

The complete code of the demo is as follows:
Note: Because it is written in seajs, so pay a little attention to the loading of the file
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>
<p id="label">
  <canvas id="get_image"></canvas>
  <p>
    <canvas id="cover_box"></canvas>
    <canvas id="edit_pic"></canvas>
  </p>
</p>
<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([&#39;_example/fileAPI/index_v2.js&#39;], function(clipFile) {
  clipFile.init({
    clipPos: {    //裁剪框的默认尺寸与定位
      x: 15,
      y: 15,
      height: 100,
      width: 100,
    },
  });
});

</script> 

js:

define(function(require, exports, module) {

  &#39;use strict&#39;;

  var postFile = {
    init: function(options) {
      var t = this;
      t.regional = document.getElementById(&#39;label&#39;);
      t.getImage = document.getElementById(&#39;get_image&#39;);
      t.clipPic = document.getElementById(&#39;edit_pic&#39;);
      t.coverBox = document.getElementById(&#39;cover_box&#39;);
      t.achieve = document.getElementById(&#39;show_edit&#39;);

      t.clipPos = options.clipPos;

      //初始化图片基本参数
      t.bgPagePos = {     
        x: 0,
        y: 0,
        height: 0,
        width: 0
      };

      //传进图片
      document.getElementById(&#39;post_file&#39;).addEventListener("change", t.handleFiles, false);

      //点击保存按钮后再裁剪图片
      document.getElementById(&#39;save_button&#39;).onclick = function() {

        //绘制剪切后的图片:
        t.clipPic.height = t.clipPos.height;
        t.clipPic.width = t.clipPos.width;

        var ctx = t.clipPic.getContext(&#39;2d&#39;);
        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(&#39;show_pic&#39;).getElementsByTagName(&#39;img&#39;)[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 + &#39;px&#39;;
        t.bgPagePos.y = (t.regional.offsetHeight - t.bgPagePos.height) / 2 + &#39;px&#39;;
        
        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 = &#39;block&#39;;
      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 = &#39;url(&#39; + t.imgUrl + &#39;)&#39; + -t.clipPos.x + &#39;px &#39; + -t.clipPos.y + &#39;px no-repeat&#39;;
      t.achieve.style.height = t.clipPos.height + &#39;px&#39;;
      t.achieve.style.width = t.clipPos.width + &#39;px&#39;;
    },
    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 = &#39;move&#39;;
          
          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 = &#39;auto&#39;;
        }
      };
    }
  }
  return postFile;
});

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

如何在canvas里面基于随机点绘制一个多边形

H5实现图片压缩与上传

在HTML5 Canvas中放入图片和保存为图片的方法

The above is the detailed content of How to implement local screenshots with html5 canvas and JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn