js+html5 canvas实现ps钢笔抠图

html5 canvas+js实现ps钢笔抠图

1. 项目要求需要用js实现photoshop中钢笔抠图功能,就用了近三四天的时间去解决它,最终还是基本上把他实现了。

做的过程中走了不少弯路,最终一同事找到了canvans以比较核心的属性globalCompositeOperation = "destination-out",

属性可以实现通过由多个点构成的闭合区间设置成透明色穿透画布背景色或是背景图片,这样省了许多事。

2.实现效果:

鼠标点完之后会将所有的点连成闭合区间,并可自由拖拉任一点,当形成闭合区间后,可在任意两点之间添加新点进行拖拉。

3.实现思路:

设置两层div,底层设置图片,顶层设置canvas画布(如果将图片渲染到画布上,抠图时会闪烁,所以至于底层),在画布上监视

鼠标事件反复渲染点及之间连线,形成闭合区间后将整体画布渲染小块背景图片,并将闭合区间渲染透明色。并把点的相对画布

坐标记录或更新到数组中去。截完图后,将点的坐标集合传回后台,由后台代码实现根据坐标点及图片宽度高度实现截图,并设

至背景色为透明色(canvas也可以实现截图,但需要处理像素点实现背景透明,暂时还没实现,计划用C#后台代码实现)。

4.js(写的不规范比较乱,大家就当参考吧)

<script type="text/javascript">
  $(function () {
   var a = new tailorImg();
   a.iniData();
  });
  //
  var tailorImg=function()
  {
   this.iniData = function () {
    //画布
    this.can.id = "canvas";
    this.can.w = 400;
    this.can.h = 400;
    this.can.roundr = 7;
    this.can.roundrr = 3;
    this.can.curPointIndex = 0;
    this.can.imgBack.src = "gzf.png";
    this.can.canvas = document.getElementById(this.can.id).getContext("2d");
    //图片
    this.img.w = 400;
    this.img.h = 400;
    this.img.image.src = "flower.jpg";
    //加载事件:
    //初始化事件:
    var a = this;
    var p = a.can.pointList;
    $("#" + a.can.id).mousemove(function (e) {
     if (a.can.paint) {//是不是按下了鼠标
      if (p.length > 0) {
       a.equalStartPoint(p[p.length - 1].pointx, p[p.length - 1].pointy);
      }
      a.roundIn(e.offsetX, e.offsetY);
     }
     //判断是否在直线上
     //光标移动到线的附近如果是闭合的需要重新划线,并画上新添加的点
     a.AddNewNode(e.offsetX, e.offsetY);
    });
    $("#" + a.can.id).mousedown(function (e) {
     a.can.paint = true;
     //点击判断是否需要在线上插入新的节点:
     if (a.can.tempPointList.length > 0) {
      a.can.pointList.splice(a.can.tempPointList[1].pointx, 0, new a.point(a.can.tempPointList[0].pointx, a.can.tempPointList[0].pointy));
      //清空临时数组
      a.can.tempPointList.length = 0;
     }
    });
    $("#" + a.can.id).mouseup(function (e) {
     //拖动结束
     a.can.paint = false;
     //拖动结束;
     if (a.can.juPull) {
      a.can.juPull = false;
      a.can.curPointIndex = 0;
      //验证抠图是否闭合:闭合,让结束点=开始点;添加标记
      a.equalStartPoint(p[p.length - 1].pointx, p[p.length - 1].pointy);
      //判断是否闭合:
      if (a.can.IsClose) {

      }
     }
     else {
      //如果闭合:禁止添加新的点;
      if (!a.can.IsClose) {//没有闭合
       p.push(new a.point(e.offsetX, e.offsetY));
       //验证抠图是否闭合:闭合,让结束点=开始点;添加标记
       a.equalStartPoint(p[p.length - 1].pointx, p[p.length - 1].pointy);
       //判断是否闭合:
       //重新画;
       if (p.length > 1) {
        a.drawLine(p[p.length - 2].pointx, p[p.length - 2].pointy, p[p.length - 1].pointx, p[p.length - 1].pointy);
        a.drawArc(p[p.length - 1].pointx, p[p.length - 1].pointy);
       } else {
        a.drawArc(p[p.length - 1].pointx, p[p.length - 1].pointy);
       }
      }
      else {
       //闭合
      }
     }
     //验证是否填充背景:
     if (a.can.IsClose) {
      a.fillBackColor();
      a.drawAllLine();
     }
    });
    $("#" + a.can.id).mouseleave(function (e) {
     a.can.paint = false;
    });
    //鼠标点击事件:
    $("#" + a.can.id).click(function (e) {
     //空
    });
   }
   this.point = function (x, y) {
    this.pointx = x;
    this.pointy = y;
   };
   //图片
   this.img = {
    image:new Image(),
    id: "",
    w:0,
    h:0
   };
   //画布;
   this.can = {
    canvas:new Object(),
    id: "",
    w: 0,
    h: 0,
    //坐标点集合
    pointList: new Array(),
    //临时存储坐标点
    tempPointList: new Array(),
    //圆点的触发半径:
    roundr: 7,
    //圆点的显示半径:
    roundrr: 7,
    //当前拖动点的索引值;
    curPointIndex : 0,
    //判断是否点击拖动
    paint : false,
    //判断是否点圆点拖动,并瞬间离开,是否拖动点;
    juPull : false,
    //判断是否闭合
    IsClose: false,
    imgBack: new Image()

   };
   //函数:
   //更新画线
   this.drawAllLine=function () {
    for (var i = 0; i < this.can.pointList.length - 1; i++) {
     //画线
     var p = this.can.pointList;
     this.drawLine(p[i].pointx, p[i].pointy, p[i + 1].pointx, p[i + 1].pointy);
     //画圈
     this.drawArc(p[i].pointx, p[i].pointy);
     if (i == this.can.pointList.length - 2) {
      this.drawArc(p[i+1].pointx, p[i+1].pointy);
     }
    }
   }
   //画线
   this.drawLine = function (startX, startY, endX, endY) {
    //var grd = this.can.canvas.createLinearGradient(0, 0,2,0); //坐标,长宽
    //grd.addColorStop(0, "black"); //起点颜色
    //grd.addColorStop(1, "white");
    //this.can.canvas.strokeStyle = grd;
    this.can.canvas.strokeStyle = "blue"
    this.can.canvas.lineWidth =1;
    this.can.canvas.moveTo(startX, startY);
    this.can.canvas.lineTo(endX, endY);
    this.can.canvas.stroke();
   }
   //画圈:
   this.drawArc=function(x, y) {
    this.can.canvas.fillStyle = "blue";
    this.can.canvas.beginPath();
    this.can.canvas.arc(x, y,this.can.roundrr, 360, Math.PI * 2, true);
    this.can.canvas.closePath();
    this.can.canvas.fill();
   }
   //光标移到线上画大圈:
   this.drawArcBig = function (x, y) {
    this.can.canvas.fillStyle = "blue";
    this.can.canvas.beginPath();
    this.can.canvas.arc(x, y, this.can.roundr+2, 360, Math.PI * 2, true);
    this.can.canvas.closePath();
    this.can.canvas.fill();
   }
   //渲染图片往画布上
   this.showImg=function() {
    this.img.image.onload = function () {
     this.can.canvas.drawImage(this.img.image, 0, 0, this.img.w,this.img.h);
    };
   }
   //填充背景色
   this.fillBackColor = function () {
    for (var i = 0; i <this.img.w; i += 96) {
     for (var j = 0; j <= this.img.h; j += 96) {
      this.can.canvas.drawImage(this.can.imgBack, i, j, 96, 96);
     }
    }
    this.can.canvas.globalCompositeOperation = "destination-out";
    this.can.canvas.beginPath();
    for (var i = 0; i <this.can.pointList.length; i++) {
     this.can.canvas.lineTo(this.can.pointList[i].pointx,this.can.pointList[i].pointy);
    }
    this.can.canvas.closePath();
    this.can.canvas.fill();
    this.can.canvas.globalCompositeOperation = "destination-over";
    this.drawAllLine();
   }
   //去掉pointlist最后一个坐标点:
   this.clearLastPoint=function () {
    this.can.pointList.pop();
    //重画:
    this.clearCan();
    this.drawAllLine();
   }
   //判断结束点是否与起始点重合;
   this.equalStartPoint = function (x,y) {
    var p = this.can.pointList;
    if (p.length > 1 && Math.abs((x - p[0].pointx) * (x - p[0].pointx)) + Math.abs((y - p[0].pointy) * (y - p[0].pointy)) <= this.can.roundr * this.can.roundr) {
     //如果闭合
     this.can.IsClose = true;
     p[p.length - 1].pointx = p[0].pointx;
     p[p.length - 1].pointy = p[0].pointy;
    }
    else {
     this.can.IsClose = false;
    }
   }
   //清空画布
   this.clearCan=function (){
    this.can.canvas.clearRect(0, 0, this.can.w, this.can.h);
   }
   //剪切区域
   this.CreateClipArea=function () {
    this.showImg();
    this.can.canvas.beginPath();
    for (var i = 0; i <this.can.pointList.length; i++) {
     this.can.canvas.lineTo(this.can.pointList[i].pointx,this.can.pointList[i].pointy);
    }
    this.can.canvas.closePath();
    this.can.canvas.clip();
   }
   //
   this.CreateClipImg=function()
   {

   }
   //判断鼠标点是不是在圆的内部:
   this.roundIn = function (x, y) {
    //刚开始拖动
    var p = this.can.pointList;
    if (!this.can.juPull) {
     for (var i = 0; i < p.length; i++) {

      if (Math.abs((x - p[i].pointx) * (x - p[i].pointx)) + Math.abs((y - p[i].pointy) * (y - p[i].pointy)) <= this.can.roundr * this.can.roundr) {
       //说明点击圆点拖动了;
       this.can.juPull = true;//拖动
       //
       this.can.curPointIndex = i;
       p[i].pointx = x;
       p[i].pointy = y;
       //重画:
       this.clearCan();
       //showImg();
       if (this.can.IsClose) {
        this.fillBackColor();
       }
       this.drawAllLine();
       return;
      }
     }
    }
    else {//拖动中
     p[this.can.curPointIndex].pointx = x;
     p[this.can.curPointIndex].pointy = y;
     //重画:
     this.clearCan();
     if (this.can.IsClose) {
      this.fillBackColor();
     }
     this.drawAllLine();
    }
   };

   //光标移到线上,临时数组添加新的节点:
   this.AddNewNode=function(newx, newy) {
    //如果闭合
    var ii=0;
    if (this.can.IsClose) {
     //判断光标点是否在线上:
     var p = this.can.pointList;
     for (var i = 0; i < p.length - 1; i++) {
      //计算a点和b点的斜率
      var k = (p[i + 1].pointy - p[i].pointy) / (p[i + 1].pointx - p[i].pointx);
      var b = p[i].pointy - k * p[i].pointx;
      //if (parseInt((p[i + 1].pointy - p[i].pointy) / (p[i + 1].pointx - p[i].pointx)) ==parseInt((p[i + 1].pointy - newy) / (p[i + 1].pointx - newx)) && newx*2-p[i+1].pointx-p[i].pointx<0 && newy*2-p[i+1].pointy-p[i].pointy<0) {
      // //如果在直线上
      // alert("在直线上");
      //}
      $("#txtone").val(parseInt(k * newx + b));
      $("#txttwo").val(parseInt(newy));
      if (parseInt(k * newx + b) == parseInt(newy) && (newx - p[i + 1].pointx) * (newx - p[i].pointx) <= 2 && (newy - p[i + 1].pointy) * (newy - p[i].pointy) <= 2) {
       //
       //parseInt(k * newx + b) == parseInt(newy)
       //添加临时点:
       this.can.tempPointList[0] = new this.point(newx, newy);//新的坐标点
       this.can.tempPointList[1] = new this.point(i+1, i+1);//需要往pointlist中插入新点的索引;
       i++;
       //alert();
       //光标移动到线的附近如果是闭合的需要重新划线,并画上新添加的点;
       if (this.can.tempPointList.length > 0) {
        //重画:
        this.clearCan();
        //showImg();
        if (this.can.IsClose) {
         this.fillBackColor();
        }
        this.drawAllLine();
        this.drawArcBig(this.can.tempPointList[0].pointx, this.can.tempPointList[0].pointy);
        return;
       }
       return;
      }
      else {
       // $("#Text1").val("");
      }
     }
     if (ii == 0) {
      if (this.can.tempPointList.length > 0) {
       //清空临时数组;
       this.can.tempPointList.length = 0;
       //重画:
       this.clearCan();
       //showImg();
       if (this.can.IsClose) {
        this.fillBackColor();
       }
       this.drawAllLine();
       //this.drawArc(this.can.tempPointList[0].pointx, this.can.tempPointList[0].pointy);
      }
     }
    }
    else {
     //防止计算误差引起的添加点,当闭合后,瞬间移动起始点,可能会插入一个点到临时数组,当再次执行时,
     //就会在非闭合情况下插入该点,所以,时刻监视:
     if (this.can.tempPointList.length > 0) {
      this.can.tempPointList.length = 0;
     }
    }
   }

  };

 </script>
<style type="text/css">
  .canvasDiv {
   position: relative;
   border: 1px solid red;
   height: 400px;
   width: 400px;
   top: 50px;
   left: 100px;
   z-index: 0;
  }

  img {
   width: 400px;
   height: 400px;
   z-index: 1;
   position: absolute;
  }

  #canvas {
   position: absolute;
   border: 1px solid green;
   z-index: 2;
  }
  .btnCollection {
   margin-left: 100px;
  }
 </style>
 <div class="canvasDiv">
    <img src="flower.jpg" />
    <canvas id="canvas" width="400" height="400" style="border: 1px solid green;"></canvas>
  </div>

