> 웹 프론트엔드 > H5 튜토리얼 > html5 캔버스 및 JavaScript를 사용하여 로컬 스크린샷을 구현하는 방법

html5 캔버스 및 JavaScript를 사용하여 로컬 스크린샷을 구현하는 방법

不言
풀어 주다: 2018-06-22 15:38:12
원래의
5030명이 탐색했습니다.

이 글은 주로 로컬 스크린샷 튜토리얼의 JavaScript+html5 캔버스 구현을 소개합니다. 스크린샷 기능에 관심이 있는 친구들은 참고하면 됩니다.

최근에 html5의 다양한 API에 대해 알아보는 시간을 가졌고 Sina의 아바타 설정을 발견했습니다. Weibo는 Canvas를 사용하여 스크린샷을 구현하고 있습니다. 얼마전 html5의 File API에 대해 알아보고 File API의 FileReader를 사용하여 파일 업로드를 구현했습니다"JavaScript File API 파일 업로드 미리보기"저는 html5가 더 재미있다고 느꼈습니다. 이 기능을 작성해보고 싶습니다. 캔버스를 배우세요.
다음은 제가 직접 작성한 데모입니다. 코드가 비교적 작아서 자세한 내용을 어떻게 처리해야 할지 모르겠습니다. 부적절한 내용이 있으면 조언 부탁드립니다^_^ ^_^
기능 구현 단계:

  • 1. 파일을 받아 파일을 읽고 URL을 생성합니다

  • 2. 캔버스를 사용하여 컨테이너의 크기에 따라 그림 그리기

  • 3. 캔버스를 사용하여 마스크 레이어 그리기

  • 4. 캔버스를 사용하여 그림 그리기 잘린 사진

  • 5. 자르기 상자를 끌어서 그림을 다시 자릅니다

PS: 이 글을 쓰기 전에 먼저 데모를 작성했기 때문에 섹션에 게시된 코드는 직접 복사할 때 이 개체에 주의하세요
1단계: 파일 가져오기, 파일 읽기 및 URL 생성
여기에서는 로컬 파일 업로드를 처리하기 위해 html5의 파일 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传给函数
  };
}  
로그인 후 복사

2단계: 캔버스를 사용하여 그에 따라 이미지를 그립니다. 위의 컨테이너 크기에 맞춰

한 단계에서 File API를 사용하는 FileReader는 업로드할 이미지의 주소를 얻었습니다. 다음으로 캔버스를 사용하여 이미지를 그려야 합니다. 여기에 img를 직접 삽입하고 캔버스로 다시 그려보면 어떨까요? 설마. img를 사용하여 페이지에 직접 삽입하면 적응적으로 중앙에 배치되지 않습니다. 캔버스를 사용하여 이미지를 그리는 경우 이미지를 적응적으로 중앙에 배치하고 균등하게 크기를 조정할 수 있을 뿐만 아니라 쉽게 만들 수 있습니다. 이미지의 좌표와 크기를 후속 마스크 레이어에 전달합니다. 이렇게 하면 이미지의 좌표와 이미지의 크기를 기반으로 마스크 레이어를 그릴 수 있습니다.
여기서 캔버스의 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 + &#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();
  };
},
 
로그인 후 복사

효과는 다음과 같습니다.

3단계: 캔버스를 사용하여 마스크 레이어 그리기
이전 단계에서 잘라야 할 배경 이미지가 그려졌습니다. 이제 배경 이미지의 좌표와 크기에 따라 배경을 덮을 마스크 레이어를 그려야 하고, 캔버스의clearRect 메소드를 사용하여 잘려진 영역을 지워서 잘리지 않은 영역과 대조될 수 있도록 해야 합니다.
(여기의 마스크 레이어는 디스플레이 효과에만 사용되며 사진을 자르는 작업은 하지 않습니다. 이 단계를 직접 제거할 수 있는지는 모르겠습니다. 아시는 분 알려주세요.)

//绘制遮罩层:
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);
 
로그인 후 복사

4단계: 캔버스를 사용하여 자른 그림 그리기
3단계에서는 마스크 레이어가 그려지지만 마스크 레이어에는 자르기 기능이 없습니다. 크롭되지 않은 영역은 비교용이므로 이미지 크롭 기능을 소개합니다. 또한 캔버스의 drawImage 메소드를 사용하십시오.

//绘制剪切图片:
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标签显示出来
}
로그인 후 복사

5단계: 자르기 상자를 드래그하고 사진을 다시 자르기
스크린샷 업로드 아바타 기능을 사용할 때 사진을 만족스럽게 자르기를 희망하므로 자르기 상자는 지속적으로 변경되어야 합니다. 완벽한 사진을 만드세요. 사진 자르기의 기본 기능은 이전 단계에서 완료되었으므로 이제 해야 할 일은 마우스의 움직임을 따라 실시간으로 사진을 자르는 것입니다.

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;;
    }
  };
}  
로그인 후 복사

완료되었습니다. 그림은 다음과 같습니다.


일부 어린이는 마우스를 움직일 때마다 그림을 자르는 것이 성능 집약적이지 않다고 지적했습니다. 배경을 사용하는 것은 어떨까요? -미리보기 효과 저장시 위치 그냥 캔버스로 잘라내셨나요? 듣고 보니 이 제안이 타당하다고 생각해서 4단계에서 코드를 살짝 바꿔봤습니다. 마우스가 움직일 때의 미리보기 효과는 이미지의 배경 위치를 변경하는 것입니다. 저장 버튼을 클릭하면 이미지가 잘립니다. 자른 이미지에 대한 새 URL을 생성하여 서버로 보냅니다~~
다음 코드입니다. 여러분 혹시 다른 좋은 제안 있으면 지적해주세요^_^ ^_^
데모의 전체 코드는 다음과 같습니다.
참고: 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>
<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中放入图片和保存为图片的方法

위 내용은 html5 캔버스 및 JavaScript를 사용하여 로컬 스크린샷을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