6

我正在尝试使用 Fabric JS 创建一种效果,其中字母看起来像这样在毛衣上“刺绣”:

我可以通过使用这个动作在 Photoshop 中实现这个效果。

我把它变成 a 的想法<canvas>是从 Photoshop 中渲染出每个刺绣字母的 png。然后,我将根据用户键入的内容将每个字母放在画布上。

然而,这种方法不会有正确的字距调整。

为了解决这个问题,我尝试使用相同的字体在 Fabric 中写出文本,然后将每个刺绣 png 覆盖在它要替换的字母之上(然后隐藏文本本身)。

这是我呈现文本的方式:

window.chest_text = new fabric.IText("NYC", {
      fill: '#000',
      fontSize: 12,
      left: 210,
      top: 100,
      fontFamily: 'Graphik',
      fontWeight: 500,
      lineHeight: 1,
      originX: 'center',
    });

然后是我渲染刺绣字母的方法:

  var n_url = 'https://res.cloudinary.com/tricot/image/upload/v1598820746/tmp/n-embroidery-test.png'
  var y_url = 'https://res.cloudinary.com/tricot/image/upload/v1598820745/tmp/y-embroidery-test.png'
  var c_url = 'https://res.cloudinary.com/tricot/image/upload/v1598820745/tmp/c-embroidery-test.png'
  
  fabric.Image.fromURL(n_url, function(img) {
    img.set({
      left: Math.round(window.chest_text.aCoords.bl.x),
      top: window.chest_text.top
    })
    
    img.scaleToHeight(Math.floor(window.chest_text.__charBounds[0][0].height / 1.13), true)
    
    canvas.add(img);
  })
  
  fabric.Image.fromURL(y_url, function(img) {
    img.set({
      left: Math.round(window.chest_text.aCoords.bl.x + window.chest_text.__charBounds[0][1].left),
      top: window.chest_text.top
    })
    
    img.scaleToHeight(Math.floor(window.chest_text.__charBounds[0][1].height / 1.13), true)
    
    canvas.add(img);
  })
  
  fabric.Image.fromURL(c_url, function(img) {
    img.set({
      left: Math.round(window.chest_text.aCoords.bl.x + window.chest_text.__charBounds[0][2].left),
      top: window.chest_text.top
    })
    
    img.scaleToHeight(Math.floor(window.chest_text.__charBounds[0][2].height / 1.13), true)
    
    canvas.add(img);
  })
  
  window.chest_text.opacity = 0.5
  
  window.canvas.renderAll()

但是,我无法让刺绣字母完全覆盖常规文本(即使它是相同的字体):

在此处输入图像描述

我怎样才能做到这一点?有没有更好的方法让字距调整正常工作?

4

2 回答 2

4

使用划线作为缝合线并 ctx.globalCompositeOperation = "source-atop" 仅在文本内绘制。改变笔触宽度以从字体内部构建针迹。

不幸的是,线划线间距仅适用于笔划中心,因此该方法适用于某些字符但并非全部。

可以改进,因为没有努力使针迹变圆(每针的高光和阴影颜色),但是由于无法控制线连接处的针迹定位,我看不到进一步细化它的意义。

适用于任何字体。

请参阅片段以获取演示和代码。

function stitchIt(text, stitchLen, stitchOffset, threadThickness, size, font, col1, col2 , shadowColor, offset, blur) {
    const can = document.createElement("canvas");
    const ctx = can.getContext("2d");
    ctx.font = size + "px "+font;
    const width = ctx.measureText(text).width;
    can.width = width;
    can.height = size;
    ctx.font = size + "px "+font;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.globalCompositeOperation = "source-over";
    ctx.lineCap = "butt";
    ctx.lineJoin = "bevel";
    ctx.fillStyle = col2;
    ctx.setTransform(1,0,0,1,width / 2, size / 2);
    ctx.fillText(text, 0, 0);
    ctx.setLineDash([stitchLen, stitchLen]);
    var w = size, off = 0;
    ctx.globalCompositeOperation = "source-atop"
    while (w > 0) {
        ctx.lineWidth = w;
        ctx.strokeStyle = col1;
        ctx.lineDashOffset = off; 
        ctx.strokeText(text, 0, 0);
        if (w > threadThickness) {
            w -= threadThickness / 2;
            ctx.lineWidth = w;
            ctx.lineDashOffset = off + stitchLen; 
            ctx.strokeStyle = col2;
            ctx.strokeText(text, 0, 0);
            off += stitchLen * stitchOffset;
            w -= threadThickness / 2;
        } else {
            break;
        }
    }
    ctx.globalCompositeOperation = "destination-out";
    ctx.globalAlpha = 0.5;
    ctx.strokeStyle = col2;
    ctx.lineWidth = threadThickness / 2;
    ctx.lineDashOffset = off + stitchLen; 
    ctx.strokeText(text, 0, 0);
    ctx.globalCompositeOperation = "destination-over";
    ctx.save();
    ctx.shadowColor = "#000";
    ctx.shadowOffsetX = offset;
    ctx.shadowOffsetY = offset;
    ctx.shadowBlur = blur;
    ctx.fillText(text, 0, 0);
    ctx.restore();    
    ctx.globalCompositeOperation = "source-over";
    return can;
}
ctx = canvas.getContext("2d");
textEl.addEventListener("input", update);
sLenEl.addEventListener("change", update);
sOffEl.addEventListener("change", update);
sThreadEl.addEventListener("change", update);
sFontEl.addEventListener("change", update);
colEl.addEventListener("click", () => {
    color = colors[colIdx++ % colors.length];
    update();
});

// update debounces render
var tHdl;
function update() {
    clearTimeout(tHdl);
    tHdl = setTimeout(draw, 200);
}
const colors=[["#DDD","#888"],["#FFF","#666"],["#F88","#338"],["#8D8","#333"]];
var colIdx = 0;
var color = colors[colIdx++];
stitchIt("STITCH",5, 1.4, 4, 160,"Arial Black","#DDD","#888","#0004" , 4, 5);
function draw() {
    ctx.clearRect(0,0,1500,180);
    if (textEl.value) {
        const image = stitchIt(
            textEl.value,
            Number(sLenEl.value), 
            sOffEl.value / 100 + 0.8, 
            Number(sThreadEl.value), 
            Number(sFontEl.value),
            "Arial Black",
            color[0],
            color[1],
            "#0004" , 
            4, 
            5
        );
        ctx.drawImage(image,0,90-image.height / 2); 
   }
}
draw();
canvas {
    background: #49b;
    border: 2px solid #258;
    position: absolute;
    top: 0px;
    left: 230px;
}
<div>
    <input id="textEl" type="text" value="STITCH"></input><br>
    <button id="colEl">change Color</button><br>
    
    <input id="sLenEl" type="range" min="2" max="16" value="5"><label for="sLenEl">Stitch length</label><br>
    <input id="sOffEl" type="range" min="0" max="100" value="50"><label for="sOffEl">Stitch offset</label><br>
    <input id="sThreadEl" type="range" min="2" max="10" value="4"><label for="sThreadEl">Thread size</label><br>
    <input id="sFontEl" type="range" min="20" max="180" value="140"><label for="sFontEl">Font size</label><br>
</div>
<canvas id="canvas" width="1500" height="180"></canvas>

于 2020-09-10T08:32:21.473 回答
1

如果您设法找到字体,则可以使用字体。

有一种字体可能更接近您的需要,NCD Embroidery,但您必须联系设计师以获得许可证。

您甚至可以在其中一个 Photoshop 库中找到该字体(我对 Photoshop 不太熟悉,所以这只是一个猜测)

在 .css 文件中注册字体。

@font-face {
    font-family:'stitchfont';
    src:url('./fonts/fs-mom.ttf') format('truetype');
}
@font-face {
    font-family:'pencilfont';
    src:url('./fonts/fs-ariapenciroman.ttf') format('truetype');
}
@font-face {
    font-family:'fabricon';
    src:url('./fonts/stf-fabricon-cross-section.ttf') format('truetype');
}

.stitch{
    font-family: 'stitchfont';
}
.pencil {
    font-family: 'pencilfont';
}

.fabri {
    font-family: 'fabricon';
}

和代码:

<body>    
    <canvas id="stitchText" width="400" height="200"></canvas>
    <canvas id="pencilText" width="400" height="200"></canvas>
    <canvas id="fabricText" width="400" height="200"></canvas>

    <div class="stitch" style="visibility: hidden;">if the font is not used it doesn't seem to load consistently</div>
    <div class="pencil" style="visibility: hidden;">so assign the font to any element</div>
    <div class="fabri" style="visibility: hidden;">set the style visibility to hidden , dont use the html hidden tag</div>

    <script src="./lib/fabric.js/fabric.js"></script>
    <script>
        let stitchCanvas = new fabric.Canvas("stitchText")
        let stitchText = new fabric.Text("NYC", {
            fill: '#EEE',
            fontFamily: 'stitchfont',
            fontSize: 60,
            left: 190,
            top: 90,
            originX: 'center',
            originY: 'center'
        });
        stitchCanvas.add(stitchText);
        stitchCanvas.setBackgroundImage("images/embroid.png", stitchCanvas.renderAll.bind(stitchCanvas), { opacity: 0.8, scaleX: 0.28, scaleY: 0.28 });

        let pencilCanvas = new fabric.Canvas("pencilText")
        let pencilText = new fabric.Text("NYC", {
            fill: '#EEE',
            fontFamily: 'pencilfont',
            fontSize: 80,
            left: 190,
            top: 85,
            originX: 'center',
            originY: 'center'
        });
        pencilCanvas.add(pencilText);
        pencilCanvas.setBackgroundImage("images/embroid.png", pencilCanvas.renderAll.bind(pencilCanvas), { opacity: 0.8, scaleX: 0.28, scaleY: 0.28 });

        let fabricCanvas = new fabric.Canvas("fabricText")
        let fabricText = new fabric.Text("NYC", {
            fill: '#EEE',
            fontFamily: 'fabricon',
            fontSize: 40,
            left: 195,
            top: 85,
            originX: 'center',
            originY: 'center'
        });
        fabricCanvas.add(fabricText);
        fabricCanvas.setBackgroundImage("images/embroid.png", fabricCanvas.renderAll.bind(fabricCanvas), { opacity: 0.8, scaleX: 0.28, scaleY: 0.28 });

    </script>
</body>

字体的结果:

fs妈妈 执照

fmMom字体

FS Ariape​​nciroman 许可证

铅笔字体

面料 许可证

布料字体

于 2020-09-10T10:08:00.277 回答