Рисование повернутого текста на холсте HTML5


часть веб-приложения, которое я разрабатываю, требует от меня создавать гистограммы для отображения различной информации. Я решил, что если браузер пользователя способен, я бы нарисовал их с помощью элемента HTML5 canvas. У меня нет проблем с рисованием линий и полос для моих графиков, но когда дело доходит до маркировки осей, баров или линий, я столкнулся с проблемой. Как нарисовать повернутый текст на элементе холста, чтобы он соответствовал элементу, который он маркирует? Несколько примеров включают в себя:

  • повернуть текст на 90 градусов против по часовой стрелке, чтобы обозначить ось y
  • повернуть текст на 90 градусов против по часовой стрелке для маркировки баров по вертикали гистограмма
  • повернуть текст на произвольную величину линии меток на линейном графике

любые указатели будут оценены.

5   51   2010-07-02 21:45:04

5 ответов:

Как уже упоминалось, вы, вероятно, хотите взглянуть на повторное использование существующего графического решения, но поворот текста не слишком трудно. Несколько запутанный бит (для меня) заключается в том, что вы поворачиваете весь контекст, а затем рисуете на нем:

ctx.rotate(Math.PI*2/(i*6));

на угол в радианах. Код из этого примера, который я считаю, был сделан для преобразования часть учебника MDC canvas.

см. ответ ниже для более полного решения.

публикуя это в попытке помочь другим с аналогичными проблемами. Я решил эту проблему с помощью пятиэтапного подхода-сохраните контекст, переведите контекст, поверните контекст, нарисуйте текст, а затем восстановите контекст в сохраненное состояние.

Я думаю о переводах и преобразованиях в контекст как о манипулировании координатной сеткой, наложенной на холст. По умолчанию начало координат (0,0) в верхнем левом углу холста. X увеличивается слева направо, Y увеличивается сверху вниз. Если вы сделаете" L " с указательным пальцем и большим пальцем на левой руке и протянете его перед собой большим пальцем вниз, ваш большой палец будет указывать в направлении увеличения Y, а указательный палец будет указывать в направлении увеличения X. Я знаю, что это элементарно, но я считаю это полезным, когда думаю о переводах и вращениях. Вот почему:

когда вы переводите контекст, вы перенести начало координат в новое место на холст. Когда вы поворачиваете контекст, подумайте о повороте "L", который вы сделали левой рукой по часовой стрелке, на величину, указанную углом, который вы указываете в радианах относительно начала координат. При использовании strokeText или fillText укажите координаты относительно новых выровненных осей. Чтобы ориентировать текст так, чтобы он читался снизу вверх, вы должны перевести его в положение ниже, где вы хотите начать свои метки, повернуть на -90 градусов и заполнить или strokeText, смещая каждую метку вдоль повернутый по оси X. Что-то вроде этого должно работать:

 context.save();
 context.translate(newx, newy);
 context.rotate(-Math.PI/2);
 context.textAlign = "center";
 context.fillText("Your Label Here", labelXposition, 0);
 context.restore();

.restore () сбрасывает контекст обратно в состояние, в котором он находился при вызове .save () -- удобно для возвращения вещей обратно в "нормальный".


хотя это своего рода продолжение предыдущего ответа, он добавляет немного (надеюсь).

в основном я хочу уточнить, что обычно мы думаем о рисовании таких вещей, как draw a rectangle at 10, 3.

Итак, если мы подумаем об этом так:move origin to 10, 3, потом draw rectangle at 0, 0. Тогда все, что нам нужно сделать, это добавить поворот между ними.

еще один важный момент-выравнивание текста. Проще всего нарисовать текст на 0, 0, поэтому использование правильного выравнивания может позволить нам это сделать без измерения ширины текста.

мы все равно должны переместить текст на сумму, чтобы получить его по центру вертикально, и, к сожалению, холст не имеет большой поддержки высоты линии, так что это догадка и проверить вещь ( поправьте меня, если есть что-то лучше ).

Я создал 3 примера, которые предоставляют точку и текст с 3 выравнивания, чтобы показать, что фактическая точка на экране, где шрифт будет идти.

enter image description here

var font, lineHeight, x, y;

x = 100;
y = 100;
font = 20;
lineHeight = 15; // this is guess and check as far as I know
this.context.font = font + 'px Arial';


// Right Aligned
this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'right';
this.context.fillText('right', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);


// Center
this.context.fillStyle = 'black';
x = 150;
y = 100;

this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'center';
this.context.fillText('center', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);


// Left
this.context.fillStyle = 'black';
x = 200;
y = 100;

this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'left';
this.context.fillText('left', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);

линия this.context.fillText('right', 0, lineHeight / 2); в основном 0, 0, за исключением того, что мы немного перемещаемся, чтобы текст был центрирован рядом с точкой

вот HTML5 альтернатива homebrew:http://www.rgraph.net/ Возможно, вы сможете перепроектировать свои методы....

вы также можете рассмотреть что-то вроде Flot (http://code.google.com/p/flot/) или GCharts: (http://www.maxb.net/scripts/jgcharts/include/demo/#1) это не совсем так круто, но полностью обратно совместимо и страшно легко реализовать.

Funkodebat опубликовал отличное решение, на которое я ссылался много раз. Тем не менее, я пишу свою собственную рабочую модель каждый раз, когда мне это нужно. Итак, вот моя рабочая модель... с некоторой добавленной ясностью.

в первую очередь высота текста равна размеру пиксельного шрифта. Так вот, это было то, что я читал некоторое время назад, и это сработало в моих расчетах. Я не уверен, что это работает со всеми шрифтами, но, похоже, работает с Arial, без засечек.

кроме того, чтобы убедиться, что вы поместили весь текст на своем холсте (и не обрезайте хвосты своих "p"), вам нужно установить контексте.textBaseline*.

вы увидите в коде, что мы вращаем текст вокруг его центра. Для этого нам нужно установить контексте.textAlign = "center" и контексте.textBaseline внизу, в противном случае, мы обрезаем части нашего текста.

зачем изменять размер холста? Я обычно есть холст, который не добавляется к странице. Я использую его, чтобы нарисовать весь мой повернутый текст, затем я рисую его на другой холст, который я показываю. Например, вы можете использовать этот холст, чтобы нарисовать все метки для диаграммы (один за другим) и нарисовать скрытый холст на холсте диаграммы, где вам нужна метка (context.drawImage(hiddenCanvas, 0, 0);).

ВАЖНОЕ ПРИМЕЧАНИЕ: установите шрифт перед измерением текста и повторно примените все свои стили к контексту после изменения размера холста. Один контекст холста полностью сбрасывается при изменении размера холста.

надеюсь, что это помогает!

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var font, text, x, y;

text = "Mississippi";

//Set font size before measuring
font = 20;
ctx.font = font + 'px Arial, sans-serif';
//Get width of text
var metrics = ctx.measureText(text);
//Set canvas dimensions
c.width = font;//The height of the text. The text will be sideways.
c.height = metrics.width;//The measured width of the text
//After a canvas resize, the context is reset. Set the font size again
ctx.font = font + 'px Arial';
//Set the drawing coordinates
x = font/2;
y = metrics.width/2;
//Style
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.textBaseline = "bottom";
//Rotate the canvas and draw the text
ctx.save();
ctx.translate(x, y);
ctx.rotate(-Math.PI / 2);
ctx.fillText(text, 0, font / 2);
ctx.restore();
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">