html5 canvas+js implements ps pen cutout
html5 canvas+js to implement pen cutout in PS
1. The project requirements required using js to implement the pen cutout function in photoshop, and it took nearly three or four days to solve it. It, in the end, basically realized it for him.
I took a lot of detours during the process, and finally a colleague found canvas to compare the core attribute globalCompositeOperation = "destination-out",
The attribute can be realized by being composed of multiple points The closed range is set to a transparent color that penetrates the canvas background color or background image, which saves a lot of work.
2. Realization effect:
After clicking the mouse, all points will be connected into a closed interval, and any point can be dragged freely. After a closed interval is formed, you can click between any two points. Add new points by dragging.
3. Implementation idea:
Set up two layers of p, set the picture on the bottom layer, and set the canvas canvas on the top layer (if Render the image to the canvas. It will flicker when cutting out, so go to the bottom layer), monitor
mouse events on the canvas, repeatedly render the points and the lines between them, form a closed interval, and then render the entire canvas into a small background image, and render the closed interval with a transparent color. And record or update the relative canvas
coordinates of the point into the array. After taking the picture, the coordinate set of the points is sent back to the background, and the background code implements the screenshot according to the coordinate points and the width and height of the picture, and sets
to the background color as transparent (canvas can also realize screenshots, but It is necessary to process pixels to achieve background transparency, which has not been implemented yet, and is planned to be implemented using C# background code).
4.js (the writing is not standardized and confusing, please take it as a reference)
<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>
Summary:
Shortcomings: When the cursor moves to the line, make a judgment The calculation method of whether it is on a straight line connected by two points is incorrect. It should be calculated as whether a point is within the rectangle
enclosed by the two tangent lines of the two-point circle; the pen point should be replaced by a small p square The grid is more reasonable, like the rectangular cutout below; (Idea: Establish a corresponding relationship between the accessed point coordinate set and the dynamically added small p square
When the small square is dragged, an event is triggered to update the coordinates Point collection and re-render).
6. Can you share it. Thank you
For more html5 canvas+js implementation of PS pen cutout related articles, please pay attention to the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



PS "Loading" problems are caused by resource access or processing problems: hard disk reading speed is slow or bad: Use CrystalDiskInfo to check the hard disk health and replace the problematic hard disk. Insufficient memory: Upgrade memory to meet PS's needs for high-resolution images and complex layer processing. Graphics card drivers are outdated or corrupted: Update the drivers to optimize communication between the PS and the graphics card. File paths are too long or file names have special characters: use short paths and avoid special characters. PS's own problem: Reinstall or repair the PS installer.

Frequently Asked Questions and Solutions when Exporting PS as PDF: Font Embedding Problems: Check the "Font" option, select "Embed" or convert the font into a curve (path). Color deviation problem: convert the file into CMYK mode and adjust the color; directly exporting it with RGB requires psychological preparation for preview and color deviation. Resolution and file size issues: Choose resolution according to actual conditions, or use the compression option to optimize file size. Special effects issue: Merge (flatten) layers before exporting, or weigh the pros and cons.

Solving the problem of slow Photoshop startup requires a multi-pronged approach, including: upgrading hardware (memory, solid-state drive, CPU); uninstalling outdated or incompatible plug-ins; cleaning up system garbage and excessive background programs regularly; closing irrelevant programs with caution; avoiding opening a large number of files during startup.

PS card is "Loading"? Solutions include: checking the computer configuration (memory, hard disk, processor), cleaning hard disk fragmentation, updating the graphics card driver, adjusting PS settings, reinstalling PS, and developing good programming habits.

Export password-protected PDF in Photoshop: Open the image file. Click "File"> "Export"> "Export as PDF". Set the "Security" option and enter the same password twice. Click "Export" to generate a PDF file.

The Pen Tool is a tool that creates precise paths and shapes, and is used by: Select the Pen Tool (P). Sets Path, Fill, Stroke, and Shape options. Click Create anchor point, drag the curve to release the Create anchor point. Press Ctrl/Cmd Alt/Opt to delete the anchor point, drag and move the anchor point, and click Adjust curve. Click the first anchor to close the path to create a shape, and double-click the last anchor to create an open path.

The reason for slow PS loading is the combined impact of hardware (CPU, memory, hard disk, graphics card) and software (system, background program). Solutions include: upgrading hardware (especially replacing solid-state drives), optimizing software (cleaning up system garbage, updating drivers, checking PS settings), and processing PS files. Regular computer maintenance can also help improve PS running speed.

"Loading" stuttering occurs when opening a file on PS. The reasons may include: too large or corrupted file, insufficient memory, slow hard disk speed, graphics card driver problems, PS version or plug-in conflicts. The solutions are: check file size and integrity, increase memory, upgrade hard disk, update graphics card driver, uninstall or disable suspicious plug-ins, and reinstall PS. This problem can be effectively solved by gradually checking and making good use of PS performance settings and developing good file management habits.
