use*_*611 11 html5 text canvas
无论如何从html 5中的文本字母中提取路径,然后沿着该路径获取(x,y)坐标,以便字母可以通过沿着该字母路径的圆圈形成?
我想采用x,y坐标并在其位置应用一个形状,使其类似于"像素化"格式的文本字符串,然后是一些动画效果.
有关在画布上沿着字符路径获取某种x,y坐标的任何建议都会很棒.
编辑:我实际上是在尝试自动生成坐标,以执行与此类似的操作:http://www.html5canvastutorials.com/labs/html5-canvas-google-bouncing-balls/
小智 15
一个简单的方法是执行以下操作:
现在你有一个粗略的"球"代表文本,可以动画.它在字母间距方面并不是非常准确,但它应该用于给定的目的(您可以始终测量每个字母,在分离点处使用额外的delta值增加结束x值).
较大的字体可以提高质量,但也会产生更多的点数.与下面演示中使用的通用字体类型不同的字体类型也可以有利于整体外观(实验!).您还可以调整alpha阈值,以包含不完全稳定但有影响力的像素.
最后,不同的浏览器以不同的方式呈现文本,因此您可能也想要考虑到这一点(请参阅上文关于测量每个字母以在它们之间添加额外空间).

var ctx = document.querySelector("canvas").getContext("2d"),
inp = document.querySelector("input"),
w = ctx.canvas.width,
h = ctx.canvas.height,
balls = []; // global ball array
ctx.fillStyle = "rgb(0, 154, 253)"; // fill must be a solid color
generate(inp.value) // init default text
inp.onkeyup = function() {generate(this.value)}; // get some text to demo
function generate(txt) {
var i, radius = 5, // ball radius
data32; // we'll use uint32 for speed
balls = []; // clear ball array
ctx.clearRect(0, 0, w, h); // clear canvas so we can
ctx.fillText(txt.toUpperCase(), 0, 10); // draw the text (default 10px)
// get a Uint32 representation of the bitmap:
data32 = new Uint32Array(ctx.getImageData(0, 0, w, h).data.buffer);
// loop through each pixel. We will only store the ones with alpha = 255
for(i = 0; i < data32.length; i++) {
if (data32[i] & 0xff000000) { // check alpha mask
balls.push({ // add new ball if a solid pixel
x: (i % w) * radius * 2 + radius, // use position and radius to
y: ((i / w)|0) * radius * 2 + radius, // pre-calc final position and size
radius: radius,
a: (Math.random() * 250)|0 // just to demo animation capability
});
}
}
// return array - here we'll animate it directly to show the resulting objects:
}
(function animate() {
ctx.clearRect(0, 0, w, h);
ctx.beginPath();
for(var i = 0, ball; ball = balls[i]; i++) {
var dx = Math.sin(ball.a * 0.2) + ball.radius, // do something funky
dy = Math.cos(ball.a++ * 0.2) + ball.radius;
ctx.moveTo(ball.x + ball.radius + dx, ball.y + dy);
ctx.arc(ball.x + dx, ball.y + dy, ball.radius, 0, 6.28);
ctx.closePath();
}
ctx.fill();
requestAnimationFrame(animate);
})();Run Code Online (Sandbox Code Playgroud)
body {font:bold 16px sans-serif}Run Code Online (Sandbox Code Playgroud)
<label>Type some text: <input value="PIXELS"></label><br>
<canvas width=1024></canvas>Run Code Online (Sandbox Code Playgroud)
这是一项艰巨的任务,通过在字母的路径上直观地放置圆圈来手动完成.
在没有人为干预的情况下自动(自动地!)更难.
以下是如何自动排列圆形以形成字母.
答案分为两部分......
找到"字母",
创建圆圈以填充和勾勒出字母形状.
1.困难的部分
Frederik De Bleser编写了一个很好的库opentype.js,它接受一个.ttf字体文件,并使用画布上的二次曲线解析出任何指定字符的字形轮廓:https://github.com/nodebox/opentype.js
2.唯一稍微不那么难的部分
每封信:
在每个二次曲线上找到"很多"点.这是以间隔T计算曲线上的[x,y]的算法.T的范围从曲线起点的0.00到曲线末端的1.00.T不会沿曲线产生均匀间隔的[x,y],因此您需要进行过采样(因此"很多"可能意味着1000的T值介于0.00和1.00之间).
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
Run Code Online (Sandbox Code Playgroud)找到与这些点处的曲线角度相切的角度.(基本上计算曲线的直角).您可以使用二次公式的下一个导数来做到这一点:
function quadraticBezierTangentAngle(t, p0, p2, p1) {
var tt = 1 - t;
var dx = (tt * p1.x + t * p2.x) - (tt * p0.x + t * p1.x);
var dy = (tt * p1.y + t * p2.y) - (tt * p0.y + t * p1.y);
return Math.tan(Math.atan2(dy,dx));
}
Run Code Online (Sandbox Code Playgroud)从曲线的开始处开始,计算从当前[x,y]到下一个[x,y]的每个距离.你可以用毕达哥拉斯定理来做到这一点:
var dx=nextX-currentX;
var dy=nextY-currentY;
var distance=Math.sqrt(dx*dx+dy*dy);
Run Code Online (Sandbox Code Playgroud)重复数组,使所有剩余的[x,y]元素与前一个[x,y]元素相距1px.您可以通过使用第一个数组中的值填充第二个数组来完成此操作parseInt( nextInOriginalArray - lastDistanceInNewArray)==1;
确定构成每个字母的圆圈的半径.这实际上比看起来更难.对于"块状"字体,您可以在画布上绘制字母"I".然后使用获取所有像素getImageData.通过搜索在字母垂直中间水平延伸的不透明像素的数量来计算"I"的垂直笔划的宽度.对于块状字体,var radius = horizontalOpaquePixelCount/2;.对于具有可变宽度笔划的字体,您必须具有创造性.也许var radius = horizontalOpaquePixelCount/3;还是var radius = horizontalOpaquePixelCount/4;.
迭代点数组并为每个radius*2像素定义一个新圆.您可以使用切线角度和三角函数计算每个圆的中心点,如下所示:
var centerX = curvePointX + radius*Math.cos(tangentAngle);
var centerY = curvePointY + radius*Math.sin(tangentAngle);
Run Code Online (Sandbox Code Playgroud)创建圆时,在某些时候字母的曲线会自动回转,因此您必须检查您创建的每个新圆,以确保它不会与现有圆重叠.您可以计算新圆是否与每个现有圆相交,如下所示:
var dx = newCircleCenterX - existingCircleCenterX;
var dy = newCircleCenterY - existingCircleCenterY;
var distance=Math.sqrt(dx*dx+dy*dy);
var circlesAreIntersecting=(distance<=newCircleRadius+existingCircleRadius);
Run Code Online (Sandbox Code Playgroud)微调:在字母路径中的某些端点附近,您会发现下一个完整的半径圆将溢出字母.如果发生这种情况,您可以缩小某些圆的半径以适合字母形状.如果你想要圆圈的固定半径,那么你可以根据所有圆的平均半径重新计算所有圆的固定半径 - 包括你必须"收缩"以适合字母形状的半径.
例如.这是由15个圆圈组成的字母"L".

