搜索
首页web前端js教程JavaScript+html5 canvas实现本地截图教程_javascript技巧

最近有时间了解了下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 &#63; t.regional.offsetWidth : pWidth;
      t.imgHeight = img.height > img.width &#63; 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 &#63; t.regional.offsetWidth : pWidth;
          t.bgPagePos.height = img.height > img.width &#63; 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;
});

以上就是本文的全部内容,希望对大家的学习有所帮助。

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
在JavaScript中替换字符串字符在JavaScript中替换字符串字符Mar 11, 2025 am 12:07 AM

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

构建您自己的Ajax Web应用程序构建您自己的Ajax Web应用程序Mar 09, 2025 am 12:11 AM

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

如何创建和发布自己的JavaScript库?如何创建和发布自己的JavaScript库?Mar 18, 2025 pm 03:12 PM

文章讨论了创建,发布和维护JavaScript库,专注于计划,开发,测试,文档和促销策略。

如何在浏览器中优化JavaScript代码以进行性能?如何在浏览器中优化JavaScript代码以进行性能?Mar 18, 2025 pm 03:14 PM

本文讨论了在浏览器中优化JavaScript性能的策略,重点是减少执行时间并最大程度地减少对页面负载速度的影响。

如何使用浏览器开发人员工具有效调试JavaScript代码?如何使用浏览器开发人员工具有效调试JavaScript代码?Mar 18, 2025 pm 03:16 PM

本文讨论了使用浏览器开发人员工具的有效JavaScript调试,专注于设置断点,使用控制台和分析性能。

jQuery矩阵效果jQuery矩阵效果Mar 10, 2025 am 12:52 AM

将矩阵电影特效带入你的网页!这是一个基于著名电影《黑客帝国》的酷炫jQuery插件。该插件模拟了电影中经典的绿色字符特效,只需选择一张图片,插件就会将其转换为充满数字字符的矩阵风格画面。快来试试吧,非常有趣! 工作原理 插件将图片加载到画布上,读取像素和颜色值: data = ctx.getImageData(x, y, settings.grainSize, settings.grainSize).data 插件巧妙地读取图片的矩形区域,并利用jQuery计算每个区域的平均颜色。然后,使用

如何构建简单的jQuery滑块如何构建简单的jQuery滑块Mar 11, 2025 am 12:19 AM

本文将引导您使用jQuery库创建一个简单的图片轮播。我们将使用bxSlider库,它基于jQuery构建,并提供许多配置选项来设置轮播。 如今,图片轮播已成为网站必备功能——一图胜千言! 决定使用图片轮播后,下一个问题是如何创建它。首先,您需要收集高质量、高分辨率的图片。 接下来,您需要使用HTML和一些JavaScript代码来创建图片轮播。网络上有很多库可以帮助您以不同的方式创建轮播。我们将使用开源的bxSlider库。 bxSlider库支持响应式设计,因此使用此库构建的轮播可以适应任何

如何使用Angular上传和下载CSV文件如何使用Angular上传和下载CSV文件Mar 10, 2025 am 01:01 AM

数据集对于构建API模型和各种业务流程至关重要。这就是为什么导入和导出CSV是经常需要的功能。在本教程中,您将学习如何在Angular中下载和导入CSV文件

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能