如何在html画布上绘制人字形图案

时间:2017-05-20 01:00:41

标签: javascript html5 canvas svg fabricjs

我必须在画布上绘制人字形图案并填充图像

有些人请帮助我,我是画布2d画的新手。 我需要绘制具有交叉图案的混合图块(人字形)

  var canvas = this.__canvas = new fabric.Canvas('canvas');
  var canvas_objects = canvas._objects;
// create a rectangle with a fill and a different color stroke
var left = 150;
var top = 150;
var x=20;
var y=40;

var rect = new fabric.Rect({
   left: left,
   top: top,
   width: x,
   height: y,
   angle:45,
   fill: 'rgba(255,127,39,1)',
   stroke: 'rgba(34,177,76,1)',
   strokeWidth:0,
    originX:'right',
        originY:'top',
        centeredRotation: false
});
canvas.add(rect);
for(var i=0;i<15;i++){
    var rectangle = fabric.util.object.clone(getLastobject());
 if(i%2==0){
    rectangle.left = rectangle.oCoords.tr.x;
    rectangle.top = rectangle.oCoords.tr.y;
    rectangle.originX='right';
        rectangle.originY='top';
    rectangle.angle =-45;
  }else{

      fabric.log('rectangle: ', rectangle.toJSON());
            rectangle.left = rectangle.oCoords.tl.x;
      rectangle.top = rectangle.oCoords.tl.y;
      fabric.log('rectangle: ', rectangle.toJSON());
        rectangle.originX='left';
            rectangle.originY='top';
        rectangle.angle =45;

  }
  //rectangle.angle -90;
  canvas.add(rectangle);
}
fabric.log('rectangle: ', canvas.toJSON());
canvas.renderAll();    


function getLastobject(){
    var last = null;
    if(canvas_objects.length !== 0){
        last = canvas_objects[canvas_objects.length -1]; //Get last object   
    }    
  return last;
}

如何使用svg或2d,3d方法在画布中绘制此模式。如果有任何第三方库也对我有好处。

我不知道从哪里开始以及如何绘制这种复杂的模式。

有人请帮我在画布上用动态颜色的矩形填充绘制这个图案。

这是我需要的示例输出。 herringbone pattern

我使用fabric.js库尝试了类似的东西,这里是我的JSFiddle

2 个答案:

答案 0 :(得分:4)

Trippy迪斯科地板

要获得图案,您需要绘制矩形,每个水平平铺一个空格,每行向左或向右,垂直矩形相同。

矩形的宽度为2时间高度。

绘制图案很简单。

旋转很容易,更难的部分是找到绘制旋转瓷砖的位置。

为此,我创建了一个旋转的逆矩阵(它反转了一个旋转)。然后我将旋转应用到画布的{4}角,0,0 width,0width,height,这使我在旋转空间中的4个点位于画布。

当我从左到右从上到下绘制瓷砖时,我发现左上角的最小角落,右下角的最大角落,将其展开一点,所以我不会错过任何像素并绘制瓷砖转换设置了旋转。

由于我无法锻炼你想要的角度,它会在任何角度绘制它。 On动画,另一个是顺时针60deg。

警告演示包含闪烁内容。

更新闪光就是在那里,所以做了一些改变,现在颜色是一个更令人愉悦的混合,并具有固定的绝对位置,并已将瓷砖原点绑定到鼠标位置,单击鼠标按钮也会循环显示某些尺寸。

