您好,登錄后才能下訂單哦!
這篇文章主要介紹js+html5 canvas實(shí)現(xiàn)ps鋼筆摳圖的示例,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
html5 canvas+js實(shí)現(xiàn)ps鋼筆摳圖
1. 項(xiàng)目要求需要用js實(shí)現(xiàn)photoshop中鋼筆摳圖功能,就用了近三四天的時(shí)間去解決它,最終還是基本上把他實(shí)現(xiàn)了。
做的過程中走了不少彎路,最終一同事找到了canvans以比較核心的屬性globalCompositeOperation = "destination-out",
屬性可以實(shí)現(xiàn)通過由多個(gè)點(diǎn)構(gòu)成的閉合區(qū)間設(shè)置成透明色穿透畫布背景色或是背景圖片,這樣省了許多事。
2.實(shí)現(xiàn)效果:
鼠標(biāo)點(diǎn)完之后會(huì)將所有的點(diǎn)連成閉合區(qū)間,并可自由拖拉任一點(diǎn),當(dāng)形成閉合區(qū)間后,可在任意兩點(diǎn)之間添加新點(diǎn)進(jìn)行拖拉。
3.實(shí)現(xiàn)思路:
設(shè)置兩層div,底層設(shè)置圖片,頂層設(shè)置canvas畫布(如果將圖片渲染到畫布上,摳圖時(shí)會(huì)閃爍,所以至于底層),在畫布上監(jiān)視
鼠標(biāo)事件反復(fù)渲染點(diǎn)及之間連線,形成閉合區(qū)間后將整體畫布渲染小塊背景圖片,并將閉合區(qū)間渲染透明色。并把點(diǎn)的相對(duì)畫布
坐標(biāo)記錄或更新到數(shù)組中去。截完圖后,將點(diǎn)的坐標(biāo)集合傳回后臺(tái),由后臺(tái)代碼實(shí)現(xiàn)根據(jù)坐標(biāo)點(diǎn)及圖片寬度高度實(shí)現(xiàn)截圖,并設(shè)
至背景色為透明色(canvas也可以實(shí)現(xiàn)截圖,但需要處理像素點(diǎn)實(shí)現(xiàn)背景透明,暫時(shí)還沒實(shí)現(xiàn),計(jì)劃用C#后臺(tái)代碼實(shí)現(xiàn))。
4.js(寫的不規(guī)范比較亂,大家就當(dāng)參考吧)
<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) {//是不是按下了鼠標(biāo) if (p.length > 0) { a.equalStartPoint(p[p.length - 1].pointx, p[p.length - 1].pointy); } a.roundIn(e.offsetX, e.offsetY); } //判斷是否在直線上 //光標(biāo)移動(dòng)到線的附近如果是閉合的需要重新劃線,并畫上新添加的點(diǎn) a.AddNewNode(e.offsetX, e.offsetY); }); $("#" + a.can.id).mousedown(function (e) { a.can.paint = true; //點(diǎn)擊判斷是否需要在線上插入新的節(jié)點(diǎn): 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)); //清空臨時(shí)數(shù)組 a.can.tempPointList.length = 0; } }); $("#" + a.can.id).mouseup(function (e) { //拖動(dòng)結(jié)束 a.can.paint = false; //拖動(dòng)結(jié)束; if (a.can.juPull) { a.can.juPull = false; a.can.curPointIndex = 0; //驗(yàn)證摳圖是否閉合:閉合,讓結(jié)束點(diǎn)=開始點(diǎn);添加標(biāo)記 a.equalStartPoint(p[p.length - 1].pointx, p[p.length - 1].pointy); //判斷是否閉合: if (a.can.IsClose) { } } else { //如果閉合:禁止添加新的點(diǎn); if (!a.can.IsClose) {//沒有閉合 p.push(new a.point(e.offsetX, e.offsetY)); //驗(yàn)證摳圖是否閉合:閉合,讓結(jié)束點(diǎn)=開始點(diǎn);添加標(biāo)記 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 { //閉合 } } //驗(yàn)證是否填充背景: if (a.can.IsClose) { a.fillBackColor(); a.drawAllLine(); } }); $("#" + a.can.id).mouseleave(function (e) { a.can.paint = false; }); //鼠標(biāo)點(diǎn)擊事件: $("#" + 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, //坐標(biāo)點(diǎn)集合 pointList: new Array(), //臨時(shí)存儲(chǔ)坐標(biāo)點(diǎn) tempPointList: new Array(), //圓點(diǎn)的觸發(fā)半徑: roundr: 7, //圓點(diǎn)的顯示半徑: roundrr: 7, //當(dāng)前拖動(dòng)點(diǎn)的索引值; curPointIndex : 0, //判斷是否點(diǎn)擊拖動(dòng) paint : false, //判斷是否點(diǎn)圓點(diǎn)拖動(dòng),并瞬間離開,是否拖動(dòng)點(diǎn); juPull : false, //判斷是否閉合 IsClose: false, imgBack: new Image() }; //函數(shù): //更新畫線 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); //坐標(biāo),長(zhǎng)寬 //grd.addColorStop(0, "black"); //起點(diǎn)顏色 //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(); } //光標(biāo)移到線上畫大圈: 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最后一個(gè)坐標(biāo)點(diǎn): this.clearLastPoint=function () { this.can.pointList.pop(); //重畫: this.clearCan(); this.drawAllLine(); } //判斷結(jié)束點(diǎn)是否與起始點(diǎn)重合; 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); } //剪切區(qū)域 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() { } //判斷鼠標(biāo)點(diǎn)是不是在圓的內(nèi)部: this.roundIn = function (x, y) { //剛開始拖動(dòng) 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) { //說明點(diǎn)擊圓點(diǎn)拖動(dòng)了; this.can.juPull = true;//拖動(dòng) // 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 {//拖動(dòng)中 p[this.can.curPointIndex].pointx = x; p[this.can.curPointIndex].pointy = y; //重畫: this.clearCan(); if (this.can.IsClose) { this.fillBackColor(); } this.drawAllLine(); } }; //光標(biāo)移到線上,臨時(shí)數(shù)組添加新的節(jié)點(diǎn): this.AddNewNode=function(newx, newy) { //如果閉合 var ii=0; if (this.can.IsClose) { //判斷光標(biāo)點(diǎn)是否在線上: var p = this.can.pointList; for (var i = 0; i < p.length - 1; i++) { //計(jì)算a點(diǎn)和b點(diǎn)的斜率 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) //添加臨時(shí)點(diǎn): this.can.tempPointList[0] = new this.point(newx, newy);//新的坐標(biāo)點(diǎn) this.can.tempPointList[1] = new this.point(i+1, i+1);//需要往pointlist中插入新點(diǎn)的索引; i++; //alert(); //光標(biāo)移動(dòng)到線的附近如果是閉合的需要重新劃線,并畫上新添加的點(diǎn); 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) { //清空臨時(shí)數(shù)組; 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 { //防止計(jì)算誤差引起的添加點(diǎn),當(dāng)閉合后,瞬間移動(dòng)起始點(diǎn),可能會(huì)插入一個(gè)點(diǎn)到臨時(shí)數(shù)組,當(dāng)再次執(zhí)行時(shí), //就會(huì)在非閉合情況下插入該點(diǎn),所以,時(shí)刻監(jiān)視: 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" ></canvas> </div>
以上是“js+html5 canvas實(shí)現(xiàn)ps鋼筆摳圖的示例”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。