"Отменить" преобразования холста для ввода текста
При применении преобразования с холстом полученный текст также (очевидно) преобразуется. Есть ли способ предотвратить определенные преобразования, такие как отражение, влияющих на текст?
Например, я устанавливаю глобальную матрицу преобразований, поэтому ось Y направлена вверх, ось X вправо, а точка (0, 0)
находится в центре экрана (что вы ожидаете от математической координаты система).
Однако это также переводит текст в обратном порядке.
const size = 200;
const canvas = document.getElementsByTagName('canvas')[0]
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, -1, size / 2, size / 2);
const triangle = [
{x: -70, y: -70, label: 'A'},
{x: 70, y: -70, label: 'B'},
{x: 0, y: 70, label: 'C'},
];
// draw lines
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(triangle[2].x, triangle[2].y);
triangle.forEach(v => ctx.lineTo(v.x, v.y));
ctx.stroke();
ctx.closePath();
// draw labels
ctx.textAlign = 'center';
ctx.font = '24px Arial';
triangle.forEach(v => ctx.fillText(v.label, v.x, v.y - 8));
<canvas></canvas>
Ответы
Ответ 1
Чтобы построить тайский ответ, который является фантастическим, вы можете рассмотреть следующее:
const size = 200;
const canvas = document.getElementsByTagName('canvas')[0]
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
// Create a custom fillText funciton that flips the canvas, draws the text, and then flips it back
ctx.fillText = function(text, x, y) {
this.save(); // Save the current canvas state
this.scale(1, -1); // Flip to draw the text
this.fillText.dummyCtx.fillText.call(this, text, x, -y); // Draw the text, invert y to get coordinate right
this.restore(); // Restore the initial canvas state
}
// Create a dummy canvas context to use as a source for the original fillText function
ctx.fillText.dummyCtx = document.createElement('canvas').getContext('2d');
ctx.setTransform(1, 0, 0, -1, size / 2, size / 2);
const triangle = [
{x: -70, y: -70, label: 'A'},
{x: 70, y: -70, label: 'B'},
{x: 0, y: 70, label: 'C'},
];
// draw lines
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(triangle[2].x, triangle[2].y);
triangle.forEach(v => ctx.lineTo(v.x, v.y));
ctx.stroke();
ctx.closePath();
// draw labels
ctx.textAlign = 'center';
ctx.font = '24px Arial';
// For this particular example, multiplying x and y by small factors >1 offsets the labels from the triangle vertices
triangle.forEach(v => ctx.fillText(v.label, 1.2*v.x, 1.1*v.y));
Ответ 2
Мое решение поворачивает холст, а затем рисует текст.
ctx.scale(1,-1); // rotate the canvas
triangle.forEach(v => {
ctx.fillText(v.label, v.x, -v.y + 25); // draw with a bit adapt position
});
Надеюсь, что помогает:)
const size = 200;
const canvas = document.getElementsByTagName('canvas')[0]
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, -1, size / 2, size / 2);
const triangle = [
{x: -70, y: -70, label: 'A'},
{x: 70, y: -70, label: 'B'},
{x: 0, y: 70, label: 'C'},
];
// draw lines
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(triangle[2].x, triangle[2].y);
triangle.forEach(v => ctx.lineTo(v.x, v.y));
ctx.stroke();
ctx.closePath();
// draw labels
ctx.textAlign = 'center';
ctx.font = '24px Arial';
ctx.scale(1,-1);
triangle.forEach(v => {
ctx.fillText(v.label, v.x, -v.y + 25);
});
<canvas></canvas>
Ответ 3
Я бы пошел с подходом, который хранит "состояние" вашего чертежа без фактических пикселей и определяет метод draw
, который может отображать это состояние в любой точке.
Вам нужно будет реализовать свои собственные методы scale
и translate
для ваших очков, но я думаю, что это стоит того, в конце концов.
Итак, в пулях:
- Сохраните список "вещей для рисования" (точки с метками)
- Expose
scale
и translate
методы, которые изменяют эти "вещи"
- Вывести метод
draw
, который отображает эти "вещи"
В качестве примера я создал класс под названием Figure
, который показывает реализацию этих функций 1.0. Я создаю новый экземпляр, который ссылается на холст. Затем я добавляю к нему точки, передавая x
, y
и a label
. scale
и transform
обновить свойства этих точек x
и y
. draw
проходит через точки к a) нарисуйте "точку" и b) нарисуйте метку.
const Figure = function(canvas) {
const ctx = canvas.getContext('2d');
const origin = {
x: canvas.width / 2,
y: canvas.height / 2
};
const shift = p => Object.assign(p, {
x: origin.x + p.x,
y: origin.y - p.y
});
let points = [];
this.addPoint = (x, y, label) => {
points = points.concat({
x,
y,
label
});
}
this.translate = (tx, ty) => {
points = points.map(
p => Object.assign(p, {
x: p.x + tx,
y: p.y + ty
})
);
};
this.scale = (sx, sy) => {
points = points.map(
p => Object.assign(p, {
x: p.x * sx,
y: p.y * sy
})
);
};
this.draw = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
const sPoints = points.map(shift);
sPoints.forEach(p => drawDot(ctx, 5, p.x, p.y));
sPoints.forEach(p => drawLabel(ctx, p.label, p.x + 5, p.y));
ctx.fill();
}
}
const init = () => {
const canvas = document.getElementById('canvas');
const fig = new Figure(canvas);
// Generate some test data
for (let i = 0, labels = "ABCD"; i < labels.length; i += 1) {
fig.addPoint(i * 3, (i + 1) * 10, labels[i]);
}
const sX = parseFloat(document.querySelector(".js-scaleX").value);
const sY = parseFloat(document.querySelector(".js-scaleY").value);
const tX = parseFloat(document.querySelector(".js-transX").value);
const tY = parseFloat(document.querySelector(".js-transY").value);
fig.scale(sX, sY);
fig.translate(tX, tY);
fig.draw();
}
Array
.from(document.querySelectorAll("input"))
.forEach(el => el.addEventListener("change", init));
init();
// Utilities for drawing
function drawDot(ctx, d, x, y) {
ctx.arc(x, y, d / 2, 0, 2 * Math.PI);
}
function drawLabel(ctx, label, x, y) {
ctx.fillText(label, x, y);
}
canvas {
background: #efefef;
margin: 1rem;
}
input {
width: 50px;
}
<div>
<p>
Scales first, translates second (hard coded, can be changed)
</p>
<label>Scale x <input type="number" class="js-scaleX" value="1"></label>
<label>Scale y <input type="number" class="js-scaleY" value="1"></label>
<br/>
<label>Translate x <input type="number" class="js-transX" value="0"></label>
<label>translate y <input type="number" class="js-transY" value="0"></label>
</div>
<canvas id="canvas" width="250" height="250"></canvas>