亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

js+html5 canvas實現ps鋼筆摳圖

2024-05-06 16:50:22
字體:
來源:轉載
供稿:網友

html5 canvas+js實現ps鋼筆摳圖

1. 項目要求需要用js實現photoshop中鋼筆摳圖功能,就用了近三四天的時間去解決它,最終還是基本上把他實現了。

做的過程中走了不少彎路,最終一同事找到了canvans以比較核心的屬性globalCompositeOperation = "destination-out",

屬性可以實現通過由多個點構成的閉合區間設置成透明色穿透畫布背景色或是背景圖片,這樣省了許多事。

2.實現效果:

鼠標點完之后會將所有的點連成閉合區間,并可自由拖拉任一點,當形成閉合區間后,可在任意兩點之間添加新點進行拖拉。

js,html5,canvas,ps,鋼筆,摳圖

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鋼筆摳圖詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
九九热最新视频//这里只有精品| 日韩在线观看视频免费| 国产+人+亚洲| 欲色天天网综合久久| 69久久夜色精品国产69| 国产精品免费一区豆花| 亚洲精品动漫久久久久| 韩日欧美一区二区| 亚洲欧美成人一区二区在线电影| 国产成人精彩在线视频九色| 国产欧美在线视频| 久久久成人的性感天堂| 成人免费视频网| 成人av在线亚洲| 国产亚洲精品激情久久| 日韩欧美中文字幕在线播放| 热门国产精品亚洲第一区在线| 黄色一区二区在线观看| 日韩成人在线视频观看| 日韩电影中文字幕| 国产精品老女人视频| 91精品国产高清久久久久久久久| 亚洲第一天堂av| 亚洲影院污污.| 日韩在线观看免费网站| 国产精品免费观看在线| 成人免费看吃奶视频网站| 日韩av一卡二卡| 91国产在线精品| 久久艳片www.17c.com| 日韩在线一区二区三区免费视频| 久久99精品久久久久久青青91| 亚洲美女av电影| 亚洲成色999久久网站| 91国自产精品中文字幕亚洲| 久久久国产精彩视频美女艺术照福利| 国产亚洲精品久久久久久777| 国模极品一区二区三区| 91精品国产91久久久久| 久久精品国产欧美亚洲人人爽| 久久97久久97精品免视看| 热久久免费国产视频| 成人疯狂猛交xxx| 91国语精品自产拍在线观看性色| 成人午夜小视频| 亚洲最大av网站| 欧美丰满老妇厨房牲生活| 亚洲国产精品美女| 亚洲国产精品嫩草影院久久| 亚洲精品日韩丝袜精品| 国产精品精品视频一区二区三区| 国产日韩欧美夫妻视频在线观看| 国产91精品久久久久久久| 亚洲日韩中文字幕在线播放| 亚洲国产高清高潮精品美女| 欧美福利在线观看| 欧美性色视频在线| 一区二区三区动漫| 4438全国成人免费| 国产91在线播放| 欧美裸体xxxx| 欧美精品18videos性欧美| 国产精品免费久久久久久| 日韩av免费在线看| 精品高清一区二区三区| 精品久久久久久久久久ntr影视| 久久久久久久国产精品视频| 欧美激情视频一区| 人妖精品videosex性欧美| 日韩激情在线视频| 亚洲变态欧美另类捆绑| 国产精品v片在线观看不卡| 国产精品男人爽免费视频1| 亚洲视频综合网| www.久久色.com| 日本韩国在线不卡| 久久福利网址导航| 亚洲人成在线观| 国产欧美精品一区二区三区介绍| 国产精品在线看| 亚洲欧美在线一区| 日韩在线欧美在线| 97色伦亚洲国产| 亚洲欧美日本精品| 日韩电视剧在线观看免费网站| 亚洲美女动态图120秒| 欧美色图在线视频| 亚洲成人精品久久久| 欧美性少妇18aaaa视频| 国产精品丝袜久久久久久不卡| 日韩欧亚中文在线| 亚洲美女视频网站| 久久久久久久久久久网站| 高清在线视频日韩欧美| 国产精品狼人色视频一区| 伊人久久免费视频| 日本精品一区二区三区在线播放视频| 欧美午夜女人视频在线| 夜夜狂射影院欧美极品| 欧美日韩电影在线观看| 亚洲电影免费在线观看| 欧美在线视频免费观看| 最近2019中文字幕在线高清| 久久精品国产一区二区电影| 欧美午夜精品伦理| 亚洲国产精品久久久久秋霞蜜臀| 欧美国产中文字幕| 国产免费一区视频观看免费| 深夜福利国产精品| 一本久久综合亚洲鲁鲁| 青草成人免费视频| 欧美激情女人20p| 日韩av123| 一本色道久久88综合日韩精品| 日本国产精品视频| 精品自在线视频| 亚洲国产精品小视频| 精品亚洲一区二区三区四区五区| 韩国精品美女www爽爽爽视频| 国产亚洲精品久久久久动| 2019中文字幕在线免费观看| 亚洲天堂男人天堂女人天堂| 亚洲一区二区在线| 91视频88av| 欧美黄色性视频| 欧美大片欧美激情性色a∨久久| 日本免费一区二区三区视频观看| 亚洲视频在线免费看| 欧美日韩国产丝袜美女| 欧美日韩国产影院| 亚洲一区二区福利| 色偷偷噜噜噜亚洲男人的天堂| 日韩成人中文字幕| 视频一区视频二区国产精品| 国产日韩综合一区二区性色av| 国产精品男人爽免费视频1| 日本久久精品视频| 国产精品久久久久久久电影| 欧美成人自拍视频| 91国偷自产一区二区三区的观看方式| 91午夜理伦私人影院| 精品综合久久久久久97| 在线精品播放av| 国产精品手机播放| 欧美在线一区二区三区四| 日韩亚洲成人av在线| 色偷偷av一区二区三区乱| 久久激情视频免费观看| 日韩在线中文字幕| 欧美日韩精品在线观看| 欧美日韩一区二区三区在线免费观看| 亚洲理论电影网| 欧美国产日本高清在线| 亚洲热线99精品视频| 一区二区成人av| 欧美性少妇18aaaa视频| 久久久久久久久久国产| 日韩精品极品在线观看播放免费视频| 国产欧美日韩精品在线观看| 欧美日韩成人黄色| 91精品国产91久久久久| 91精品综合久久久久久五月天| 欧美精品在线免费| 日韩av一区二区在线观看|