Рисование повернутого текста на холсте HTML5
Часть веб-приложения, которое я разрабатываю, требует от меня создания гистограмм для отображения различной информации. Я полагал, что если пользовательский браузер способен, я бы рисовал их с помощью элемента canvas HTML5. У меня нет проблем с рисованием линий и баров для моих графиков, но когда дело доходит до маркировки осей, баров или линий, я столкнулся с препятствием. Как рисовать повернутый текст на элемент холста так, чтобы он выравнивался с элементом, который он маркирует? Примеры пара включают:
- Повернуть текст на 90 градусов
по часовой стрелке для обозначения оси y
- Повернуть текст на 90 градусов
по часовой стрелке для маркировки полос по вертикали
гистограмма
- Поворот текста произвольной суммой
строки меток на линейном графике
Любые указатели будут оценены.
Ответы
Ответ 1
Как и многие другие, вы, вероятно, захотите взглянуть на повторное использование существующего графического решения, но поворот текста не слишком сложный. Несколько запутанный бит (для меня) заключается в том, что вы вращаете весь контекст, а затем нарисовываете его:
ctx.rotate(Math.PI*2/(i*6));
Угол находится в радианах. Код берется из этого примера, который, как мне кажется, был сделан для части преобразования в учебнике холста MDC.
Пожалуйста, см. Ответ ниже для более полного решения.
Ответ 2
Проводя это, чтобы помочь другим с подобными проблемами. Я решил эту проблему с пятиступенчатым подходом - сохранить контекст, перевести контекст, повернуть контекст, нарисовать текст, а затем восстановить контекст в его сохраненное состояние.
Я думаю о переводах и преобразованиях в контекст как манипулирование сеткой координат, наложенной на холст. По умолчанию начало (0,0) начинается в верхнем левом углу холста. X увеличивается слева направо, Y увеличивается сверху вниз. Если вы делаете "L" с указательным пальцем и большим пальцем левой руки и удерживаете его перед собой пальцем, большой палец будет указывать в направлении увеличения Y, а указательный палец будет указывать в направлении возрастания X. Я знаю, что это элементарно, но я считаю это полезным, когда размышляю о переводах и поворотах. Вот почему:
Когда вы переводите контекст, вы перемещаете начало сетки координат в новое место на холсте. Когда вы поворачиваете контекст, подумайте о вращении "L", который вы сделали левой рукой по часовой стрелке, количество, указанное углом, указанным в радианах относительно начала координат. Когда вы нажимаетеText или fillText, указываете свои координаты относительно вновь выровненных осей. Чтобы ориентировать текст так, чтобы он читался снизу вверх, вы переводили бы в позицию ниже, где вы хотите начать свои метки, поверните на -90 градусов и заполните или потянитеText, смещая каждую метку вдоль оси 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() - удобно для возвращения возвращаемых данных к "нормальным".
Ответ 3
В то время как это похоже на предыдущий ответ, он добавляет немного (надеюсь).
В основном я хочу уточнить, что обычно мы думаем рисовать такие вещи, как draw a rectangle at 10, 3
.
Итак, если мы подумаем об этом так: move origin to 10, 3
, тогда draw rectangle at 0, 0
.
Тогда все, что нам нужно сделать, это добавить поворот между ними.
Другим важным моментом является выравнивание текста. Проще всего нарисовать текст в 0, 0, поэтому использование правильного выравнивания может позволить нам сделать это без измерения ширины текста.
Мы должны по-прежнему перемещать текст на величину, чтобы получить ее по центру по вертикали, и, к сожалению, холст не имеет большой поддержки высоты линии, так что угадайте и проверяйте вещь (исправьте меня, если что-то лучше).
Я создал 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
, за исключением того, что мы немного двигаемся, чтобы текст был центрирован вблизи точки
Ответ 4
Здесь альтернатива HTML5 для доморощенного: http://www.rgraph.net/ Возможно, вы сможете развернуть свои методы....
Вы также можете рассмотреть что-то вроде Flot (http://code.google.com/p/flot/) или GCharts: (http://www.maxb.net/scripts/jgcharts/include/demo/#1) Это не совсем так здорово, но полностью обратно совместимо и страшно легко реализовать.
Ответ 5
Funkodebat опубликовал большое решение, на которое я ссылался много раз. Тем не менее, я нахожу, что я пишу свою собственную рабочую модель каждый раз, когда мне это нужно. Итак, вот моя рабочая модель... с некоторой дополнительной ясностью.
Прежде всего, высота текста равна размеру пикселя. Теперь это было кое-что, что я прочитал некоторое время назад, и это получилось в моих расчетах. Я не уверен, что это работает со всеми шрифтами, но, похоже, работает с Arial, sans-serif.
Кроме того, чтобы убедиться, что вы поместили весь текст в свой холст (и не обрезаете хвосты с вашего "p"), вам нужно установить context.textBaseline *.
Вы увидите в коде, что мы поворачиваем текст о его центре. Для этого нам нужно установить context.textAlign = "center" и context.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;">