是否可以制作带线条背景的 canvas 或不是矩形的 canvas?
Is it possible to make canvas with background with lines or canvas that isn't a rectangle?
我正在努力制作这个https://massmoca.org/event/walldrawing340/
在 Javascript 代码中,使用 p5.js,但我不知道如何用线条填充这些形状。有没有其他的可能性,比如把 canvas 做成圆形之类的,或者我只需要分别做每个形状?
目前我是一个形状一个形状地做,但是做三角形和梯形还是比较粗糙的...
var sketch = function (p) {
with(p) {
let h,
w,
space;
p.setup = function() {
createCanvas(900, 400);
h = height / 2;
w = width / 3;
space = 10;
noLoop();
};
p.draw = function() {
drawBackground('red', 'blue', 0, 0);
shape('Circle', 'red', 'blue', 0, 0);
drawBackground('yellow', 'red', w, 0);
shape('Square', 'yellow', 'red', w, 0);
drawBackground('blue', 'yellow', 2 * w, 0);
shape('Triangle', 'blue', 'red', 2 * w, 0)
drawBackground('red', 'yellow', 0, h);
shape('Rectangle', 'red', 'blue', 0, h)
drawBackground('yellow', 'blue', w, h);
shape('Trapezoid', 'yellow', 'red', w, h);
drawBackground('blue', 'red', 2 * w, h);
};
function drawBackground(bColor, lColor, x, y) {
fill(bColor)
noStroke();
rect(x, y, w, h)
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < h / space; i++) {
line(0 + x, i * space + y + 10, w + x, i * space + y + 10);
}
}
function shape(shape, bColor, lColor, x, y) {
fill(bColor)
noStroke();
let w1;
switch (shape) {
case 'Circle':
circle(x + w / 2, y + h / 2, h - space * 6);
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < w / space; i++) {
for (let j = 0; j < h; j++) {
pX = i * space + x;
pY = 0 + y + j;
if (pow(x + w / 2 - pX, 2)
+ pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
point(pX, pY);
}
}
}
break;
case 'Square':
w1 = w - (h - space * 6);
rect(x + w1 / 2, y + space * 3, h - space * 6, h - space * 6);
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < 15; i++) {
for (let j = 0; j < h - space * 6; j++) {
point(x + w1 / 2 + i * space, y + space * 3 + j)
}
}
break;
case 'Triangle':
w1 = w - (h - space * 6);
triangle(x + w1 / 2, h - space * 3 + y, x + w / 2, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
for (let i = 0; i < w / space; i++) {
for (let j = 0; j < h; j++) {
pX = i * space + x;
pY = 0 + y + j;
if (pow(x + w / 2 - pX, 2)
+ pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
point(pX, pY);
}
}
}
break;
case 'Rectangle':
w1 = w - (h - space * 6) / 2;
rect(x + w1 / 2, y + space * 3, (h - space * 6) / 2, h - space * 6)
break;
case 'Trapezoid':
w1 = w - (h - space * 6);
quad(x + w1 / 2, h - space * 3 + y, x + w1 / 2 + (h - space * 6) / 4, y + space * 3, x + w1 / 4 + h - space * 6, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
break;
case 'Parallelogram':
w1 = w - (h - space * 6);
quad(x + w1 / 4, h - space * 3 + y, x + w1 / 2, y + space * 3, x + w1 / 2 + h - space * 6, y + space * 3, x + w1 / 4 + h - space * 6, h - space * 3 + y)
break;
break;
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
没有消息,一切正常,我只是想知道我是否必须做这么多艰巨的工作...
我有办法做一些形状,但我不确定其他形状。一种方法是,如果您知道形状轮廓上的每个点的位置,您可以只使用 for 循环并使用 line 或 rect 函数连接顶部和底部的所有其他点。对于正方形和平行四边形这样的形状,这将相对容易,但我不确定可以使用什么函数来获得圆形或梯形的点。
你需要线性代数的东西,基本上注意垂直线 starting/ending Y
坐标如何相对于线的 X
坐标变化。当然,在你得到可用的东西之前,还要进行大量的试验。像 :
var w = 600
h = 600
sp = 15
var slides = [fcircle, fsquare, ftriangle, ftrapezoid, fparallelogram];
var active = 0;
var ms;
function blines(){
stroke(0);
for (var i=0; i < h; i+=sp) {
line(0,i,w,i);
}
}
function vertlines(calcline) {
for (var x=w/2-w/4+sp; x < w/2+w/4; x+=sp) {
var pnts = calcline(x);
line(pnts[0],pnts[1],pnts[2],pnts[3]);
}
}
function fcircle() {
// cut background
noStroke();
circle(w/2, h/2, w/2);
stroke('red');
// draw figure lines
let calc = function (x){
var sx = x-w/2;
var sy = h/2;
var ey = h/2;
sy += 137*sin(2.5+x/135);
ey -= 137*sin(2.5+x/135);
return [x,sy,x,ey];
}
vertlines(calc);
}
function fsquare() {
// cut background
noStroke();
quad(w/2-w/4, h/2-h/4, w/2+w/4, h/2-h/4,
w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
return [x,h/2-h/4,x,h/2+h/4];
}
vertlines(calc);
}
function ftriangle() {
// cut background
noStroke();
quad(w/2, h/2-h/4, w/2+w/4, h/2+h/4,
w/2-w/4, h/2+h/4, w/2, h/2-h/4);
stroke('red');
// draw figure lines
let calc = function (x){
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.3*inpx*log(inpx)-220);
return [x,ys,x,h/2+h/4];
}
vertlines(calc);
}
function ftrapezoid() {
// cut background
noStroke();
quad(w/2-w/10, h/2-h/4, w/2+w/10, h/2-h/4,
w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.55*inpx*log(inpx)-420);
if (x >= w/2-w/10 && x <= w/2+w/10) {
ys=h/2-h/4;
}
return [x,ys,x,h/2+h/4];
}
vertlines(calc);
}
function fparallelogram() {
// cut background
noStroke();
quad(w/2-w/10, h/2-h/4, w/2+w/7, h/2-h/4,
w/2, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
// guard condition
if (x > w/2+w/7)
return [0,0,0,0];
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.55*inpx*log(inpx)-420);
var ye=h/2+h/4
if (x >= w/2-w/10) {
ys=h/2-h/4;
}
if (x > w/2) {
ye = h/2+h/4;
ye += 0.50*inpx*log(inpx)-870;
}
return [x,ys,x,ye];
}
vertlines(calc);
}
function setup() {
ms = millis();
createCanvas(w, h);
}
function draw() {
if (millis() - ms > 2000) {
ms = millis();
active++;
if (active > slides.length-1)
active = 0;
}
background('#D6EAF8');
fill('#D6EAF8');
blines();
slides[active]();
}
如果您不需要实际的线坐标(例如用于绘图),我会充分利用 createGraphics()
to easily render shapes and lines into (taking advantage of the fact that get()
returns a p5.Image
) and p5.Image's mask()
函数。
这是一个基本示例:
function setup() {
createCanvas(600, 300);
let w = 300;
let h = 150;
let spacing = 12;
let strokeWidth = 1;
const BLUE = color('#005398');
const YELLOW = color('#f9db44');
const RED = color('#dc1215');
bg = getLinesRect(w, h, RED, BLUE, spacing, strokeWidth, true);
fg = getLinesRect(w, h, RED, YELLOW, spacing, strokeWidth, false);
mask = getCircleMask(w, h, w * 0.5, h * 0.5, 100, 0);
image(bg, 0, 0);
image(fg, w, 0);
// render opaque mask (for visualisation only), mask() requires alpha channel
image(getCircleMask(w, h, w * 0.5, h * 0.5, 100, 255),0, h);
// apply mask
fg.mask(mask);
// render bg + masked fg
image(bg, w, h);
image(fg, w, h);
// text labels
noStroke();
fill(255);
text("bg layer", 9, 12);
text("fg layer", w + 9, 12);
text("mask", 9, h + 12);
text("bg + masked fg", w + 9, h + 12);
}
function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
let rect = createGraphics(w, h);
rect.background(bg);
rect.stroke(fg);
rect.strokeWeight(strokeWidth);
if(isHorizontal){
for(let y = 0 ; y < h; y += spacing){
rect.line(0, y + strokeWidth, w, y + strokeWidth);
}
}else{
for(let x = 0 ; x < w; x += spacing){
rect.line(x + strokeWidth, 0, x + strokeWidth, h);
}
}
// convert from p5.Graphics to p5.Image
return rect.get();
}
function getCircleMask(w, h, cx, cy, cs, opacity){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0, opacity);
mask.noStroke();
mask.fill(255);
mask.circle(cx, cy, cs);
// convert p5.Graphics to p5.Image
return mask.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
您可以对其余形状应用相同的逻辑:
function setup() {
createCanvas(1620, 590);
let compWidth = 500;
let compHeight = 250;
let compSpacing= 30;
let lineWeight = 1.5;
let lineSpacing = 12;
const BLUE = color('#005398');
const YELLOW = color('#f9db44');
const RED = color('#dc1215');
// yellow square
circleMask = getCircleMask(compWidth, compHeight, compWidth * 0.5, compHeight * 0.5, 210);
redCircle = getComposition(compWidth, compHeight, RED,
BLUE,
YELLOW,
lineSpacing, lineWeight, circleMask);
// red box
boxMask = getRectMask(compWidth, compHeight, (compWidth - 100) * 0.5, 20, 100, 210);
redBox = getComposition(compWidth, compHeight, RED,
YELLOW,
BLUE,
lineSpacing, lineWeight, boxMask);
// yellow square
squareMask = getRectMask(compWidth, compHeight, 144, 20, 210, 210);
yellowSquare = getComposition(compWidth, compHeight, YELLOW,
RED,
BLUE,
lineSpacing, lineWeight, squareMask);
// yellow trapeze
trapezeMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 115, 25,
150 + 220, 220, 150, 220);
yellowTrapeze = getComposition(compWidth, compHeight, YELLOW,
BLUE,
RED,
lineSpacing, lineWeight, trapezeMask);
// blue triangle
triangleMask = getTriangleMask(compWidth, compHeight, compWidth * 0.5, 25,
150 + 220, 220, 150, 220);
blueTriangle = getComposition(compWidth, compHeight, BLUE,
YELLOW,
RED,
lineSpacing, lineWeight, triangleMask);
// blue parallelogram
parallelogramMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 145, 25,
150 + 145, 220, 150, 220);
blueParallelogram = getComposition(compWidth, compHeight, BLUE,
RED,
YELLOW,
lineSpacing, lineWeight, parallelogramMask);
// render compositions
image(redCircle, compSpacing, compSpacing);
image(redBox, compSpacing, compSpacing + (compHeight + compSpacing));
image(yellowSquare, compSpacing + (compWidth + compSpacing), compSpacing);
image(yellowTrapeze, compSpacing + (compWidth + compSpacing), compSpacing + (compHeight + compSpacing));
image(blueTriangle, compSpacing + (compWidth + compSpacing) * 2, compSpacing);
image(blueParallelogram, compSpacing + (compWidth + compSpacing) * 2, compSpacing + (compHeight + compSpacing));
}
function getComposition(w, h, bgFill, bgStroke, fgStroke, spacing, strokeWidth, mask){
let comp = createGraphics(w, h);
bg = getLinesRect(w, h, bgFill, bgStroke, spacing, strokeWidth, true);
fg = getLinesRect(w, h, bgFill, fgStroke, spacing, strokeWidth, false);
// apply mask
fg.mask(mask);
// render to final output
comp.image(bg, 0, 0);
comp.image(fg, 0, 0);
return comp;
}
function getRectMask(w, h, rx, ry, rw, rh){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.rect(rx, ry, rw, rh);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getCircleMask(w, h, cx, cy, cs){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.circle(cx, cy, cs);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getQuadMask(w, h, x1, y1, x2, y2, x3, y3, x4, y4){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.quad(x1, y1, x2, y2, x3, y3, x4, y4);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getTriangleMask(w, h, x1, y1, x2, y2, x3, y3){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.triangle(x1, y1, x2, y2, x3, y3);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
let rect = createGraphics(w, h);
rect.background(bg);
rect.stroke(fg);
rect.strokeWeight(strokeWidth);
if(isHorizontal){
for(let y = 0 ; y < h; y += spacing){
rect.line(0, y + strokeWidth, w, y + strokeWidth);
}
}else{
for(let x = 0 ; x < w; x += spacing){
rect.line(x + strokeWidth, 0, x + strokeWidth, h);
}
}
// convert from p5.Graphics to p5.Image
return rect.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
可能矩形和三角形都可以使用 getQuadMask()
充分利用坐标来绘制。
请注意,我只是观察了一些形状,所以它们不会是完美的,但应该很容易调整。请记住,遮罩的放置会影响垂直线的对齐方式。
可能还有其他方法可以获得相同的视觉效果。
例如,将 texture()
和 textureWrap(REPEAT)
与 beginShape()
/endShape()
一起使用,为每条线使用像素并在更改方向和颜色之前检查交叉点等
在生成绘图线方面,我会从水平线开始,对凸多边形进行线相交,以确定停止水平线和开始垂直线的位置。 @AgniusVasiliauskas 的回答(+1)适合这种方法。
Freya Holmér 对测试有很好的视觉解释。
我正在努力制作这个https://massmoca.org/event/walldrawing340/
在 Javascript 代码中,使用 p5.js,但我不知道如何用线条填充这些形状。有没有其他的可能性,比如把 canvas 做成圆形之类的,或者我只需要分别做每个形状?
目前我是一个形状一个形状地做,但是做三角形和梯形还是比较粗糙的...
var sketch = function (p) {
with(p) {
let h,
w,
space;
p.setup = function() {
createCanvas(900, 400);
h = height / 2;
w = width / 3;
space = 10;
noLoop();
};
p.draw = function() {
drawBackground('red', 'blue', 0, 0);
shape('Circle', 'red', 'blue', 0, 0);
drawBackground('yellow', 'red', w, 0);
shape('Square', 'yellow', 'red', w, 0);
drawBackground('blue', 'yellow', 2 * w, 0);
shape('Triangle', 'blue', 'red', 2 * w, 0)
drawBackground('red', 'yellow', 0, h);
shape('Rectangle', 'red', 'blue', 0, h)
drawBackground('yellow', 'blue', w, h);
shape('Trapezoid', 'yellow', 'red', w, h);
drawBackground('blue', 'red', 2 * w, h);
};
function drawBackground(bColor, lColor, x, y) {
fill(bColor)
noStroke();
rect(x, y, w, h)
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < h / space; i++) {
line(0 + x, i * space + y + 10, w + x, i * space + y + 10);
}
}
function shape(shape, bColor, lColor, x, y) {
fill(bColor)
noStroke();
let w1;
switch (shape) {
case 'Circle':
circle(x + w / 2, y + h / 2, h - space * 6);
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < w / space; i++) {
for (let j = 0; j < h; j++) {
pX = i * space + x;
pY = 0 + y + j;
if (pow(x + w / 2 - pX, 2)
+ pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
point(pX, pY);
}
}
}
break;
case 'Square':
w1 = w - (h - space * 6);
rect(x + w1 / 2, y + space * 3, h - space * 6, h - space * 6);
stroke(lColor);
strokeWeight(1);
for (let i = 0; i < 15; i++) {
for (let j = 0; j < h - space * 6; j++) {
point(x + w1 / 2 + i * space, y + space * 3 + j)
}
}
break;
case 'Triangle':
w1 = w - (h - space * 6);
triangle(x + w1 / 2, h - space * 3 + y, x + w / 2, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
for (let i = 0; i < w / space; i++) {
for (let j = 0; j < h; j++) {
pX = i * space + x;
pY = 0 + y + j;
if (pow(x + w / 2 - pX, 2)
+ pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
point(pX, pY);
}
}
}
break;
case 'Rectangle':
w1 = w - (h - space * 6) / 2;
rect(x + w1 / 2, y + space * 3, (h - space * 6) / 2, h - space * 6)
break;
case 'Trapezoid':
w1 = w - (h - space * 6);
quad(x + w1 / 2, h - space * 3 + y, x + w1 / 2 + (h - space * 6) / 4, y + space * 3, x + w1 / 4 + h - space * 6, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
break;
case 'Parallelogram':
w1 = w - (h - space * 6);
quad(x + w1 / 4, h - space * 3 + y, x + w1 / 2, y + space * 3, x + w1 / 2 + h - space * 6, y + space * 3, x + w1 / 4 + h - space * 6, h - space * 3 + y)
break;
break;
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
没有消息,一切正常,我只是想知道我是否必须做这么多艰巨的工作...
我有办法做一些形状,但我不确定其他形状。一种方法是,如果您知道形状轮廓上的每个点的位置,您可以只使用 for 循环并使用 line 或 rect 函数连接顶部和底部的所有其他点。对于正方形和平行四边形这样的形状,这将相对容易,但我不确定可以使用什么函数来获得圆形或梯形的点。
你需要线性代数的东西,基本上注意垂直线 starting/ending Y
坐标如何相对于线的 X
坐标变化。当然,在你得到可用的东西之前,还要进行大量的试验。像 :
var w = 600
h = 600
sp = 15
var slides = [fcircle, fsquare, ftriangle, ftrapezoid, fparallelogram];
var active = 0;
var ms;
function blines(){
stroke(0);
for (var i=0; i < h; i+=sp) {
line(0,i,w,i);
}
}
function vertlines(calcline) {
for (var x=w/2-w/4+sp; x < w/2+w/4; x+=sp) {
var pnts = calcline(x);
line(pnts[0],pnts[1],pnts[2],pnts[3]);
}
}
function fcircle() {
// cut background
noStroke();
circle(w/2, h/2, w/2);
stroke('red');
// draw figure lines
let calc = function (x){
var sx = x-w/2;
var sy = h/2;
var ey = h/2;
sy += 137*sin(2.5+x/135);
ey -= 137*sin(2.5+x/135);
return [x,sy,x,ey];
}
vertlines(calc);
}
function fsquare() {
// cut background
noStroke();
quad(w/2-w/4, h/2-h/4, w/2+w/4, h/2-h/4,
w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
return [x,h/2-h/4,x,h/2+h/4];
}
vertlines(calc);
}
function ftriangle() {
// cut background
noStroke();
quad(w/2, h/2-h/4, w/2+w/4, h/2+h/4,
w/2-w/4, h/2+h/4, w/2, h/2-h/4);
stroke('red');
// draw figure lines
let calc = function (x){
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.3*inpx*log(inpx)-220);
return [x,ys,x,h/2+h/4];
}
vertlines(calc);
}
function ftrapezoid() {
// cut background
noStroke();
quad(w/2-w/10, h/2-h/4, w/2+w/10, h/2-h/4,
w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.55*inpx*log(inpx)-420);
if (x >= w/2-w/10 && x <= w/2+w/10) {
ys=h/2-h/4;
}
return [x,ys,x,h/2+h/4];
}
vertlines(calc);
}
function fparallelogram() {
// cut background
noStroke();
quad(w/2-w/10, h/2-h/4, w/2+w/7, h/2-h/4,
w/2, h/2+h/4, w/2-w/4, h/2+h/4);
stroke('red');
// draw figure lines
let calc = function (x){
// guard condition
if (x > w/2+w/7)
return [0,0,0,0];
var inpx = x > w/2 ? w-x : x;
var ys = h/2+h/4;
ys += -(0.55*inpx*log(inpx)-420);
var ye=h/2+h/4
if (x >= w/2-w/10) {
ys=h/2-h/4;
}
if (x > w/2) {
ye = h/2+h/4;
ye += 0.50*inpx*log(inpx)-870;
}
return [x,ys,x,ye];
}
vertlines(calc);
}
function setup() {
ms = millis();
createCanvas(w, h);
}
function draw() {
if (millis() - ms > 2000) {
ms = millis();
active++;
if (active > slides.length-1)
active = 0;
}
background('#D6EAF8');
fill('#D6EAF8');
blines();
slides[active]();
}
如果您不需要实际的线坐标(例如用于绘图),我会充分利用 createGraphics()
to easily render shapes and lines into (taking advantage of the fact that get()
returns a p5.Image
) and p5.Image's mask()
函数。
这是一个基本示例:
function setup() {
createCanvas(600, 300);
let w = 300;
let h = 150;
let spacing = 12;
let strokeWidth = 1;
const BLUE = color('#005398');
const YELLOW = color('#f9db44');
const RED = color('#dc1215');
bg = getLinesRect(w, h, RED, BLUE, spacing, strokeWidth, true);
fg = getLinesRect(w, h, RED, YELLOW, spacing, strokeWidth, false);
mask = getCircleMask(w, h, w * 0.5, h * 0.5, 100, 0);
image(bg, 0, 0);
image(fg, w, 0);
// render opaque mask (for visualisation only), mask() requires alpha channel
image(getCircleMask(w, h, w * 0.5, h * 0.5, 100, 255),0, h);
// apply mask
fg.mask(mask);
// render bg + masked fg
image(bg, w, h);
image(fg, w, h);
// text labels
noStroke();
fill(255);
text("bg layer", 9, 12);
text("fg layer", w + 9, 12);
text("mask", 9, h + 12);
text("bg + masked fg", w + 9, h + 12);
}
function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
let rect = createGraphics(w, h);
rect.background(bg);
rect.stroke(fg);
rect.strokeWeight(strokeWidth);
if(isHorizontal){
for(let y = 0 ; y < h; y += spacing){
rect.line(0, y + strokeWidth, w, y + strokeWidth);
}
}else{
for(let x = 0 ; x < w; x += spacing){
rect.line(x + strokeWidth, 0, x + strokeWidth, h);
}
}
// convert from p5.Graphics to p5.Image
return rect.get();
}
function getCircleMask(w, h, cx, cy, cs, opacity){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0, opacity);
mask.noStroke();
mask.fill(255);
mask.circle(cx, cy, cs);
// convert p5.Graphics to p5.Image
return mask.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
您可以对其余形状应用相同的逻辑:
function setup() {
createCanvas(1620, 590);
let compWidth = 500;
let compHeight = 250;
let compSpacing= 30;
let lineWeight = 1.5;
let lineSpacing = 12;
const BLUE = color('#005398');
const YELLOW = color('#f9db44');
const RED = color('#dc1215');
// yellow square
circleMask = getCircleMask(compWidth, compHeight, compWidth * 0.5, compHeight * 0.5, 210);
redCircle = getComposition(compWidth, compHeight, RED,
BLUE,
YELLOW,
lineSpacing, lineWeight, circleMask);
// red box
boxMask = getRectMask(compWidth, compHeight, (compWidth - 100) * 0.5, 20, 100, 210);
redBox = getComposition(compWidth, compHeight, RED,
YELLOW,
BLUE,
lineSpacing, lineWeight, boxMask);
// yellow square
squareMask = getRectMask(compWidth, compHeight, 144, 20, 210, 210);
yellowSquare = getComposition(compWidth, compHeight, YELLOW,
RED,
BLUE,
lineSpacing, lineWeight, squareMask);
// yellow trapeze
trapezeMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 115, 25,
150 + 220, 220, 150, 220);
yellowTrapeze = getComposition(compWidth, compHeight, YELLOW,
BLUE,
RED,
lineSpacing, lineWeight, trapezeMask);
// blue triangle
triangleMask = getTriangleMask(compWidth, compHeight, compWidth * 0.5, 25,
150 + 220, 220, 150, 220);
blueTriangle = getComposition(compWidth, compHeight, BLUE,
YELLOW,
RED,
lineSpacing, lineWeight, triangleMask);
// blue parallelogram
parallelogramMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 145, 25,
150 + 145, 220, 150, 220);
blueParallelogram = getComposition(compWidth, compHeight, BLUE,
RED,
YELLOW,
lineSpacing, lineWeight, parallelogramMask);
// render compositions
image(redCircle, compSpacing, compSpacing);
image(redBox, compSpacing, compSpacing + (compHeight + compSpacing));
image(yellowSquare, compSpacing + (compWidth + compSpacing), compSpacing);
image(yellowTrapeze, compSpacing + (compWidth + compSpacing), compSpacing + (compHeight + compSpacing));
image(blueTriangle, compSpacing + (compWidth + compSpacing) * 2, compSpacing);
image(blueParallelogram, compSpacing + (compWidth + compSpacing) * 2, compSpacing + (compHeight + compSpacing));
}
function getComposition(w, h, bgFill, bgStroke, fgStroke, spacing, strokeWidth, mask){
let comp = createGraphics(w, h);
bg = getLinesRect(w, h, bgFill, bgStroke, spacing, strokeWidth, true);
fg = getLinesRect(w, h, bgFill, fgStroke, spacing, strokeWidth, false);
// apply mask
fg.mask(mask);
// render to final output
comp.image(bg, 0, 0);
comp.image(fg, 0, 0);
return comp;
}
function getRectMask(w, h, rx, ry, rw, rh){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.rect(rx, ry, rw, rh);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getCircleMask(w, h, cx, cy, cs){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.circle(cx, cy, cs);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getQuadMask(w, h, x1, y1, x2, y2, x3, y3, x4, y4){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.quad(x1, y1, x2, y2, x3, y3, x4, y4);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getTriangleMask(w, h, x1, y1, x2, y2, x3, y3){
let mask = createGraphics(w, h);
// make background transparent (alpha is used for masking)
mask.background(0,0);
mask.noStroke();
mask.fill(255);
mask.triangle(x1, y1, x2, y2, x3, y3);
// convert p5.Graphics to p5.Image
return mask.get();
}
function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
let rect = createGraphics(w, h);
rect.background(bg);
rect.stroke(fg);
rect.strokeWeight(strokeWidth);
if(isHorizontal){
for(let y = 0 ; y < h; y += spacing){
rect.line(0, y + strokeWidth, w, y + strokeWidth);
}
}else{
for(let x = 0 ; x < w; x += spacing){
rect.line(x + strokeWidth, 0, x + strokeWidth, h);
}
}
// convert from p5.Graphics to p5.Image
return rect.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
可能矩形和三角形都可以使用 getQuadMask()
充分利用坐标来绘制。
请注意,我只是观察了一些形状,所以它们不会是完美的,但应该很容易调整。请记住,遮罩的放置会影响垂直线的对齐方式。
可能还有其他方法可以获得相同的视觉效果。
例如,将 texture()
和 textureWrap(REPEAT)
与 beginShape()
/endShape()
一起使用,为每条线使用像素并在更改方向和颜色之前检查交叉点等
在生成绘图线方面,我会从水平线开始,对凸多边形进行线相交,以确定停止水平线和开始垂直线的位置。 @AgniusVasiliauskas 的回答(+1)适合这种方法。
Freya Holmér 对测试有很好的视觉解释。