JavaScript Canvas
Создание канваса
Создание на html-странице области рисования canvas размером 600x40 и вывод в её центре прямоугольника размером 100x20:
<html>
<head>
<style>
canvas {background-color: #ffffff; border: 1px dashed black; }
</style>
</head>
<body>
<canvas id= "canvas" width= "600" height= "40" ></canvas>
<script>
plot();
function plot()
{
var canvas = document.getElementById( 'canvas' );
var ctx = canvas.getContext( '2d' );
ctx.fillRect(canvas.width/2-15, canvas.height/2-10, 100, 20);
}
</script>
</body>
</html>
|
Графические примитивы
Прямоугольники:
ctx.fillRect (x,y, width,height);
ctx.strokeRect(x,y, width,height);
ctx.clearRect (x,y, width,height);
|
Настройки:
ctx.globalAlpha = 0.0
ctx.fillStyle = "orange" ;
ctx.fillStyle = "#FFA500" ;
ctx.fillStyle = "rgb(255,165,0)" ;
ctx.fillStyle = "rgba(255,165,0,1)" ;
ctx.strokeStyle = "orange" ;
ctx.setLineDash([5,7]);
ctx.lineWidth = 5;
сtx.lineCap = "round" ;
|
Линии:
beginPath();
...
closePath();
stroke();
fill();
|
Возможные команды линий:
moveTo(x, y);
lineTo(x, y);
arc(x, y, radius, startAngle, endAngle, anticlockwise);
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
var p = new Path2D( "M 525,268 l 60,0 0,30 -60,0 0,-30 Z" );
ctx.stroke(p);
|
- Для arc: x и y это координаты центра круга;
startAngle и endAngle - точки начала и конца арки в радианах (углы от оси x).
Параметр anticlockwise равен true,
то дуга рисуется против часовой стрелки, иначе - по часовой. Угол отсчитывается от оси x в сторону оси y, которая направлена вниз (!!!)
- Для bezierCurveTo: cp1x и cp1y
- координаты первой точки контроля, а cp2x и cp2y - второй точки контроля,
x и y - координаты конечной точки.
Примеры линий и окружности:
ctx.beginPath();
ctx.arc(200, 90, 5, 0, Math.PI*2, true );
ctx.stroke();
ctx.setLineDash([5,7]);
ctx.beginPath();
ctx.moveTo(10,30);
ctx.lineTo(200,90);
ctx.lineTo(200,5);
ctx.stroke();
|
|
|
|
Кружки - точки контроля линии Безье:
ctx.beginPath();
ctx.moveTo(10,130);
ctx.bezierCurveTo(50,190, 159,70, 200,130);
ctx.lineTo(200,190);
ctx.lineTo(10,190);
ctx.closePath();
ctx.fillStyle = 'red' ;
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = 'green' ;
ctx.stroke();
|
|
|
|
Градиент закрашивается в той же системе координат, какой рисуется фигура,
а не внутренних координат фигуры.
Если мы изменим x,y фигуры на 100,100, то градиент будет по-прежнему находиться в начале экрана.
var grad = ctx.createLinearGradient(0,0,200,0);
grad.addColorStop(0, "white" );
grad.addColorStop(0.5, "red" );
grad.addColorStop(1, "black" );
ctx.fillStyle = grad;
ctx.fillRect(0,0,400,50);
|
|
|
|
Текст
ctx.font = "italic 30pt Arial" ;
ctx.font = "8pt Consolas" ;
ctx.fillStyle = "#00F" ;
ctx.strokeStyle = "#F00" ;
ctx.textAlign = "center" ;
ctx.textBaseline = "middle" ;
ctx.fillText ( "Fill txt" , 20,50);
ctx.strokeText( "Stroke txt" , 20,70);
ctx.strokeStyle = "#F00" ;
ctx.shadowColor = "#F00" ;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.shadowBlur = 5;
ctx.strokeText( "Shadow text" , 20, 100);
var grad = ctx.createLinearGradient(0,0,0,60);
grad.addColorStop(0.0, 'rgba(0, 0, 255, 1)' );
grad.addColorStop(0.3, 'rgba(128, 0, 255, 0.6)' );
grad.addColorStop(0.6, 'rgba(0, 0, 255, 0.4)' );
grad.addColorStop(1.0, 'rgba(0, 255, 0, 0.2)' );
ctx.fillStyle = grad;
var pat = ctx.createPattern(img, 'repeat' );
ctx.fillStyle = pat;
|
Пример функции, которая разбивает текст на строки, чтобы он поместился в ящике.
function wrapText(context, text, x, y, maxWidth, lineHeight)
{
var words = text.split( " " );
var countWords = words.length;
var line = "" ;
for ( var n = 0; n < countWords; n++) {
var testLine = line + words[n] + " " ;
var testWidth = context.measureText(testLine).width;
if (testWidth > maxWidth) {
context.fillText(line, x, y);
line = words[n] + " " ;
y += lineHeight;
}
else {
line = testLine;
}
}
context.fillText(line, x, y);
}
|
Картинки
Загрузка картинки:
var img = new Image();
img.src = 'picture.png' ;
|
Если к моменту вызова drawImage загрузка ещё не закончилась, скрипт "подвисает" до её окончания. Чтобы избежать этого, используйте обработчик события onload:
var img = new Image();
img.src = 'myImage.png' ;
img.onload = function (){ }
|
В обработчике
img.onload можно, например, написать
img.ok=true и рисовать картинку, только
если определено это поле.
Собственно рисование картинки:
ctx.drawImage(img, dx, dy);
ctx.drawImage(img, dx, dy, dWidth, dHeight);
ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
|
- img - идентификатор изображения;
- sx, sy - верхний левый угол вырезаемого куска из источника (source)
- sWidth, sHeight, - высота и ширина вырезанного изображения;
- dx, dy - положение изображения на холсте (destination);
- dWidth, dHeight - масштаб изображения
Пример загрузчика картинок
var imageList = [
{src: "pic/boy.jpg" },
{src: "pic/girl.jpg" },
];
var imageLoadOkTot = 0;
var imageLoadErrTot = 0;
var imageLoadErrStr = "" ;
function init()
{
for ( var i in imageList){
imageList[i].img = new Image();
imageList[i].ok= false ;
imageList[i].img.onload = function (){
imageList[i].ok= true ; imageLoadOkTot++;
}
imageList[i].img.onerror = function (evt) {
imageLoadErrTot++;
imageLoadErrStr += "[" + this .src+ ":" +evt.type+ "]" ;
}
imageList[i].img.src = imageList[i].src;
}
}
|
Буферы пикселей
Шахматная доска:
var data = ctx.createImageData(100,100);
for ( var x=0; x < data.width; x++) {
for ( var y=0; y < data.height; y++) {
var val = 0;
var h = (Math.floor(x/4)%2 === 0);
var v = (Math.floor(y/4)%2 === 0);
if ( (h && !v) || (!h && v)) val = 255; else val = 0;
var index = (y*data.width+x)*4;
data.data[index] = val;
data.data[index+1] = val;
data.data[index+2] = val;
data.data[index+3] = 255;
}
}
ctx.putImageData(data,0,0);
|
Инверсия фото:
var img = new Image();
img.onload = function ()
{
ctx.drawImage(img, 0, 0);
var data = ctx.getImageData(0,0, img.width,img.height);
for ( var n=0; n < data.width*data.height; n++){
var index = n*4;
data.data[index] = 255-data.data[index];
data.data[index+1] = 255-data.data[index+1];
data.data[index+2] = 255-data.data[index+2];
}
ctx.putImageData(data,0,100);
}
img.src = "andromeda_galaxy.jpg" ;
|
В Google Chrome по умолчанию чтение с диска локальных файлов запрещно. По этой причине существуют проблемы с
getImageData, если на канвасе нарисована картинка.
Чтобы это обойти,
необходимо все Chrome-ы закрыть, а потом запустить с параметром --allow-file-access-from-files
(на иконке правая кнопка мыши, Properties и в поле Target добавляем этот параметр).
Матрицы
Преобразования, это последовательный перенос, поворот и скейлинг канваса с последующим рисованием его на "исходном" канвасе:
ctx.save();
ctx.translate(canvas.width/2, 0);
ctx.strokeStyle = "red" ; ctx.strokeRect(0,0, 100,50);
ctx.rotate(30 * Math.PI / 180);
ctx.strokeStyle = "green" ; ctx.strokeRect(0,0, 100,50);
ctx.scale(0.5,0.75);
ctx.strokeStyle = "blue" ; ctx.strokeRect(0,0, 100,50);
ctx.restore();
|
ctx.save();
ctx.translate(canvas.width/2, 0);
ctx.strokeStyle = "red" ; ctx.strokeRect(0,0, 100,50);
ctx.scale(0.5,0.75);
ctx.strokeStyle = "green" ; ctx.strokeRect(0,0, 100,50);
ctx.rotate(30 * Math.PI / 180);
ctx.strokeStyle = "blue" ; ctx.strokeRect(0,0, 100,50);
ctx.restore();
|
Пример: Крутилка
В центре канваса врашается линия на концах которой находятся также вращающиеся квадратики:
var canvas;
var ctx;
var angle = 0, box1X=-50, box2X=50;
function run()
{
canvas = document.getElementById( 'canvas' );
ctx = canvas.getContext( '2d' );
setInterval(rotate, 100);
}
function rotate()
{
ctx.clearRect(0,0, canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2, canvas.height/2);
ctx.strokeRect(-5,-5, 10, 10);
ctx.scale(2,2);
ctx.rotate(angle * Math.PI / 180);
ctx.beginPath();
ctx.moveTo(box1X, 0); ctx.lineTo(box2X, 0);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue" ;
box(box1X, 0, 50, 10, 1,1, 10*angle);
box(box2X, 0, 50, 10, 2,2, 10*angle);
ctx.restore();
angle+=1;
}
function box(x,y, w, h, sx, sy, a)
{
ctx.save();
ctx.translate(x,y);
ctx.scale(sx, sy);
ctx.rotate(a*Math.PI/180);
ctx.fillRect(-w/2, -h/2, w, h);
ctx.restore();
}
|
События
var canvas;
var ctx;
function run()
{
canvas = document.getElementById( 'canvas' );
ctx = canvas.getContext( '2d' );
canvas.addEventListener( "mousedown" , mouseDn, false );
canvas.addEventListener( "mouseup" , mouseUp, false );
canvas.addEventListener( "mousemove" , mouseMv, false );
setInterval(timer, 100);
}
function mouseDn(event)
{
var rct = canvas.getBoundingClientRect();
var x = event.clientX - rct.left;
var y = event.clientY - rct.top;
ctx.fillText(x+ "," +y, x+10, y+10);
}
function timer()
{
}
|
SVG
Файл векторной графики (см. детали на w3schools):
<svg width= "600" height= "400" >
<circle cx= "116" cy= "67" r= "24" stroke= "black" fill= "#dddddd" ></circle>
<line x1= "0" y1= "0" x2= "200" y2= "200" style= "stroke:rgb(255,0,0); stroke-width:2" ></line>
<text x= "0" y= "15" fill= "red" >I love SVG!</text>
Sorry, your browser does not support inline SVG.
</svg>
|
Справочник по svg-формату см.
здесь.
См. также