&#13;
&#13;
0,height
&#13;
const ctx = canvas.getContext("2d");
const colours = []
for(let i = 0; i < 1; i += 1/80){
    colours.push(`hsl(${Math.floor(i * 360)},${Math.floor((Math.sin(i * Math.PI *4)+1) * 50)}%,${Math.floor(Math.sin(i * Math.PI *8)* 25 + 50)}%)`)
}
const sizes = [0.04,0.08,0.1,0.2];
var currentSize = 0;
const origin = {x : canvas.width / 2, y : canvas.height / 2};
var size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
function drawPattern(size,origin,ang){
    const xAx = Math.cos(ang);  // define the direction of xAxis
    const xAy = Math.sin(ang);    
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.setTransform(xAx,xAy,-xAy,xAx,origin.x,origin.y);
    function getExtent(xAx,xAy,origin){
        const im = [1,0,0,1]; // inverse matrix
        const dot = xAx *  xAx + xAy * xAy;
        im[0] =  xAx / dot;
        im[1] = -xAy / dot;
        im[2] = xAy / dot;
        im[3] = xAx / dot;
        const toWorld = (x,y) => {
            var point = {};
            var xx = x - origin.x;     
            var yy = y - origin.y;     
            point.x = xx * im[0] + yy * im[2]; 
            point.y = xx * im[1] + yy * im[3];
            return point;
        }
        return [
            toWorld(0,0),
            toWorld(canvas.width,0),
            toWorld(canvas.width,canvas.height),
            toWorld(0,canvas.height),
        ]
    }
    const corners = getExtent(xAx,xAy,origin);
    var startX = Math.min(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
    var endX = Math.max(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
    var startY = Math.min(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
    var endY = Math.max(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
    
    startX = Math.floor(startX / size) - 2;
    endX = Math.floor(endX / size) + 2;
    startY = Math.floor(startY / size) - 2;
    endY = Math.floor(endY / size) + 2;
                
    // draw the pattern        
    ctx.lineWidth = size * 0.1;
    ctx.lineJoin = "round";
    ctx.strokeStyle = "black";
    var colourIndex = 0;
    for(var y = startY; y <endY; y+=1){
        for(var x = startX; x <endX; x+=1){
            if((x + y) % 4 === 0){
                colourIndex = Math.floor(Math.abs(Math.sin(x)*size  + Math.sin(y) * 20));
                ctx.fillStyle = colours[(colourIndex++)% colours.length];
                ctx.fillRect(x * size,y * size,size * 2,size);
                ctx.strokeRect(x * size,y * size,size * 2,size);
                x += 2;
                ctx.fillStyle = colours[(colourIndex++)% colours.length];
                ctx.fillRect(x * size,y * size, size, size * 2);
                ctx.strokeRect(x * size,y * size, size, size * 2);
                x += 1;
            }
    
        }
    }
}

// Animate it all 
var update = true; // flag to indecate something needs updating
function mainLoop(time){
    // if window size has changed update canvas to new size
    if(canvas.width !== innerWidth || canvas.height !== innerHeight || update){
        canvas.width = innerWidth;
        canvas.height = innerHeight    
        origin.x = canvas.width / 2;
        origin.y = canvas.height / 2;
        size = Math.min(canvas.width, canvas.height) * sizes[currentSize % sizes.length];
        update = false;
    }
    if(mouse.buttonRaw !== 0){
        mouse.buttonRaw = 0;
        currentSize  += 1;
        update = true;
    }
    // draw the patter
    drawPattern(size,mouse,time/2000);
    requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);

mouse = (function () {
    function preventDefault(e) { e.preventDefault() }
    var m; // alias for mouse
    var mouse = {
        x : 0, y : 0, // mouse position
        buttonRaw : 0,
        over : false,                        // true if mouse over the element
        buttonOnMasks : [0b1, 0b10, 0b100],  // mouse button on masks
        buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
        bounds : null,
        eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover".split(","),
        event(e) {
            var t = e.type;
            m.bounds = m.element.getBoundingClientRect();
            m.x = e.pageX - m.bounds.left - scrollX;
            m.y = e.pageY - m.bounds.top - scrollY;
            if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
            else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
            else if (t === "mouseout") { m.over = false }
            else if (t === "mouseover") { m.over = true }
            e.preventDefault();
        },
        start(element) {
            if (m.element !== undefined) { m.remove() }
            m.element = element === undefined ? document : element;
            m.eventNames.forEach(name =>  document.addEventListener(name, mouse.event) );
            document.addEventListener("contextmenu", preventDefault, false);
        },
    }
    m = mouse;
    return mouse;
})();
mouse.start(canvas);
&#13;
canvas {
   position : absolute;
   top : 0px;
   left : 0px;
}
&#13;
&#13;
&#13;

60Deg的未动画版

&#13;
&#13;
<canvas id=canvas></canvas>
&#13;
        const ctx = canvas.getContext("2d");
        const colours = ["red","green","yellow","orange","blue","cyan","magenta"]
        const origin = {x : canvas.width / 2, y : canvas.height / 2};
        var size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
        function drawPattern(size,origin,ang){
            const xAx = Math.cos(ang);  // define the direction of xAxis
            const xAy = Math.sin(ang);    
            ctx.setTransform(1,0,0,1,0,0);
            ctx.clearRect(0,0,canvas.width,canvas.height);
            ctx.setTransform(xAx,xAy,-xAy,xAx,origin.x,origin.y);
            function getExtent(xAx,xAy,origin){
                const im = [1,0,0,1]; // inverse matrix
                const dot = xAx *  xAx + xAy * xAy;
                im[0] =  xAx / dot;
                im[1] = -xAy / dot;
                im[2] = xAy / dot;
                im[3] = xAx / dot;
                const toWorld = (x,y) => {
                    var point = {};
                    var xx = x - origin.x;     
                    var yy = y - origin.y;     
                    point.x = xx * im[0] + yy * im[2]; 
                    point.y = xx * im[1] + yy * im[3];
                    return point;
                }
                return [
                    toWorld(0,0),
                    toWorld(canvas.width,0),
                    toWorld(canvas.width,canvas.height),
                    toWorld(0,canvas.height),
                ]
            }
            const corners = getExtent(xAx,xAy,origin);
            var startX = Math.min(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
            var endX = Math.max(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
            var startY = Math.min(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
            var endY = Math.max(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
            
            startX = Math.floor(startX / size) - 4;
            endX = Math.floor(endX / size) + 4;
            startY = Math.floor(startY / size) - 4;
            endY = Math.floor(endY / size) + 4;
                        
            // draw the pattern        
            ctx.lineWidth = 5;
            ctx.lineJoin = "round";
            ctx.strokeStyle = "black";
            for(var y = startY; y <endY; y+=1){
                for(var x = startX; x <endX; x+=1){
                    ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
                    if((x + y) % 4 === 0){
                        ctx.fillRect(x * size,y * size,size * 2,size);
                        ctx.strokeRect(x * size,y * size,size * 2,size);
                        x += 2;
                        ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];        
                        ctx.fillRect(x * size,y * size, size, size * 2);
                        ctx.strokeRect(x * size,y * size, size, size * 2);
                        x += 1;
                    }
            
                }
            }
        }


        canvas.width = innerWidth;
        canvas.height = innerHeight    
        origin.x = canvas.width / 2;
        origin.y = canvas.height / 2;
        size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
        drawPattern(size,origin,Math.PI / 3);
&#13;
canvas {
   position : absolute;
   top : 0px;
   left : 0px;
}
&#13;
&#13;
&#13;

答案 1 :(得分:1)

解决这个问题的最佳方法是检查模式并分析其对称性及其重复方式。

你可以看几种方法。例如,您可以将图案旋转45度,使得图块是平直的正交矩形。但是,让我们看看它是怎么回事。我将假设您对45度瓷砖感到满意。

enter image description here

与瓷砖本身一样,事实证明该图案的比例为2:1。如果我们水平和垂直地重复这个模式,我们可以用完整的模式填充画布。

我们可以看到有五个图块与我们的图案块重叠。但是,当我们绘制每个模式块时,我们不需要全部绘制它们。我们可以利用块重复的事实,我们可以将一些块的绘制留给以后的行和列。

假设我们正在从左到右,从上到下绘制图案块。我们需要绘制哪些图块,至少要确保完全绘制此图案块(考虑到相邻的图案块)?

由于我们将从左上角(向右和向下移动)开始,我们需要绘制第2块。这是因为该块不会被块块绘制在我们之下,或在我们右边的街区。这同样适用于图块3。 事实证明,这两个是我们需要为每个模式块绘制的。当我们下面的图案块分别绘制它们的图块2和3时,将绘制图块1和图4。当我们东南方的图案块绘制瓷砖1时,将绘制图块。

我们只需要记住,我们可能需要在右侧绘制一个额外的列,并在底部绘制,以确保完全绘制行尾和模式块。< / p>

要解决的最后一件事是我们的模式块有多大。

让我们调用图块a和长边b的短边。我们知道b = 2 * a。我们可以使用毕达哥拉斯定理得出模式块的高度为:

h = sqrt(a^2 + a^2)
  = sqrt(2 * a^2)
  = sqrt(2) * a

我们可以看到的模式块的宽度为w = 2 * h

现在我们已经研究了如何绘制模式,让我们实现我们的算法。

&#13;
&#13;
const a = 60;
const b = 120;

const h = 50 * Math.sqrt(2);
const w = h * 2;
const h2 = h / 2;  // How far tile 1 sticks out to the left of the pattern block

// Set of colours for the tiles
const colours = ["red","cornsilk","black","limegreen","deepskyblue",
                 "mediumorchid", "lightgrey", "grey"]

const canvas = document.getElementById("herringbone");
const ctx = canvas.getContext("2d");

// Set a universal stroke colour and width
ctx.strokeStyle = "black";
ctx.lineWidth = 4;

// Loop through the pattern block rows
for (var y=0; y < (canvas.height + h); y+=h)
{
  // Loop through the pattern block columns
  for (var x=0; x < (canvas.width + w); x+=w)
  {
     // Draw tile "2"
     // I'm just going to draw a path for simplicity, rather than
     // worrying about drawing a rectangle with rotation and translates
     ctx.beginPath();
     ctx.moveTo(x - h2, y - h2);
     ctx.lineTo(x, y - h);
     ctx.lineTo(x + h, y);
     ctx.lineTo(x + h2, y + h2);
     ctx.closePath();
     ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
     ctx.fill();
     ctx.stroke();

     // Draw tile "3"
     ctx.beginPath();
     ctx.moveTo(x + h2, y + h2);
     ctx.lineTo(x + w - h2, y - h2);
     ctx.lineTo(x + w, y);
     ctx.lineTo(x + h, y + h);
     ctx.closePath();
     ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
     ctx.fill();
     ctx.stroke();
   }
}
&#13;
<canvas id="herringbone" width="500" height="400"></canvas>
&#13;
&#13;
&#13;