简体   繁体   English

在任何画布形状周围绘制外边框和内边框

[英]draw outer and inner border around any canvas shape

How to draw outer and inner border around any canvas shape? 如何在任何画布形状周围绘制外边框和内边框?

I'm drawing several stroke-only shapes on an html canvas, and I would like to draw an inner and outer border around them. 我在html画布上绘制了几个仅限笔画的形状,我想在它们周围绘制一个内外边框。

draft example: 草稿示例: 有和没有内/外边框的形状

Is there a generic why to do it for any shape (assuming it's a closed stroke-only shape)? 对于任何形状都有通用的原因(假设它是一个封闭的仅行程形状)?

Two methods 两种方法

There is no inbuilt way to do this and there are two programmatic ways that I use. 没有内置的方法来实现这一点,我使用了两种编程方式。 The first is complicated and involves expanding and contracting the path then drawing along that path. 第一个是复杂的,涉及扩展和收缩路径,然后沿着该路径绘制。 This works for most situations but will fail in complex situation, and the solution has many variables and options to account for these complications and how to handle them. 这适用于大多数情况,但在复杂情况下会失败,并且解决方案有许多变量和选项可以解决这些复杂性以及如何处理它们。

The better of the two 这两者中的更好

The second and easiest way that I present below is by using the ctx.globalCompositeOperation setting to mask out what you want drawn or not. 我在下面介绍的第二种也是最简单的方法是使用ctx.globalCompositeOperation设置来屏蔽你想要绘制的内容。 As the stroke is drawn along the center and the fill fills up to the center you can draw the stroke at twice the desired width and then either mask in or mask out the inner or outer part. 当沿着中心绘制笔划并且填充填充到中心时,您可以以所需宽度的两倍绘制笔划,然后屏蔽或遮盖内部或外部。

This does become problematic when you start to create very complex images as the masking (Global Composite Operation) will interfere with what has already been drawn. 当您开始创建非常复杂的图像时,这确实会成为问题,因为屏蔽(全局复合操作)将干扰已经绘制的内容。

To simplify the process you can create a second canvas the same size as the original as a scratch space. 要简化该过程,您可以创建与原始尺寸相同的第二个画布作为临时空间。 You can then draw the shape on he scratch canvas do the masking and then draw the scratch canvas onto the working one. 然后,您可以在刮刮画布上绘制形状进行遮罩,然后将刮刮画布绘制到工作区上。

Though this method is not as fast as computing the expanded or shrunk path, it does not suffer from the ambiguities faced by moving points in the path. 虽然这种方法没有计算扩展或收缩路径那么快,但它不会受到路径中移动点所面临的模糊性的影响。 Nor does this method create the lines with the correct line join or mitering for the inside or outside edges, for that you must use a the other method. 此方法也不会为内边缘或外边缘创建具有正确线连接或缓和的线条,因为您必须使用其他方法。 For most purposes the masking it is a good solution. 在大多数情况下,掩盖它是一个很好的解决方案。

Below is a demo of the masking method to draw an inner or outer path. 下面是绘制内部或外部路径的掩蔽方法的演示。 If you modify the mask by including drawing a stroke along with the fill you can also set an offset so that the outline or inline will be offset by a number of pixels. 如果通过包括绘制笔划以及填充来修改蒙版,则还可以设置偏移,以便轮廓或内联将偏移多个像素。 I have left that for you. 我已经把它留给了你。 (hint add stroke and set the line width to twice the offset distance when drawing the mask). (提示添加笔划并在绘制蒙版时将线宽设置为偏移距离的两倍)。

 var demo = function(){ /** fullScreenCanvas.js begin **/ var canvas = ( function () { canvas = document.getElementById("canv"); if(canvas !== null){ document.body.removeChild(canvas); } // creates a blank image with 2d context canvas = document.createElement("canvas"); canvas.id = "canv"; canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.style.position = "absolute"; canvas.style.top = "0px"; canvas.style.left = "0px"; canvas.style.zIndex = 1000; canvas.ctx = canvas.getContext("2d"); document.body.appendChild(canvas); return canvas; })(); var ctx = canvas.ctx; /** fullScreenCanvas.js end **/ /** CreateImage.js begin **/ // creates a blank image with 2d context var createImage = function(w,h){ var image = document.createElement("canvas"); image.width = w; image.height =h; image.ctx = image.getContext("2d"); return image; } /** CreateImage.js end **/ // define a shape for demo var shape = [0.1,0.1,0.9,0.1,0.5,0.5,0.8,0.9,0.1,0.9]; // draws the shape as a stroke var strokeShape = function (ctx) { var w, h, i; w = canvas.width; h = canvas.height; ctx.beginPath(); ctx.moveTo(shape[0] *w, shape[1] *h) for (i = 2; i < shape.length; i += 2) { ctx.lineTo(shape[i] * w, shape[i + 1] * h); } ctx.closePath(); ctx.stroke(); } // draws the shape as filled var fillShape = function (ctx) { var w, h, i; w = canvas.width; h = canvas.height; ctx.beginPath(); ctx.moveTo(shape[0] * w,shape[1] * h) for (i = 2; i < shape.length; i += 2) { ctx.lineTo(shape[i]*w,shape[i+1]*h); } ctx.closePath(); ctx.fill(); } var drawInOutStroke = function(width,style,where){ // clear the workspace workCtx.ctx.globalCompositeOperation ="source-over"; workCtx.ctx.clearRect(0, 0, workCtx.width, workCtx.height); // set the width to double workCtx.ctx.lineWidth = width*2; workCtx.ctx.strokeStyle = style; // fill colour does not matter here as its not seen workCtx.ctx.fillStyle = "white"; // can use any join type workCtx.ctx.lineJoin = "round"; // draw the shape outline at double width strokeShape(workCtx.ctx); // set comp to in. // in means leave only pixel that are both in the source and destination if (where.toLowerCase() === "in") { workCtx.ctx.globalCompositeOperation ="destination-in"; } else { // out means only pixels on the destination that are not part of the source workCtx.ctx.globalCompositeOperation ="destination-out"; } fillShape(workCtx.ctx); ctx.drawImage(workCtx, 0, 0); } // clear in case of resize ctx.globalCompositeOperation ="source-over"; ctx.clearRect(0,0,canvas.width,canvas.height); // create the workspace canvas var workCtx = createImage(canvas.width, canvas.height); // draw the outer stroke drawInOutStroke((canvas.width + canvas.height) / 45, "black", "out"); // draw the inner stroke drawInOutStroke((canvas.width + canvas.height) / 45, "red", "in"); // draw the shape outline just to highlight the effect ctx.strokeStyle = "white"; ctx.lineJoin = "round"; ctx.lineWidth = (canvas.width + canvas.height) / 140; strokeShape(ctx); }; // run the demo demo(); // incase fullscreen redraw it all window.addEventListener("resize",demo) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM