使用Fabric JS模拟字距调整为位图字体

如何解决使用Fabric JS模拟字距调整为位图字体

我正在尝试使用Fabric JS创建一种效果,其中字母似乎在这样的毛衣上被“绣了”:

我可以使用this action在Photoshop中实现这种效果。

我将其放入<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()

但是我无法让绣花字母准确地覆盖普通文本(即使它是相同的字体):

enter image description here

我该如何实现?有更好的方法使字距调整正常工作吗?

解决方法

使用破折号作为针迹和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,1,width / 2,size / 2);
    ctx.fillText(text,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);
        if (w > threadThickness) {
            w -= threadThickness / 2;
            ctx.lineWidth = w;
            ctx.lineDashOffset = off + stitchLen; 
            ctx.strokeStyle = col2;
            ctx.strokeText(text,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);
    ctx.globalCompositeOperation = "destination-over";
    ctx.save();
    ctx.shadowColor = "#000";
    ctx.shadowOffsetX = offset;
    ctx.shadowOffsetY = offset;
    ctx.shadowBlur = blur;
    ctx.fillText(text,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",5);
function draw() {
    ctx.clearRect(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),color[0],color[1],5
        );
        ctx.drawImage(image,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>

,

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

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",fontFamily: 'pencilfont',fontSize: 80,top: 85,originY: 'center'
        });
        pencilCanvas.add(pencilText);
        pencilCanvas.setBackgroundImage("images/embroid.png",pencilCanvas.renderAll.bind(pencilCanvas),scaleY: 0.28 });

        let fabricCanvas = new fabric.Canvas("fabricText")
        let fabricText = new fabric.Text("NYC",fontFamily: 'fabricon',fontSize: 40,left: 195,originY: 'center'
        });
        fabricCanvas.add(fabricText);
        fabricCanvas.setBackgroundImage("images/embroid.png",fabricCanvas.renderAll.bind(fabricCanvas),scaleY: 0.28 });

    </script>
</body>

字体的结果:

fs Mom license

fmMomFont

FS Ariapenciroman license

pencilFont

Fabric license

fabricFont

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-