| Компоненты | - Графика |
Графика
Описание
Элементы этой вкладки предоставляют доступ к богатым возможностям HTML5 Canvas, поэтому вся информация, доступная по этой теме в интернете применима и к данным элементам.
Первое знакомство
Для начала попробуем нарисовать самую простую фигуру на элементе PaintBox и убедиться, что все работает. Например, линию. Для этого нам понадобится элемент Line:
После запуска этой схемы на форме будет отображена одна единственная линия черного цвета:
Обратите внимание: для корректной отрисовки графики метод doDraw элемента PaintBox должен быть вызван хотя бы раз. В дальнейшем же рисовать можно поверх уже отрисованного с использованием точки Canvas.
Add(MainForm,1,56,56)
{
link(onCreate,2:doDraw,[])
}
Add(PaintBox,2,126,56)
{
Left=14
Top=14
Width=#3:373|
Height=#3:254|
link(onDraw,3:doDraw,[])
}
Add(Line,3,189,56)
{
X2=200
Y2=200
}
После запуска этой схемы на форме будет отображена одна единственная линия черного цвета:
Обратите внимание: для корректной отрисовки графики метод doDraw элемента PaintBox должен быть вызван хотя бы раз. В дальнейшем же рисовать можно поверх уже отрисованного с использованием точки Canvas.
Добавляем цвета
Цвета заливки и контуров задаются глобально (так же, например, сделано и в OpenGL), т.е. для текущей канвы указывается отдельно какого цвета будет заливка, а какого контуры и в дальнейшем все отрисовываемые фигуры будут использовать именно эти настройки для своего отображения.
В примере ниже нарисуем для прямоугольника с разным цветом заливки, но одинаковым цветом контура:
Цвет контура задается один раз и оба прямоугольника используют именно его для отрисовки. Цвет же заливки задается для каждого прямоугольника свой непосредственно перед их отрисовкой.
Вы могли обратить внимание на то, что контур прямоугольников не четкий и смазан на границах. Это происходит потому, что по умолчанию в Canvas включен режим сглаживания, который делает косые линии менее рваными и более плавными. Далее мы узнаем, как можно избежать этого эффекта там, где он не нужен.
В примере ниже нарисуем для прямоугольника с разным цветом заливки, но одинаковым цветом контура:
Add(MainForm,1,70,56)
{
link(onCreate,2:doDraw,[])
}
Add(PaintBox,2,126,56)
{
Left=14
Top=14
Width=#3:373|
Height=#3:254|
link(onDraw,6:doStroke,[])
}
Add(Rectangle,4,336,56)
{
X1=50
Y1=50
X2=30
Y2=30
Type=2
}
Add(FillStyle,5,287,56)
{
Color=HotPink
link(onFill,4:doDraw,[])
}
Add(StrokeStyle,6,182,56)
{
Color=Blue
link(onStroke,7:doEvent1,[])
}
Add(Hub,7,238,56)
{
link(onEvent1,5:doFill,[])
link(onEvent2,9:doFill,[(269,69)(269,111)])
}
Add(Rectangle,8,336,105)
{
X1=120
Y1=160
X2=30
Y2=30
Type=2
}
Add(FillStyle,9,287,105)
{
Color=Lime
link(onFill,8:doDraw,[])
}
Цвет контура задается один раз и оба прямоугольника используют именно его для отрисовки. Цвет же заливки задается для каждого прямоугольника свой непосредственно перед их отрисовкой.
Вы могли обратить внимание на то, что контур прямоугольников не четкий и смазан на границах. Это происходит потому, что по умолчанию в Canvas включен режим сглаживания, который делает косые линии менее рваными и более плавными. Далее мы узнаем, как можно избежать этого эффекта там, где он не нужен.
Использование градиентов
Пользователю для использования доступно два типа градиента: линейный и радиальный. При этом градиент может содержать любое количество цветов, между которыми автоматически строятся все промежуточные.
В случае линейного градиента построение происходит вдоль отрезка, заданного координатами начальной и конечной точки в свойствах самого элемента. Для радиального градиента построение палитры цветов происходит от круга №1 до круга №2, описанных координатами цента и радиусами в свойствах элемента. При этом ключевые цвета, которые задаются элементами GradientStopColor, имеют индекс, лежащий в диапазоне от 0 до 1 с любым шагом и определяющий, на каком расстоянии в % будет желать ключевой цвет на отрезке (в случае линейного градиента) или промежуточном круге (в случае радиального градиента).
В примере ниже мы попробуем нарисовать прямоугольник и круг с заливкой из линейного градиента, который имеет три цвета - черный в начале отрезка(index = 0), желтый в середине (index = 0.5) и белый в конце (index = 1):
Вот что нарисует эта схема после запуска:
Следует обратить внимание на следующие моменты:
- градиент создается только один раз при старте программы (если конечно его конфигурацию не необходимо менять в ходе выполнения приложения) и затем используется как обычный цвет (т.е. через точку Color)
- созданный градиент не масштабируется и если он построен на отрезке длиной 100, то фигура длиной 200 залитая таким градиентом, в первых 100 пикселях будет залита градиентом, а в последних 100 тем цветом, который был в градиенте последним (т.е. index = 1)
- по умолчанию фигура заливается градиентом не от своего левого верхнего угла, как это казалось бы логичным, а от левого верхнего угла канвы элемента PaintBox, что можно заметить по фону круга из примера выше. Если необходимо сдвинуть начальную точку, от которой будет происходить заливку фигур градиентом, то необходимо воспользоваться элементом переноса координат, речь о котором пойдет ниже.
В случае линейного градиента построение происходит вдоль отрезка, заданного координатами начальной и конечной точки в свойствах самого элемента. Для радиального градиента построение палитры цветов происходит от круга №1 до круга №2, описанных координатами цента и радиусами в свойствах элемента. При этом ключевые цвета, которые задаются элементами GradientStopColor, имеют индекс, лежащий в диапазоне от 0 до 1 с любым шагом и определяющий, на каком расстоянии в % будет желать ключевой цвет на отрезке (в случае линейного градиента) или промежуточном круге (в случае радиального градиента).
В примере ниже мы попробуем нарисовать прямоугольник и круг с заливкой из линейного градиента, который имеет три цвета - черный в начале отрезка(index = 0), желтый в середине (index = 0.5) и белый в конце (index = 1):
Add(MainForm,1,56,56)
{
link(onCreate,4:doEvent1,[])
}
Add(PaintBox,2,161,63)
{
Left=14
Top=14
Width=#3:373|
Height=#3:254|
link(onDraw,6:doFill,[])
}
Add(LinearGradient,3,161,126)
{
X2=100
link(onCreate,7:doAdd,[])
link(Canvas,2:Canvas,[])
}
Add(Hub,4,126,56)
{
link(onEvent1,3:doCreate,[(150,62)(150,132)])
link(onEvent2,2:doDraw,[])
}
Add(Rectangle,5,280,63)
{
X2=200
Y2=50
link(onDraw,10:doDraw,[])
}
Add(FillStyle,6,224,63)
{
link(onFill,5:doDraw,[])
link(Color,3:Gradient,[(237,49)(202,49)(202,172)(167,172)])
}
Add(GradientStopColor,7,224,126)
{
link(onAdd,8:doAdd,[])
}
Add(GradientStopColor,8,280,126)
{
Index=0.5
Color=Yellow
link(onAdd,9:doAdd,[])
}
Add(GradientStopColor,9,336,126)
{
Index=1
Color=White
}
Add(Circle,10,336,63)
{
X=75
Y=150
Radius=60
}
Вот что нарисует эта схема после запуска:
Следует обратить внимание на следующие моменты:
- градиент создается только один раз при старте программы (если конечно его конфигурацию не необходимо менять в ходе выполнения приложения) и затем используется как обычный цвет (т.е. через точку Color)
- созданный градиент не масштабируется и если он построен на отрезке длиной 100, то фигура длиной 200 залитая таким градиентом, в первых 100 пикселях будет залита градиентом, а в последних 100 тем цветом, который был в градиенте последним (т.е. index = 1)
- по умолчанию фигура заливается градиентом не от своего левого верхнего угла, как это казалось бы логичным, а от левого верхнего угла канвы элемента PaintBox, что можно заметить по фону круга из примера выше. Если необходимо сдвинуть начальную точку, от которой будет происходить заливку фигур градиентом, то необходимо воспользоваться элементом переноса координат, речь о котором пойдет ниже.
Отрисовка изображений
Для того, чтобы отрисовать картинку на канве её необходимо загрузить в приложение. Проще всего это можно сделать с помощью элемента Image. В примере ниже это показано более наглядно:
Исходное изображение показано на форме для примера, в реальных же схемах его можно скрыть, установив свойство Visible=false. Следует обратить внимание на то, что рисовать картинку на канве желательно только после ее загрузки, т.е. после срабатывания события onLoad элемента Image.
Add(PaintBox,3,140,119)
{
Left=21
Top=56
Width=#3:324|
Height=#3:198|
link(onDraw,5:doDraw,[])
}
Add(Image,4,231,56)
{
Left=21
Top=14
Width=#2:86|
Height=#2:44|
URL=#17:/img/commands.png|
link(onLoad,3:doDraw,[(277,62)(277,44)(126,44)(126,125)])
}
Add(DrawImage,5,217,119)
{
X=50
Y=50
link(Image,4:Image,[])
}
Исходное изображение показано на форме для примера, в реальных же схемах его можно скрыть, установив свойство Visible=false. Следует обратить внимание на то, что рисовать картинку на канве желательно только после ее загрузки, т.е. после срабатывания события onLoad элемента Image.
Рисование по контурам
Если необходимо нарисовать более сложную фигуру, чем прямоугольник или круг, то можно воспользоваться элементами рисования по контурам. Они позволяют нарисовать абсолютно любую фигуру из отдельных линий, дуг и кривых. Так же контуры следует использовать в том случае, если необходимо нарисовать множество стандартных фигур одновременно, т.к. это будет более производительное решение.
В примере ниже показано, как можно нарисовать стандартный зеленый крест, которым помечают аптеки:
Следует обратить внимание на то, что любой контур должен начинаться с элемента PathCreator со свойством Mode=Begin и заканчиваться им же со свойством Mode=End. При этом первая и последняя точки контура автоматически соединяются прямой линией.
В примере ниже показано, как можно нарисовать стандартный зеленый крест, которым помечают аптеки:
Add(MainForm,1,56,119)
{
link(onCreate,3:doDraw,[])
}
Add(PaintBox,3,140,119)
{
Left=21
Top=56
Width=#3:324|
Height=#3:198|
link(onDraw,11:doEvent1,[])
}
Add(PathDrawer,6,287,182)
{
}
Add(PathCreator,7,238,119)
{
link(onPath,8:doMoveTo,[])
}
Add(MoveTo,8,287,119)
{
Y=25
link(onMoveTo,9:doLineTo,[])
}
Add(LineTo,9,336,119)
{
X=25
Y=25
link(onLineTo,12:doLineTo,[])
}
Add(FillStyle,10,238,182)
{
Color=LimeGreen
link(onFill,6:doDrawPath,[])
}
Add(Hub,11,189,119)
{
link(onEvent1,7:doPath,[])
link(onEvent2,10:doFill,[(220,132)(220,188)])
}
Add(LineTo,12,385,119)
{
X=25
link(onLineTo,13:doLineTo,[])
}
Add(LineTo,13,434,119)
{
X=50
link(onLineTo,14:doLineTo,[])
}
Add(LineTo,14,483,119)
{
X=50
Y=25
link(onLineTo,15:doLineTo,[])
}
Add(LineTo,15,532,119)
{
X=75
Y=25
link(onLineTo,16:doLineTo,[])
}
Add(LineTo,16,581,119)
{
X=75
Y=50
link(onLineTo,17:doLineTo,[])
}
Add(LineTo,17,630,119)
{
X=50
Y=50
link(onLineTo,18:doLineTo,[])
}
Add(LineTo,18,679,119)
{
X=50
Y=75
link(onLineTo,19:doLineTo,[])
}
Add(LineTo,19,728,119)
{
X=25
Y=75
link(onLineTo,21:doLineTo,[])
}
Add(PathCreator,20,875,119)
{
Mode=1
}
Add(LineTo,21,777,119)
{
X=25
Y=50
link(onLineTo,22:doLineTo,[])
}
Add(LineTo,22,826,119)
{
Y=50
link(onLineTo,20:doPath,[])
}
Следует обратить внимание на то, что любой контур должен начинаться с элемента PathCreator со свойством Mode=Begin и заканчиваться им же со свойством Mode=End. При этом первая и последняя точки контура автоматически соединяются прямой линией.
Изменение системы координат
Достаточно часто гораздо проще рисовать сцену или отдельные объекты на ней не по абсолютным координатам фигур, а по относительным. Для решения этой задачи применяются элементы переноса начала координат и трансформации текущей матрицы отображения (точно, как в OpenGL).
В примере ниже показано, как отрисовать 5 квадратов вдоль дуги:
Как правило после изменения начала координат или матрицы отображения и отрисовки фигур необходимо вернуть все обратно. Для этого используется элемент CanvasState, который как раз это и позволяет делать. Однако не стоит им пользоваться без лишней необхдоимости, т.к. кроме сохранения текущей матрицы отображения он сохраняет практически все состояние канвы (цвета, стили и т.д.), что негативно отражается на производительности. Скажем если в схеме точка начала координат была сдвинута 5 раз на 20 пикселей, то лучше вместо CanvasState вставить еще одни сдвиг на -100 пикселей (т.е. переместить обратно).
Изменение начала координат влияет не только на то, где будет нарисована следующая фигура, но и на положение градиента (об этом говорилось в предыдущих абзацах) и на сглаживание вертикальных и горизонтальных линий. В примере ниже наглядно продемонстрированна эта особенность канвы:
Первый прямоугольник рисуется как есть и его контуры остаются сглаженными (размытыми), а перед отрисовкой второго выполняется перенос системы координат на 0.5 пикселей по обоим осям. В результате контуры становятся абсолютно четкими (т.е. ровно в 1 пиксель толщиной). На сглаживание линий, отрисованных под другими углами это однако никак не влияет.
В примере ниже показано, как отрисовать 5 квадратов вдоль дуги:
Add(MainForm,1,56,91)
{
link(onCreate,2:doDraw,[])
}
Add(PaintBox,2,119,91)
{
Left=35
Top=28
Width=#3:324|
Height=#3:226|
link(onDraw,3:doFor,[])
}
Add(For,3,182,91)
{
Start=1
End=6
link(onEvent,6:doData,[])
}
Add(CanvasTranslate,5,280,91)
{
X=50
link(onTranslate,7:doDraw,[])
link(Canvas,2:Canvas,[(286,77)(165,77)(165,137)(125,137)])
}
Add(DoData,6,231,91)
{
link(onEventData,5:doTranslate,[])
}
Add(Rectangle,7,329,91)
{
X2=20
Y2=20
link(onDraw,8:doRotate,[])
}
Add(CanvasRotate,8,378,91)
{
Angle=0.2
}
Как правило после изменения начала координат или матрицы отображения и отрисовки фигур необходимо вернуть все обратно. Для этого используется элемент CanvasState, который как раз это и позволяет делать. Однако не стоит им пользоваться без лишней необхдоимости, т.к. кроме сохранения текущей матрицы отображения он сохраняет практически все состояние канвы (цвета, стили и т.д.), что негативно отражается на производительности. Скажем если в схеме точка начала координат была сдвинута 5 раз на 20 пикселей, то лучше вместо CanvasState вставить еще одни сдвиг на -100 пикселей (т.е. переместить обратно).
Изменение начала координат влияет не только на то, где будет нарисована следующая фигура, но и на положение градиента (об этом говорилось в предыдущих абзацах) и на сглаживание вертикальных и горизонтальных линий. В примере ниже наглядно продемонстрированна эта особенность канвы:
Add(MainForm,1,56,91)
{
link(onCreate,2:doDraw,[])
}
Add(PaintBox,2,119,91)
{
Left=35
Top=28
Width=#3:324|
Height=#3:226|
link(onDraw,10:doDraw,[])
}
Add(Rectangle,10,182,91)
{
X1=10
Y1=10
X2=50
Y2=50
Type=1
link(onDraw,11:doTranslate,[])
}
Add(CanvasTranslate,11,245,91)
{
X=0.5
Y=0.5
link(onTranslate,12:doDraw,[])
}
Add(Rectangle,12,308,91)
{
X1=10
Y1=80
X2=50
Y2=50
Type=1
}
Первый прямоугольник рисуется как есть и его контуры остаются сглаженными (размытыми), а перед отрисовкой второго выполняется перенос системы координат на 0.5 пикселей по обоим осям. В результате контуры становятся абсолютно четкими (т.е. ровно в 1 пиксель толщиной). На сглаживание линий, отрисованных под другими углами это однако никак не влияет.
Анимация
Что делать, если в сцене необходимо добавить анимацию? Да, можно использовать стандартный Timer, но для отрисовки графики он совершенно не пригоден по двум причинам:
1) таймер не останавливается тогда, когда вкладка не активна или браузер вовсе свернут, а это значит, что ваша анимация продолжит отъедать ресурсы компьютера даже тогда, когда пользователь на нее не смотрит
2) обычный таймер ничего не знает о том, когда перерисовывается страница браузера и при его использовании чаще всего случается рассинхронизация основного цикла рендера страницы и цикла рендера вашей канвы
Исправить оба этих недостатка призван специальный элемент RenderFrameTimer. Он работает с использованием API, которое предоставляет браузер для отрисовки сцен в том же цикле, в котором рисуется анимация на всей странице, что положительно сказывается на производительности в целом. У этого таймера нет настройки интервала, т.к. он всегда старается выдать 60 событий в секунду, если это возможно. Данной частоты вполне достаточно для любого рода анимаций. Ниже представлен простой пример, демонстрирующий работу этого таймера:
1) таймер не останавливается тогда, когда вкладка не активна или браузер вовсе свернут, а это значит, что ваша анимация продолжит отъедать ресурсы компьютера даже тогда, когда пользователь на нее не смотрит
2) обычный таймер ничего не знает о том, когда перерисовывается страница браузера и при его использовании чаще всего случается рассинхронизация основного цикла рендера страницы и цикла рендера вашей канвы
Исправить оба этих недостатка призван специальный элемент RenderFrameTimer. Он работает с использованием API, которое предоставляет браузер для отрисовки сцен в том же цикле, в котором рисуется анимация на всей странице, что положительно сказывается на производительности в целом. У этого таймера нет настройки интервала, т.к. он всегда старается выдать 60 событий в секунду, если это возможно. Данной частоты вполне достаточно для любого рода анимаций. Ниже представлен простой пример, демонстрирующий работу этого таймера:
Add(MainForm,1,56,91)
{
Height=#3:379|
link(onCreate,30:doDraw,[])
}
Add(PaintBox,30,147,91)
{
Left=21
Top=21
Width=#3:359|
Height=#3:317|
link(onDraw,34:doStroke,[])
}
Add(RenderFrameTimer,31,315,91)
{
link(onTimer,36:doDraw,[])
}
Add(Circle,32,427,91)
{
Radius=30
Type=1
link(onDraw,37:doNext,[])
link(Radius,37:Value,[(447,77)(526,77)(526,136)(489,136)])
}
Add(StrokeStyle,34,203,91)
{
Color=Yellow
link(onStroke,35:doFill,[])
}
Add(FillStyle,35,259,91)
{
Color=rgba(0,0,0,0.05)
link(onFill,31:doTimer,[])
}
Add(Rectangle,36,371,91)
{
X2=400
Y2=400
link(onDraw,32:doDraw,[])
link(Canvas,30:Canvas,[(377,77)(130,77)(130,137)(153,137)])
}
Add(Counter,37,483,91)
{
Max=300
}
Отрисовка видео
Замечательной особенностью HTML5 Canvas является возможность рисовать на канве не только картинки, но и видео. Посмотрим на простом примере, как это можно сделать:
Через ту же точку HTMLElement можно на одной канве отрисовать другую.
Add(PaintBox,2,168,119)
{
Left=7
Top=147
Width=#3:202|
Height=#3:118|
link(onDraw,4:doDraw,[])
}
Add(Button,3,112,119)
{
Left=252
Top=112
Width=#2:94|
Height=#2:70|
Caption=#4:Draw|
link(onClick,2:doDraw,[])
}
Add(DrawImage,4,224,119)
{
link(Image,5:HTMLElement,[])
}
Add(VideoPlayer,5,231,63)
{
Left=7
Height=#3:136|
URL=#41:http://www.w3schools.com/html/mov_bbb.ogg|
Point(HTMLElement)
}
Через ту же точку HTMLElement можно на одной канве отрисовать другую.
BB-code статьи для вставки
Всего комментариев: 0
(комментарии к статье еще не добавлены)