使用中继器而不是 canvas 来弯曲文本
Using a repeater instead of a canvas to curve text
而不是使用 canvas 来弯曲文本以适合图像。是否可以使用 Repeater
来达到同样的效果?目前,我的代码使用 Canvas
完成了我需要它做的事情。但我很好奇,使用 Repeater 来完成我最终想要的东西对我来说也可能更好。我怎样才能修改我现有的代码来实现这个?
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property string letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Image {
id: abcBar
source: "alphabetBar.png"
visible: false
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -10
}
// Rectangle {
// id: body
// anchors.fill: parent
// readonly property real dx: 12
// function midpoint(idx) {
// return 10 + idx * body.dx;
// }
// Repeater {
// anchors.fill: parent
// model: root.letters.length
// Text {
// anchors.horizontalCenter: parent.left
// anchors.horizontalCenterOffset: body.midpoint(index)
// anchors.verticalCenter: parent.verticalCenter
// text: root.letters[index]
// }
// }
// }
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext('2d');
ctx.save();
ctx.canvas.width = 160;
ctx.canvas.height = 432;
ctx.font = "18px Roboto";
ctx.textAlign = "center";
ctx.fillStyle = "#000000"
const centerX = 10;
const centerY = ctx.canvas.height / 2;
const angle = Math.PI;
const radius = 130
ctx.fillStyle = "#000000"
ctx.restore();
ctx.save();
ctx.drawImage(abcBar, 0, 0, abcBar.width, abcBar.height);
console.log('loaded')
ctx.restore();
const args = {
ctx,
// text: "A • D • G • J • M • P • S • V • Z",
text: root.letters,
offset: 0,
G1: {
x: 20,
y: 80,
},
G2: {
x: 190,
y: 230,
},
G3: {
x: 0,
y: 372
},
}
textOnCurve(args);
}
}
function textOnCurve({ ctx, text, offset, G1, G2, G3, G4}){
const x1 = G1.x;
const y1 = G1.y;
const x2 = G2.x;
const y2 = G2.y;
const x3 = G3.x;
const y3 = G3.y;
const x4 = G3.x;
const y4 = G3.y;
ctx.save();
ctx.textAlign = "center";
var widths = [];
for (var i = 0; i < text.length; i++)
{
widths[widths.length] = ctx.measureText(text[i]).width;
}
ctx.beginPath();
var ch = curveHelper(x1, y1, x2, y2, x3, y3, x4, y4);
ctx.stroke();
var pos = offset;
var cpos = 0;
for (var j = 0; j < text.length; j++)
{
pos += widths[j] / 2;
cpos = ch.forward(pos);
ch.tangent(cpos);
ctx.setTransform(ch.vect.x, ch.vect.y, -ch.vect.y, ch.vect.x, ch.vec.x, ch.vec.y);
ctx.fillText(text[j], 0, 0);
pos += widths[j] / 2;
}
ctx.restore();
}
function curveHelper(x1, y1, x2, y2, x3, y3, x4, y4)
{
var tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
var a, b, c, u;
var vec, currentPos, vec1, vect, quad, currentDist;
vec = { x: 0, y: 0 } ;
vec1 = { x: 0, y: 0 } ;
vect = { x: 0, y: 0 } ;
quad = false;
currentPos = 0;
currentDist = 0;
if (x4 === undefined || x4 === null)
{
quad = true;
x4 = x3;
y4 = y3;
}
var estLen = Math.sqrt((x4 - x1)* (x4 - x1)+ (y4 - y1)* (y4 - y1));
var onePix = 1 / estLen;
function posAtC(c)
{
tx1 = x1; ty1 = y1;
tx2 = x2; ty2 = y2;
tx3 = x3; ty3 = y3;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (tx3 - tx2)* c;
ty2 += (ty3 - ty2)* c;
tx3 += (x4 - tx3)* c;
ty3 += (y4 - ty3)* c;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (tx3 - tx2)* c;
ty2 += (ty3 - ty2)* c;
vec.x = tx1 + (tx2 - tx1)* c;
vec.y = ty1 + (ty2 - ty1)* c;
return vec;
}
function posAtQ(c)
{
tx1 = x1; ty1 = y1;
tx2 = x2; ty2 = y2;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (x3 - tx2)* c;
ty2 += (y3 - ty2)* c;
vec.x = tx1 + (tx2 - tx1)* c;
vec.y = ty1 + (ty2 - ty1)* c;
return vec;
}
function forward(dist)
{
var step;
helper.posAt(currentPos);
while (currentDist < dist)
{
vec1.x = vec.x;
vec1.y = vec.y;
currentPos += onePix;
helper.posAt(currentPos);
currentDist += step = Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
}
currentPos -= ((currentDist - dist)/ step)* onePix
currentDist -= step;
helper.posAt(currentPos);
currentDist += Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
return currentPos;
}
function tangentQ(pos)
{
a = (1 - pos)* 2;
b = pos * 2;
vect.x = a * (x2 - x1)+ b * (x3 - x2);
vect.y = a * (y2 - y1)+ b * (y3 - y2);
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
function tangentC(pos)
{
a = (1 - pos)
b = 6 * a * pos;
a *= 3 * a;
c = 3 * pos * pos;
vect.x = -x1 * a + x2 * (a - b)+ x3 * (b - c)+ x4 * c;
vect.y = -y1 * a + y2 * (a - b)+ y3 * (b - c)+ y4 * c;
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
var helper = {
vec: vec,
vect: vect,
forward: forward,
}
if (quad)
{
helper.posAt = posAtQ;
helper.tangent = tangentQ;
} else
{
helper.posAt = posAtC;
helper.tangent = tangentC;
}
return helper
}
}
当然可以:
Rectangle {
id: control
width: Math.min(parent.height, parent.width)
height: width
anchors.centerIn: parent
color: "#40ff0000" //to see where it is going, which helps in positioning
property double startAngle: -45
property double endAngle: 45
Repeater {
model: 26
Text {
property double angle: control.startAngle + (control.endAngle - control.startAngle) * (index / 26)
x: (control.width / 2) * (1 + 0.9 * Math.cos(Math.PI * angle / 180)) - width * 0.5
y: (control.height / 2) * (1 + 0.9 * Math.sin(Math.PI * angle / 180)) - height * 0.5
text: String.fromCharCode(index + 65)
//rotation: angle
}
}
}
如果您想为每个文本添加一个 MouseArea 或将其更改为一个按钮,这也是一个明智的选择。
请注意,您必须根据图像稍微调整矩形的位置。
而不是使用 canvas 来弯曲文本以适合图像。是否可以使用 Repeater
来达到同样的效果?目前,我的代码使用 Canvas
完成了我需要它做的事情。但我很好奇,使用 Repeater 来完成我最终想要的东西对我来说也可能更好。我怎样才能修改我现有的代码来实现这个?
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property string letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Image {
id: abcBar
source: "alphabetBar.png"
visible: false
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -10
}
// Rectangle {
// id: body
// anchors.fill: parent
// readonly property real dx: 12
// function midpoint(idx) {
// return 10 + idx * body.dx;
// }
// Repeater {
// anchors.fill: parent
// model: root.letters.length
// Text {
// anchors.horizontalCenter: parent.left
// anchors.horizontalCenterOffset: body.midpoint(index)
// anchors.verticalCenter: parent.verticalCenter
// text: root.letters[index]
// }
// }
// }
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext('2d');
ctx.save();
ctx.canvas.width = 160;
ctx.canvas.height = 432;
ctx.font = "18px Roboto";
ctx.textAlign = "center";
ctx.fillStyle = "#000000"
const centerX = 10;
const centerY = ctx.canvas.height / 2;
const angle = Math.PI;
const radius = 130
ctx.fillStyle = "#000000"
ctx.restore();
ctx.save();
ctx.drawImage(abcBar, 0, 0, abcBar.width, abcBar.height);
console.log('loaded')
ctx.restore();
const args = {
ctx,
// text: "A • D • G • J • M • P • S • V • Z",
text: root.letters,
offset: 0,
G1: {
x: 20,
y: 80,
},
G2: {
x: 190,
y: 230,
},
G3: {
x: 0,
y: 372
},
}
textOnCurve(args);
}
}
function textOnCurve({ ctx, text, offset, G1, G2, G3, G4}){
const x1 = G1.x;
const y1 = G1.y;
const x2 = G2.x;
const y2 = G2.y;
const x3 = G3.x;
const y3 = G3.y;
const x4 = G3.x;
const y4 = G3.y;
ctx.save();
ctx.textAlign = "center";
var widths = [];
for (var i = 0; i < text.length; i++)
{
widths[widths.length] = ctx.measureText(text[i]).width;
}
ctx.beginPath();
var ch = curveHelper(x1, y1, x2, y2, x3, y3, x4, y4);
ctx.stroke();
var pos = offset;
var cpos = 0;
for (var j = 0; j < text.length; j++)
{
pos += widths[j] / 2;
cpos = ch.forward(pos);
ch.tangent(cpos);
ctx.setTransform(ch.vect.x, ch.vect.y, -ch.vect.y, ch.vect.x, ch.vec.x, ch.vec.y);
ctx.fillText(text[j], 0, 0);
pos += widths[j] / 2;
}
ctx.restore();
}
function curveHelper(x1, y1, x2, y2, x3, y3, x4, y4)
{
var tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
var a, b, c, u;
var vec, currentPos, vec1, vect, quad, currentDist;
vec = { x: 0, y: 0 } ;
vec1 = { x: 0, y: 0 } ;
vect = { x: 0, y: 0 } ;
quad = false;
currentPos = 0;
currentDist = 0;
if (x4 === undefined || x4 === null)
{
quad = true;
x4 = x3;
y4 = y3;
}
var estLen = Math.sqrt((x4 - x1)* (x4 - x1)+ (y4 - y1)* (y4 - y1));
var onePix = 1 / estLen;
function posAtC(c)
{
tx1 = x1; ty1 = y1;
tx2 = x2; ty2 = y2;
tx3 = x3; ty3 = y3;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (tx3 - tx2)* c;
ty2 += (ty3 - ty2)* c;
tx3 += (x4 - tx3)* c;
ty3 += (y4 - ty3)* c;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (tx3 - tx2)* c;
ty2 += (ty3 - ty2)* c;
vec.x = tx1 + (tx2 - tx1)* c;
vec.y = ty1 + (ty2 - ty1)* c;
return vec;
}
function posAtQ(c)
{
tx1 = x1; ty1 = y1;
tx2 = x2; ty2 = y2;
tx1 += (tx2 - tx1)* c;
ty1 += (ty2 - ty1)* c;
tx2 += (x3 - tx2)* c;
ty2 += (y3 - ty2)* c;
vec.x = tx1 + (tx2 - tx1)* c;
vec.y = ty1 + (ty2 - ty1)* c;
return vec;
}
function forward(dist)
{
var step;
helper.posAt(currentPos);
while (currentDist < dist)
{
vec1.x = vec.x;
vec1.y = vec.y;
currentPos += onePix;
helper.posAt(currentPos);
currentDist += step = Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
}
currentPos -= ((currentDist - dist)/ step)* onePix
currentDist -= step;
helper.posAt(currentPos);
currentDist += Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
return currentPos;
}
function tangentQ(pos)
{
a = (1 - pos)* 2;
b = pos * 2;
vect.x = a * (x2 - x1)+ b * (x3 - x2);
vect.y = a * (y2 - y1)+ b * (y3 - y2);
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
function tangentC(pos)
{
a = (1 - pos)
b = 6 * a * pos;
a *= 3 * a;
c = 3 * pos * pos;
vect.x = -x1 * a + x2 * (a - b)+ x3 * (b - c)+ x4 * c;
vect.y = -y1 * a + y2 * (a - b)+ y3 * (b - c)+ y4 * c;
u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
vect.x /= u;
vect.y /= u;
}
var helper = {
vec: vec,
vect: vect,
forward: forward,
}
if (quad)
{
helper.posAt = posAtQ;
helper.tangent = tangentQ;
} else
{
helper.posAt = posAtC;
helper.tangent = tangentC;
}
return helper
}
}
当然可以:
Rectangle {
id: control
width: Math.min(parent.height, parent.width)
height: width
anchors.centerIn: parent
color: "#40ff0000" //to see where it is going, which helps in positioning
property double startAngle: -45
property double endAngle: 45
Repeater {
model: 26
Text {
property double angle: control.startAngle + (control.endAngle - control.startAngle) * (index / 26)
x: (control.width / 2) * (1 + 0.9 * Math.cos(Math.PI * angle / 180)) - width * 0.5
y: (control.height / 2) * (1 + 0.9 * Math.sin(Math.PI * angle / 180)) - height * 0.5
text: String.fromCharCode(index + 65)
//rotation: angle
}
}
}
如果您想为每个文本添加一个 MouseArea 或将其更改为一个按钮,这也是一个明智的选择。
请注意,您必须根据图像稍微调整矩形的位置。