繁体   English   中英

为 canvas 上的活刻文字添加填充

[英]Add padding to live engraved text on canvas

我正在研究使用 canvas 叠加层在手镯上实时雕刻文本的解决方案。

我的小提琴在这里:

 var first = true; startIt(); function startIt() { canvasDiv = document.getElementById('canvasDiv'); canvasDiv.innerHTML = '<canvas id="layer0" width="400" height="400"></canvas>'; //for IE canvas = document.getElementById('layer0'); ctx = canvas.getContext('2d'); ctx.fillStyle = "green"; ctx.font = "20px Courier New"; curve = document.getElementById('curve'); curveText = document.getElementById('text'); $(curve).keyup(function(e) {changeCurve();}); $(curveText).keyup(function(e) {changeCurve();}); if (first) { changeCurve(); first = false; } } function changeCurve() { points = curve.value.split(','); if (points.length == 8) drawStack(); } function drawStack() { Ribbon = {maxChar: 50, startX: points[0], startY: points[1], control1X: points[2], control1Y: points[3], control2X: points[4], control2Y: points[5], endX: points[6], endY: points[7]}; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.beginPath(); ctx.moveTo(Ribbon.startX,Ribbon.startY); ctx.bezierCurveTo(Ribbon.control1X,Ribbon.control1Y, Ribbon.control2X,Ribbon.control2Y, Ribbon.endX,Ribbon.endY); ctx.stroke(); ctx.restore(); FillRibbon(curveText.value,Ribbon); } function FillRibbon(text,Ribbon) { var textCurve = []; var ribbon = text.substring(0,Ribbon.maxChar); var curveSample = 1000; xDist = 0; var i = 0; for (i = 0; i < curveSample; i++) { a = new bezier2(i/curveSample,Ribbon.startX,Ribbon.startY,Ribbon.control1X,Ribbon.control1Y,Ribbon.control2X,Ribbon.control2Y,Ribbon.endX,Ribbon.endY); b = new bezier2((i+1)/curveSample,Ribbon.startX,Ribbon.startY,Ribbon.control1X,Ribbon.control1Y,Ribbon.control2X,Ribbon.control2Y,Ribbon.endX,Ribbon.endY); c = new bezier(a,b); textCurve.push({bezier: a, curve: c.curve}); } letterPadding = ctx.measureText("").width / 4; w = ribbon.length; ww = Math.round(ctx.measureText(ribbon).width); totalPadding = (w-1) * letterPadding; totalLength = ww + totalPadding; p = 0; cDist = textCurve[curveSample-1].curve.cDist; z = (cDist / 2) - (totalLength / 2); for (i=0;i<curveSample;i++) { if (textCurve[i].curve.cDist >= z) { p = i; break; } } for (i = 0; i < w; i++) { ctx.save(); ctx.translate(textCurve[p].bezier.point.x,textCurve[p].bezier.point.y); ctx.rotate(textCurve[p].curve.rad); ctx.fillText(ribbon[i],0,0); ctx.restore(); x1 = ctx.measureText(ribbon[i]).width + letterPadding; x2 = 0; for (j=p;j<curveSample;j++) { x2 = x2 + textCurve[j].curve.dist; if (x2 >= x1) { p = j; break; } } } } //end FillRibon function bezier(b1, b2) { //Final stage which takes p, p+1 and calculates the rotation, distance on the path and accumulates the total distance this.rad = Math.atan(b1.point.mY/b1.point.mX); this.b2 = b2; this.b1 = b1; dx = (b2.x - b1.x); dx2 = (b2.x - b1.x) * (b2.x - b1.x); this.dist = Math.sqrt( ((b2.x - b1.x) * (b2.x - b1.x)) + ((b2.y - b1.y) * (b2.y - b1.y)) ); xDist = xDist + this.dist; this.curve = {rad: this.rad, dist: this.dist, cDist: xDist}; } function bezierT(t,startX, startY,control1X,control1Y,control2X,control2Y,endX,endY) { //calculates the tangent line to a point in the curve; later used to calculate the degrees of rotation at this point. this.mx = (3*(1-t)*(1-t) * (control1X - startX)) + ((6 * (1-t) * t) * (control2X - control1X)) + (3 * t * t * (endX - control2X)); this.my = (3*(1-t)*(1-t) * (control1Y - startY)) + ((6 * (1-t) * t) * (control2Y - control1Y)) + (3 * t * t * (endY - control2Y)); } function bezier2(t,startX, startY,control1X,control1Y,control2X,control2Y,endX,endY) { //Quadratic bezier curve plotter this.Bezier1 = new bezier1(t,startX,startY,control1X,control1Y,control2X,control2Y); this.Bezier2 = new bezier1(t,control1X,control1Y,control2X,control2Y,endX,endY); this.x = ((1 - t) * this.Bezier1.x) + (t * this.Bezier2.x); this.y = ((1 - t) * this.Bezier1.y) + (t * this.Bezier2.y); this.slope = new bezierT(t,startX, startY,control1X,control1Y,control2X,control2Y,endX,endY); this.point = {t: t, x: this.x, y: this.y, mX: this.slope.mx, mY: this.slope.my}; } function bezier1(t,startX, startY,control1X,control1Y,control2X,control2Y) { //linear bezier curve plotter; used recursivly in the quadratic bezier curve calculation this.x = (( 1 - t) * (1 - t) * startX) + (2 * (1 - t) * t * control1X) + (t * t * control2X); this.y = (( 1 - t) * (1 - t) * startY) + (2 * (1 - t) * t * control1Y) + (t * t * control2Y); }
 .product-images-container { width: 400px; height: 400px; position:relative; } canvas { position: absolute; top: 0; }.productImage { width: 100% }.engravingArea { position: relative; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="product-images-container"> <input size="80" type="text" id="curve" name="curve" value="40,228,100,260,270,260,330,240"> <input size="80" type="text" id="text" name="text" value="testing 1234567890"> <div class="engravingArea"> <div id="canvasDiv"></div> </div> <img class="productImage" src="https://i.imgur.com/BaoChJT.jpeg"> </div>

  1. 我希望实现的是将文本集中在袖口上(我想在底部添加一些字母填充?)

  2. 文字有点模糊? 无论如何优化它以获得更好的质量?

  3. 我希望在容器调整大小时动态缩放 canvas(和里面的文本)。 容器使用百分比宽度,我们希望 canvas 遵循容器或图像本身的精确度量。 我尝试设置 canvas.width = $(".product-images-container").width(),但在这种情况下运气不佳。

期待听到您的反馈!

像这样尝试: https://jsfiddle.net/uajfh78p/

 var first = true; var productImage = document.getElementsByClassName('productImage')[0]; var curveOffset = document.getElementById('offset'); var fontSize = document.getElementById('fontsize'); $(productImage).on('load', function() { startIt(); }); function startIt() { canvasDiv = document.getElementById('canvasDiv'); canvasDiv.innerHTML = '<canvas id="layer0" width="' + productImage.naturalWidth + '" height="' + productImage.naturalHeight + '"></canvas>'; //for IE canvas = document.getElementById('layer0'); ctx = canvas.getContext('2d'); ctx.fillStyle = "green"; curve = document.getElementById('curve'); curveText = document.getElementById('text'); scale = productImage.naturalWidth/400; $(curve).keyup(function(e) {changeCurve();}); $(curveText).keyup(function(e) {changeCurve();}); $(curveOffset).on('change', changeCurve); $(fontSize).on('change', changeCurve); if (first) { changeCurve(); first = false; } } function changeCurve() { points = curve.value.split(','); offset = (parseInt(curveOffset.value) || 0); ctx.font = (parseInt(fontSize.value) || 20)*scale + "px Courier New"; if (points.length == 8) drawStack(); } function drawStack() { Ribbon = {maxChar: 50, startX: points[0]*scale, startY: (points[1] - offset)*scale, control1X: points[2]*scale, control1Y: (points[3] - offset)*scale, control2X: points[4]*scale, control2Y: (points[5] - offset)*scale, endX: points[6]*scale, endY: (points[7] - offset)*scale}; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.beginPath(); ctx.moveTo(Ribbon.startX,Ribbon.startY); ctx.bezierCurveTo(Ribbon.control1X,Ribbon.control1Y, Ribbon.control2X,Ribbon.control2Y, Ribbon.endX,Ribbon.endY); ctx.stroke(); ctx.restore(); FillRibbon(curveText.value,Ribbon); } function FillRibbon(text,Ribbon) { var textCurve = []; var ribbon = text.substring(0,Ribbon.maxChar); var curveSample = 1000; xDist = 0; var i = 0; for (i = 0; i < curveSample; i++) { a = new bezier2(i/curveSample,Ribbon.startX,Ribbon.startY,Ribbon.control1X,Ribbon.control1Y,Ribbon.control2X,Ribbon.control2Y,Ribbon.endX,Ribbon.endY); b = new bezier2((i+1)/curveSample,Ribbon.startX,Ribbon.startY,Ribbon.control1X,Ribbon.control1Y,Ribbon.control2X,Ribbon.control2Y,Ribbon.endX,Ribbon.endY); c = new bezier(a,b); textCurve.push({bezier: a, curve: c.curve}); } letterPadding = ctx.measureText("").width / 4; w = ribbon.length; ww = Math.round(ctx.measureText(ribbon).width); totalPadding = (w-1) * letterPadding; totalLength = ww + totalPadding; p = 0; cDist = textCurve[curveSample-1].curve.cDist; z = (cDist / 2) - (totalLength / 2); for (i=0;i<curveSample;i++) { if (textCurve[i].curve.cDist >= z) { p = i; break; } } for (i = 0; i < w; i++) { ctx.save(); ctx.translate(textCurve[p].bezier.point.x,textCurve[p].bezier.point.y); ctx.rotate(textCurve[p].curve.rad); ctx.fillText(ribbon[i],0,0); ctx.restore(); x1 = ctx.measureText(ribbon[i]).width + letterPadding; x2 = 0; for (j=p;j<curveSample;j++) { x2 = x2 + textCurve[j].curve.dist; if (x2 >= x1) { p = j; break; } } } } //end FillRibon function bezier(b1, b2) { //Final stage which takes p, p+1 and calculates the rotation, distance on the path and accumulates the total distance this.rad = Math.atan(b1.point.mY/b1.point.mX); this.b2 = b2; this.b1 = b1; dx = (b2.x - b1.x); dx2 = (b2.x - b1.x) * (b2.x - b1.x); this.dist = Math.sqrt( ((b2.x - b1.x) * (b2.x - b1.x)) + ((b2.y - b1.y) * (b2.y - b1.y)) ); xDist = xDist + this.dist; this.curve = {rad: this.rad, dist: this.dist, cDist: xDist}; } function bezierT(t,startX, startY,control1X,control1Y,control2X,control2Y,endX,endY) { //calculates the tangent line to a point in the curve; later used to calculate the degrees of rotation at this point. this.mx = (3*(1-t)*(1-t) * (control1X - startX)) + ((6 * (1-t) * t) * (control2X - control1X)) + (3 * t * t * (endX - control2X)); this.my = (3*(1-t)*(1-t) * (control1Y - startY)) + ((6 * (1-t) * t) * (control2Y - control1Y)) + (3 * t * t * (endY - control2Y)); } function bezier2(t,startX, startY,control1X,control1Y,control2X,control2Y,endX,endY) { //Quadratic bezier curve plotter this.Bezier1 = new bezier1(t,startX,startY,control1X,control1Y,control2X,control2Y); this.Bezier2 = new bezier1(t,control1X,control1Y,control2X,control2Y,endX,endY); this.x = ((1 - t) * this.Bezier1.x) + (t * this.Bezier2.x); this.y = ((1 - t) * this.Bezier1.y) + (t * this.Bezier2.y); this.slope = new bezierT(t,startX, startY,control1X,control1Y,control2X,control2Y,endX,endY); this.point = {t: t, x: this.x, y: this.y, mX: this.slope.mx, mY: this.slope.my}; } function bezier1(t,startX, startY,control1X,control1Y,control2X,control2Y) { //linear bezier curve plotter; used recursivly in the quadratic bezier curve calculation this.x = (( 1 - t) * (1 - t) * startX) + (2 * (1 - t) * t * control1X) + (t * t * control2X); this.y = (( 1 - t) * (1 - t) * startY) + (2 * (1 - t) * t * control1Y) + (t * t * control2Y); }
 .product-images-container { width:100%; position:relative; } canvas { position: absolute; top: 0; }.productImage { width: 100% }.engravingArea { position: relative; } #canvasDiv > canvas { width:100%; height: auto; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="product-images-container"> <label for="curve">Curve</label><input size="80" type="text" id="curve" name="curve" value="40,228,100,260,270,260,330,240"> <label for="text">Text</label><input size="80" type="text" id="text" name="text" value="testing 1234567890"> <label for="offset">Y offset</label><input size="80" type="number" id="offset" name="offset" value="5"> <label for="fontsize">Font size</label><input size="80" type="number" id="fontsize" name="fontsize" value="20"> <div class="engravingArea"> <div id="canvasDiv"></div> </div> <img class="productImage" src="https://i.imgur.com/BaoChJT.jpeg"> </div>

  1. 我添加了一个名为 offset 的字段,它允许您指定距底部的 y 偏移量。 在渲染到 canvas 之前,该值被合并到曲线数字中。这与字体大小的类似字段一起,允许轻松调整以匹配图像(更改时调用changeCurve() )。

  2. 我建议将 canvas 设为与图像相同的像素大小,这样它就会同样清晰。 我更改了代码以读取图像宽度,然后创建该大小的 canvas,使用比例因子相应地缩放尺寸。

  3. 在 2. 中调整了 canvas 的大小后,缩放整个对象就变成了设置 CSS 的情况,这样 canvas 和图像都会填充它们的容器宽度,以便它们一起缩放。

暂无
暂无

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

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