我想绘制一个像这个Flash动画一样的canvas图形:http://www.cci.com.tr/tr/bizi-taniyin/tarihcemiz/
我画了六个弧线,想在这些弧线上写六个单词。有什么好的想法吗?
我想绘制一个像这个Flash动画一样的canvas图形:http://www.cci.com.tr/tr/bizi-taniyin/tarihcemiz/
我画了六个弧线,想在这些弧线上写六个单词。有什么好的想法吗?
我有一个jsFiddle示例,可以将文本应用于任意贝塞尔曲线定义。享受http://jsfiddle.net/Makallus/hyyvpp8g/
var first = true;
startIt();
function startIt() {
canvasDiv = document.getElementById('canvasDiv');
canvasDiv.innerHTML = '<canvas id="layer0" width="300" height="300"></canvas>'; //for IE
canvas = document.getElementById('layer0');
ctx = canvas.getContext('2d');
ctx.fillStyle = "black";
ctx.font = "18px arial black";
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);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<TR>
<TH>Bezier Curve</TH>
<TD>
<input size="80" type="text" id="curve" name="curve" value="99.2,177.2,130.02,60.0,300.5,276.2,300.7,176.2">
</TD>
</TR>
<TR>
<TH>Text</TH>
<TD>
<input size="80" type="text" id="text" name="text" value="testing 1234567890">
</TD>
</TR>
<TR>
<TD colspan=2>
<div id="canvasDiv"></div>
</TD>
</TR>
</table>
一个古老的问题...然而,在我的博客上,我非常详细地探讨了使用HTML5 Canvas创建圆形文本:
示例中的选项包括从给定角度实现的圆形文本对齐(左、中和右),内向和外向文本,字距(可调整字符之间的间隔)以及半径内或半径外的文本。
还有一个带有工作示例的 jsfiddle。
代码如下:
document.body.appendChild(getCircularText("ROUNDED TEXT LOOKS BEST IN CAPS!", 250, 0, "center", true, true, "Arial", "18pt", 0));
function getCircularText(text, diameter, startAngle, align, textInside, inwardFacing, fName, fSize, kerning) {
// text: The text to be displayed in circular fashion
// diameter: The diameter of the circle around which the text will
// be displayed (inside or outside)
// startAngle: In degrees, Where the text will be shown. 0 degrees
// if the top of the circle
// align: Positions text to left right or center of startAngle
// textInside: true to show inside the diameter. False draws outside
// inwardFacing: true for base of text facing inward. false for outward
// fName: name of font family. Make sure it is loaded
// fSize: size of font family. Don't forget to include units
// kearning: 0 for normal gap between letters. positive or
// negative number to expand/compact gap in pixels
//------------------------------------------------------------------------
// declare and intialize canvas, reference, and useful variables
align = align.toLowerCase();
var mainCanvas = document.createElement('canvas');
var ctxRef = mainCanvas.getContext('2d');
var clockwise = align == "right" ? 1 : -1; // draw clockwise for aligned right. Else Anticlockwise
startAngle = startAngle * (Math.PI / 180); // convert to radians
// calculate height of the font. Many ways to do this
// you can replace with your own!
var div = document.createElement("div");
div.innerHTML = text;
div.style.position = 'absolute';
div.style.top = '-10000px';
div.style.left = '-10000px';
div.style.fontFamily = fName;
div.style.fontSize = fSize;
document.body.appendChild(div);
var textHeight = div.offsetHeight;
document.body.removeChild(div);
// in cases where we are drawing outside diameter,
// expand diameter to handle it
if (!textInside) diameter += textHeight * 2;
mainCanvas.width = diameter;
mainCanvas.height = diameter;
// omit next line for transparent background
mainCanvas.style.backgroundColor = 'lightgray';
ctxRef.font = fSize + ' ' + fName;
// Reverse letter order for align Left inward, align right outward
// and align center inward.
if (((["left", "center"].indexOf(align) > -1) && inwardFacing) || (align == "right" && !inwardFacing)) text = text.split("").reverse().join("");
// Setup letters and positioning
ctxRef.translate(diameter / 2, diameter / 2); // Move to center
startAngle += (Math.PI * !inwardFacing); // Rotate 180 if outward
ctxRef.textBaseline = 'middle'; // Ensure we draw in exact center
ctxRef.textAlign = 'center'; // Ensure we draw in exact center
// rotate 50% of total angle for center alignment
if (align == "center") {
for (var j = 0; j < text.length; j++) {
var charWid = ctxRef.measureText(text[j]).width;
startAngle += ((charWid + (j == text.length-1 ? 0 : kerning)) / (diameter / 2 - textHeight)) / 2 * -clockwise;
}
}
// Phew... now rotate into final start position
ctxRef.rotate(startAngle);
// Now for the fun bit: draw, rotate, and repeat
for (var j = 0; j < text.length; j++) {
var charWid = ctxRef.measureText(text[j]).width; // half letter
ctxRef.rotate((charWid/2) / (diameter / 2 - textHeight) * clockwise); // rotate half letter
// draw char at "top" if inward facing or "bottom" if outward
ctxRef.fillText(text[j], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));
ctxRef.rotate((charWid/2 + kerning) / (diameter / 2 - textHeight) * clockwise); // rotate half letter
}
// Return it
return (mainCanvas);
}
function drawTextAlongArc(context, str, centerX, centerY, radius, angle) {
var len = str.length,
s;
context.save();
context.translate(centerX, centerY);
context.rotate(-1 * angle / 2);
context.rotate(-1 * (angle / len) / 2);
for (var n = 0; n < len; n++) {
context.rotate(angle / len);
context.save();
context.translate(0, -1 * radius);
s = str[n];
context.fillText(s, 0, 0);
context.restore();
}
context.restore();
}
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
centerX = canvas.width / 2,
centerY = canvas.height - 30,
angle = Math.PI * 0.8,
radius = 150;
context.font = '30pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'blue';
context.strokeStyle = 'blue';
context.lineWidth = 4;
drawTextAlongArc(context, 'Text along arc path', centerX, centerY, radius, angle);
// draw circle underneath text
context.arc(centerX, centerY, radius - 10, 0, 2 * Math.PI, false);
context.stroke();
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="250"></canvas>
</body>
</html>
无法使用任何内置方法。请注意,SVG本身支持沿路径的文本,因此您可能希望考虑使用SVG!
但是,您可以编写自定义代码以实现相同的效果,就像我们为这个问题做的一样:HTML5 Canvas Circle Text