5.总结:

不足:当光标移动到线上时,判断一点是否在两点连成的直线上计算方法不正确,应该计算为一点是否在两点圆两条外切线所围成的矩形内;钢笔点应为替换为小的div方格比较合理,像下面的矩形抠图;(思路:将存取的点坐标集合和动态添加的小div方格建立对应关系当拖动小方格时,触发事件更新坐标点集合,并重新渲染)。

以上所述是小编给大家介绍的js+html5 canvas实现ps钢笔抠图详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Javascript HTML5 Canvas实现的一个画板

    本文实例为大家分享了HTML5 Canvas实现的一个画板代码,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>DEMO6:自定义画板</title> </head> <body> <canvas id="canvas" width="600" he

  • js HTML5 canvas绘制图片的方法

    本文实例为大家分享了HTML5 canvas绘制图片的具体代码,供大家参考,具体内容如下 demo.js window.onload=function() { createcanvas(); drawImage(); } function createcanvas() { var CANVAS=document.getElementById('mycanvas'); context=CANVAS.getContext('2d'); } function drawImage() { var img

  • JavaScript+html5 canvas实现本地截图教程

    最近有时间了解了下html5的各API,发现新浪微博的头像设置是使用canvas实现截图的,加之前段时间了解了下html5的File API使用File API 之FileReader实现文件上传<JavaScript File API文件上传预览>,更加觉得html5好玩了,想着也试试写写这功能权当学习canvas吧. 下面奉上我自己写的一个demo,代码写得比较少,很多细节不会处理.如果有不得当的地方恳请指教,谢谢啦 ^_^ ^_^ 功能实现步奏: 一.获取文件,读取文件并生成url 二.

  • javascript HTML5 canvas实现打砖块游戏

    本文实例编写的一个小游戏,基于HTML5中的canvas.游戏主要是小球反弹击打小方块.在代码中主要实现了小方块的生成,键盘键事件的监听,小球的移动和碰壁之后的反弹以及怎样消除小方块等方法.代码使用到了一个js脚本库 游戏开发流程: 1.创建画布: 将画布放在div标签里面,这样可以控制画布居中的位置,再对div标签加上一些样式比如border和border-radius,这样一来使其看上去像手机,利于观看. <div id="main"> <!--将画布嵌在div块

  • javascript html5 canvas实现可拖动省份的中国地图

    本文实例分享了html5 canvas可拖动省份的中国地图实现方法,供大家参考,具体内容如下 1.数据获取 画地图需要省份边界坐标,理论上可以每次都用百度API获取数据并绘图,但为了增加效率,首先将所有坐标都获取下来并存入数据库中. 新建省份数据数组 复制代码 代码如下: var allZoneData = [{'name':'辽宁省','been':'yes','id':'01'},<span style="font-family: Arial, Helvetica, sans-seri

  • js HTML5 Canvas绘制转盘抽奖

    本文实例为大家分享了js转盘抽奖的具体代码,供大家参考,具体内容如下 1.实现的基本效果 2.主要的内容 •html5中canvas标签的使用  •jQueryRotate.js旋转插件 3.主要html代码 <body> <div class="content"> <div class="wheel"> <canvas class="item" id="wheelCanvas" wi

  • JS+html5 canvas实现的简单绘制折线图效果示例

    本文实例讲述了JS+html5 canvas实现的简单绘制折线图效果.分享给大家供大家参考,具体如下: 1.实例代码: <!DOCTYPE html> <html> <head> <meta charset='utf-8'> <title>画图</title> <style> #divContainer{ margin-top: 20px; text-align: center; } #cv{ width: 300px;

  • javascript+HTML5 Canvas绘制转盘抽奖

    之前做过的项目中,有需要抽奖转盘功能的.项目已经完工一段时间了,也没出现什么严重的bug,所以现在拎出来分享给大家. 功能需求 1.转盘要美观,转动效果流畅. 2.转盘上需要显示奖品图片,并且奖品是后台读取的照片和名字. 3.转动动画完成后要有相应提示. 4.获取的奖品具体算法在数据库里操作,前端只提供最后的效果展示. 知识要点 1.引用了一个jq插件:awardRotate,用来实现更智能化的转动(插件下载:http://www.jqcool.net/jquery-jqueryrotate.h

  • js+html5实现canvas绘制椭圆形图案的方法

    本文实例讲述了js+html5实现canvas绘制椭圆形图案的方法,HTML5 canvas 没有画椭圆的方法,以下代码可以画出椭圆,分享给大家供大家参考,具体实现方法如下: 1.在一个隐式的画布 (将 其 CSS 定义成:display:none; ) 上画园. 2.将隐式画布的影像,以不同的宽高比值,画在另一个显式的画布,以使园变成椭圆. 3.进而,加进动画功能. <html> <head> <meta http-equiv="Content-Type"

  • JavaScript+html5 canvas实现图片破碎重组动画特效

    也许你见过HTML5图片破碎动画特效,实现的原理也挺简单的.但是你应该没有见过视频也可以破碎重组,这个HTML5动画就是利用Canvas的相关特性,实现了点击鼠标让视频破碎重组的效果.在视频区域点击鼠标,即可让该区域的视频破碎,让后经过一段时间后,破碎的区域又可以重组还原,视觉效果非常棒. HTML代码 <div style="display:none"> <video id="sourcevid" autoplay="true"

随机推荐