但是2个红色圆圈落在了它的字母形状之外.您可以(1)收缩红色圆圈以适合字母形状或(2)根据符合字母形状的平均半径重新计算新的固定圆弧半径:
var total=0;
total += greenRadii * 13;
total += verticalRedRadiusResizedToFitInsideLetterform;
total += horizontalRedRadiusResizedToFitInsideLetterform;
var newRadius = total / 15;
Run Code Online (Sandbox Code Playgroud)
您可以通过计算两条线的交点来计算适合字母形状的红色半径的长度:(1)通过连接最后的绿色圆心和红色圆心形成的线段,(2)垂直形成的线曲线上的最后一点.这是一个计算2条线交点的算法:
// Get interseting point of 2 line segments (if any)
// Attribution: http://paulbourke.net/geometry/pointlineplane/
function line2lineIntersection(p0,p1,p2,p3) {
var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x);
var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x);
var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);
// Test if Coincident
// If the denominator and numerator for the ua and ub are 0
// then the two lines are coincident.
if(unknownA==0 && unknownB==0 && denominator==0){return(null);}
// Test if Parallel
// If the denominator for the equations for ua and ub is 0
// then the two lines are parallel.
if (denominator == 0) return null;
// If the intersection of line segments is required
// then it is only necessary to test if ua and ub lie between 0 and 1.
// Whichever one lies within that range then the corresponding
// line segment contains the intersection point.
// If both lie within the range of 0 to 1 then
// the intersection point is within both line segments.
unknownA /= denominator;
unknownB /= denominator;
var isIntersecting=(unknownA>=0 && unknownA<=1 && unknownB>=0 && unknownB<=1)
if(!isIntersecting){return(null);}
return({
x: p0.x + unknownA * (p1.x-p0.x),
y: p0.y + unknownA * (p1.y-p0.y)
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3896 次 |
| 最近记录: |