Это третья статья из серии заметок об изучении и просмотре холста. Полные заметки см.технология сердцевины холста
через предыдущую статьюТехнология ядра холста — как рисовать графикуПри изучении мы знаем, как рисовать произвольные многоугольники и правила заполнения картинок. В холсте больше приложений для рисования изображений и текста. В этой статье поговорим подробно о рисовании картинок и текста.
картина
На холсте мы можем нарисовать изображение прямо на холсте и использоватьimg
Теги похожи, разница в том, что изображение рисуется на холст, а не в отдельный html-элемент. холст обеспечиваетdrawImage
метод рисования изображений, этот метод можно использовать в трех формах, а именно:
-
void drawImage(image,dx,dy);
Нарисуйте изображение непосредственно в указанных координатах холста, изображение передается по изображению, а координаты передаются по dx и dy. -
void drawImage(image,dx,dy,dw,dh);
То же, что и вышеприведенная форма, за исключением того, что ширина и высота рисунка изображения указаны, а ширина и высота передаются через dw и dh. -
void drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh);
Это самая сложная и гибкая форма использования.Первый параметр это элемент изображения который нужно отрисовать.Параметры со второго по пятый задают координаты и ширину и высоту исходного изображения.Эта часть области будет отрисовываться на холст ., и другие области будут игнорироваться.Последние четыре параметра такие же, как и во второй форме, определяющие координаты, ширину и высоту в цели холста.
По количеству параметров будем называть разные формыdrawImage
, первая форма является самой простой, она заключается в том, чтобы нарисовать исходное изображение непосредственно в указанные координаты целевого холста.Ширина и высота изображения являются шириной и высотой исходного изображения и не будут масштабироваться. Во второй форме задаются ширина и высота целевой области рисования холста, затем фиксируются ширина и высота изображения, окончательно нарисованного на холсте, и изображение будет масштабироваться.Если указанные dw и dh не равны к ширине и высоте исходного изображения Скорее изображение будет сжато или растянуто. Третья форма указывает область, в которой рисуется исходное изображение, и область на целевом холсте.Через sx, sy, sw и sh мы можем выбрать только часть исходного изображения или указать полное изображение.Через dx , dy, dw, dh — это целевая область холста, которую мы хотим нарисовать.
let img = document.createElement('img'); //创建img元素
img.src = './learn9/google.png'; //指定img的src
img.addEventListener(
'load',
() => {
ctx.drawImage(img, 0, 0); // 将img元素调用drawImage(img,dx,dy)绘制出来
},
false,
);
В приведенном выше примере исходный размер этого изображения Google составляет 544 * 184, а размер области холста по умолчанию составляет 300 * 150. Вызываем первую форму для непосредственной отрисовки картинки в начало координат канвы.Картинка не масштабируется и выходит за пределы области канвы.Лишняя часть будет игнорироваться канвой. Одна вещь, чтобы отметить, что я на картинкеonload
Он начинает рисовать только в событии, потому что изображение не было загружено, и рисовать изображение напрямую нельзя. Примеры кода ниже, я просто опубликуюonload
Код в событии и код части загрузки изображения одинаковы, поэтому они опущены.
let canvasWidth = canvas.width; //获取canvas宽度
let canvasHeight = canvas.height; //获取canvas高度
ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight);
Мы указываем целевую область холста как ширину и высоту холста, изображение всегда будет отрисовываться во всем холсте, и вы также можете видеть, что нарисованное изображение деформировано. Мы можем рассчитать высоту целевой области холста, рассчитав соотношение сторон исходного изображения, рассчитать высоту целевой области холста в соответствии с шириной целевой области холста или рассчитать ширину целевой области холста в соответствии с высота целевой области холста.
let imgWidth = img.width; //获取图片的宽度
let imgHeight = img.height; //获取图片的高度
let targetWidth = canvasWidth; //指定目标canvas区域的宽度
let targetHeight = (imgHeight * targetWidth) / imgWidth; //计算出目标canvas区域的高度
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
Как видно из рисунка, целевая площадь холста рассчитывается в соответствии с соотношением сторон изображения, и, наконец, эффект, нарисованный изображением, представляет собой пропорциональное масштабирование без деформации.
Рассмотрим третий, самый сложный и гибкий способ. Используя этот метод, мы можем нарисовать красную часть буквы «о» на изображении Google.
ctx.drawImage(img, 143, 48, 90, 90, 0, 0, 90, 90);
На картинке Google координаты красной буквы o в исходной картинке (143,48), а ширина и высота 90 * 90. Мы просто рисуем эту букву в координатах (0,0) холста, и ширина и высота тоже 90*90. Можно немного усложнить.Сделайте красную букву o той же высоты, что и холст, и пропорционально увеличьте ширину, а центр круга как раз в центре холста.Реализация следующая,
let oWidth = 90; //获取字母o的宽度
let oHeight = 90; //获取字母o的高度
let targetHeight = canvas.height; //指定目标canvas区域的高度
let targetWidth = (oWidth * targetHeight) / oHeight; //计算出目标canvas区域的宽度
let targetX = (canvas.width - targetWidth) / 2; //移动目标canvas坐标X
ctx.drawImage(img, 143, 48, oWidth, oHeight, targetX, 0, targetWidth, targetHeight);
drawImage
Возвращаемое изображение первого параметра может быть не только элементом изображения, но также элементом canavs и элементом видео. Обычно закадровый холст используется для рисования невидимого за экраном холста на отображаемом в данный момент холсте. Эта часть закадрового холста будет освещена в последующей игровой части и здесь подробно обсуждаться не будет.
пиксель изображения
Есть еще три функции, связанные с рисованием картинок, ониgetImageData
,putImageData
,createImageData
. Эти функции могут напрямую изменять определенное значение пикселя в изображении, чтобы вы могли выполнять некоторые операции с изображением, например фильтры.
Давайте сначала посмотримgetImageData
метод, который называетсяlet imgData = ctx.getImageData(sx,sy,sw,sh)
, принимает четыре параметра, представляющих собой прямоугольную область области холста, левый верхний угол прямоугольной области равен (sx, sy), ширина и высота — sw и sh, а возвращаемое значение — aImageData
Объект типа, который содержит такие свойства, какwidth
,height
,data
.
-
ImageData.width
, длинное целое число без знака, представляющее ширину в пикселях этой области изображения. -
ImageData.height
, длинное целое число без знака, представляющее высоту в пикселях этой области изображения. -
ImageData.data
,ОдинUint8ClampedArray
Массив, каждые 4 ячеек в массиве, представляет собой значение пикселя. Значение изображения представлено RGBA. Эти четыре блока представляют R, G, B, A, соответственно, что означает красный, зеленый, синий и прозрачность. Диапазон значения от 0 до 255.
Следует отметить, что если мы вызываемctx.getImageData(sx,sy,sw,sh)
, прямоугольная область, представляемая параметром, превышает площадь холста, то лишняя часть будет представлена черным значением RGBA с прозрачностью 0, то есть (0,0,0,0).
let imgWidth = img.width; //获取图片的宽度
let imgHeight = img.height; //获取图片的高度
let targetWidth = canvasWidth; //指定目标canvas区域的宽度
let targetHeight = (imgHeight * targetWidth) / imgWidth; //计算出目标canvas区域的高度
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
let imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
console.log(`canvas.width = ${canvasWidth}`);
console.log(`canvas.height = ${canvasHeight}`);
console.log(imgData);
Как видите, ширина и высота нашего холста по умолчанию 300*150, черезctx.getImageData
Получите значение данных пикселя всей области холста, получитеImageData
Ширина и высота пикселя устройства также 300*150,Imagedata.data
Длина массива 180000, потому что количество пикселей этого imgData равно 300*150, и каждый пиксель представлен 4 компонентами, поэтому300*150*4 = 180000
.
когда мы проходимgetImageData
Получив пиксельные данные прямоугольной области холста, мы можем изменить этоimageData.data
Значения компонентов цвета в массиве, а затем измененныеImageData
пройти черезputImageData
метод рисования на холсте.putImageData
Есть 2 вызывающие формы использования, а именно:
-
ctx.putImageData(imgData,dx,dy)
, таким образом, imgData отрисовывается в координаты области холста (dx, dy), а размер прямоугольника области, отрисованной на холсте, равен размеру прямоугольника imgData. -
ctx.putImageData(imgData,dx,dy,dirtyX,dirtyY,dirtyW,dirtyH)
, не только указывает область холста (dx, dy), но также указывает (dirtyX, dirtyY), а также ширину и высоту dirtyW, dirtyH области грязных данных imgData. В таком виде на холст может быть отрисована только определенная область imgData.
let canvasWidth = canvas.width;
let canvasHeight = canvas.height;
let img = document.createElement('img');
img.src = './learn9/google.png';
img.addEventListener(
'load',
() => {
let imgWidth = img.width; //获取图片的宽度
let imgHeight = img.height; //获取图片的高度
let targetWidth = canvasWidth; //指定目标canvas区域的宽度
let targetHeight = (imgHeight * targetWidth) / imgWidth; //计算出目标canvas区域的高度
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
//操作ImageData像素数据
let imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
oprImageData(imgData, (r, g, b, a) => {
if (a === 0) {
return [r, g, b, 255]; //将透明的黑色像素值改变为不透明
}
return [r, g, b, a];
});
//将imgData绘制到canvas的中心。超出canvas区域将被自动忽略
ctx.putImageData(imgData, canvasWidth / 2, canvasHeight / 2);
},
false,
);
// 遍历像素数据
function oprImageData(imgData, oprFunction) {
let data = imgData.data;
for (let i = 0, l = data.length; i < l; i = i + 4) {
let pixel = oprFunction(data[i], data[i + 1], data[i + 2], data[i + 3]);
data[i] = pixel[0];
data[i + 1] = pixel[1];
data[i + 2] = pixel[2];
data[i + 3] = pixel[3];
}
}
Выше мы прошлиImageData
В массиве данных измените прозрачность значения пикселя, прозрачность которого равна от 0 до 1 (255/255=1). При обходе массива пикселей каждый раз, когда мы облегчаем,i
Значение равно плюс 4, это связано с тем, что значение пикселя представлено 4 значениями единиц массива, а именно R, G, B, A, мы можем изменить только определенное значение компонента определенного значения пикселя, например прозрачность.
ctx.putImageData(imgData, canvasWidth / 2, canvasHeight / 2, 79, 27, 50, 50);
Мы указалиImageData
В области грязных данных рисуется только красная буква о, а остальные части игнорируются. выше звонитputImageData
Ранее мы изменили прозрачность некоторых значений пикселей путем обхода данных пикселей.Этот способ манипулирования значениями пикселей очень полезен в таких областях, как обработка изображений, таких как обычные оттенки серого изображения и инвертированные цвета.
//操作ImageData像素数据
let imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
ctx.clearRect(0, 0, canvasWidth, canvasHeight); //清除canvas
oprImageData(imgData, (r, g, b, a) => {
return [255 - r, 255 - g, 255 - b, a]; //反相颜色
});
ctx.putImageData(imgData, 0, 0);
Вычтите исходное значение компонента цвета из 255 из значения RGB компонента цвета.Вы можете видеть, что цвет каждой буквы Google отличается от цвета исходного изображения. При этом меняется значение каждой цветовой составляющей, и с помощью неразумных логических вычислений можно получить разные обработанные картинки.
//操作ImageData像素数据
let imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
ctx.clearRect(0, 0, canvasWidth, canvasHeight); //清除canvas
oprImageData(imgData, (r, g, b, a) => {
let avg = (r + g + b) / 3;
return [avg, avg, avg, a]; //灰度
});
ctx.putImageData(imgData, 0, 0);
Взяв среднее значение RGB, каждая буква исходного изображения будет серой.Конечно, при расчете к каждому компоненту можно добавить коэффициент, например формулуlet avg = 0.299r + 0.587g + 0.114b
, конкретное приложение можно просмотретьGrayscale.
Последний взглядcreateImageData
, это легко понять, это создатьImageData
Объект имеет две формы, а именно:
-
ctx.createImageData(width,height)
, можно указать ширину и высоту, создатьImageData
объект,ImageData.data
Значения пикселей — это прозрачный черный цвет, который равен (0, 0, 0, 0). -
ctx.createImageData(imgData)
, вы можете указать существующийImageData
объект для создания новогоImageData
объект, вновь созданныйImageData
Ширина и высота объекта и параметры вImageData
Ширина и высота одинаковы, но значения пикселей разные.ImageData
Все значения пикселей прозрачно-черные, то есть (0, 0, 0, 0).
текст
На холсте мы можем рисовать не только графику, картинки, но и текст. Отрисовка текста относительно проста, сначала установите стиль текста текущей кисти ctx, например размер шрифта, стиль шрифта, метод выравнивания и т. д., что похоже на css.
Существует три метода, связанных с текстом, а именно:
-
strokeText(text,x,y,maxWidth?)
, рисовать указанный текстовый текст в виде штриха, в котором также указываются координаты (x, y) для рисования, и последний необязательный параметр, максимальная ширина, если нарисованный текст превышает указанныйmaxWidth
, текст будет отрисован в соответствии с максимальной шириной, расстояние между текстом будет уменьшено, и текст может быть сжат. -
fillText(text,x,y,maxWidth?)
,такой жеstrokeText
То же самое, за исключением того, что текст отрисовывается в виде заливки, а его параметры имеют то же значение. -
measureText(text)
, под текущим стилем текста измерьте значение ширины, которое будет занимать нарисованный текстовый текст, и верните объект, который имеетwidth
Атрибуты. Главное, что следует отметить, это то, что текст должен быть стилизован, прежде чем измерение будет точным.
Настройки свойств, непосредственно связанные с текстом, следующие:
-
font
, так же, как значение в css, вы можете указать размер шрифта, набор шрифтов, стиль шрифта и т. д. текста. Но на холсте,line-height
принудительно устанавливается наnormal
, значения других настроек игнорируются. -
textAlign
, установите горизонтальное выравнивание текста, необязательные значения:left
,right
,center
,start
,end
. Значение по умолчаниюstart
. Для каждого значения см.значение выравнивания текста. -
textBaseline
, установите вертикальное выравнивание текста, необязательные значения:top
,hanging
,middle
,alphabetic
,ideographic
,bottom
. Значение по умолчаниюalphabetic
. Для каждого значения см.textBaseline значение.
Конечно, есть некоторые другие свойства, которые также влияют на окончательный эффект отрисовки текста, например, добавление эффекта тени к текущему ctx или настройкаfillStyle
Стиль может быть изображением или градиентом. Это глобальные настройки свойств, которые влияют на все остальные холсты, а не только на текст, поэтому я не буду подробно обсуждать их здесь.
let textAligns = ['left', 'right', 'center', 'start', 'end']; //textAlign的取值
let colors = ['red', 'blue', 'green', 'orange', 'blueviolet']; //描边颜色
ctx.font = '18px sans-serif'; //设置font
for (let [index, textAlign] of textAligns.entries()) {
ctx.save();
ctx.textAlign = textAlign; // 设置textAlign
ctx.strokeStyle = colors[index]; //设置描边颜色
ctx.strokeText(textAlign, width / 2, 20 + index * 30); //使用描边绘制文本
ctx.restore();
}
мы кладемtextAlign
Все свойстваstart
а такжеleft
тот же эффект,end
а такжеright
имеет тот же эффект, потому чтоstart
а такжеend
Это связано с начальным направлением текущего локального текста.Если он начинается слева направо, тоstart
а такжеleft
То же самое, а если справа налево начинать, тоstart
С участиемright
Эффект тот же.
let textBaselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'];
let colors = ['red', 'blue', 'green', 'orange', 'blueviolet', 'cyan']; //描边颜色
ctx.font = '18px sans-serif'; //设置font
for (let [index, textBaseline] of textBaselines.entries()) {
ctx.save();
ctx.textBaseline = textBaseline; // 设置textBaseline
ctx.strokeStyle = colors[index]; //设置描边颜色
ctx.strokeText('abj', 10 + index * 50, height / 2); //使用描边绘制文本
ctx.restore();
}
мы ставим сноваtextBaseline
Все значения , устанавливаются один раз, и эффект, который вы видите, показан на рисунке выше. Наиболее часто используемым должно бытьtop
,middle
,alphabetic
,bottom
, где значение по умолчаниюalphabetic
.
measureText
Это также метод, который больше используется в реальном бизнесе.Этот метод может измерять ширину, занимаемую рисованием указанного текста в соответствии с текущим установленным стилем текста. Особенно при рисовании табличных данных или каких-то аналитических графиков вам нужно рисовать пояснительный текст, но вы хотите определять координаты рисования текста по текущему положению мыши, чтобы не выходить за пределы видимой области холста. Этот метод относительно прост в использовании и возвращаетwidth
свойства объекта, этоwidth
Значение свойства является результатом измерения. Нет возможности измерить высоту текста на холсте, однако на практике это часто бываетWЗначение ширины, измеренное буквой плюс немного, можно грубо рассматривать как значение высоты текущего текста.
ctx.font = '18px sans-serif'; //设置font,一定得先设置font属性,才能测量准确
let textWidth = ctx.measureText('W').width;
let textHeight = textWidth + textWidth / 6;
console.log(`当前文本W的宽度:${textWidth}`);
console.log(`当前文本W的高度:${textHeight}`);
резюме
Эта статья в основном учит, как использовать холстdrawImage
рисовать картинки и как ими пользоватьсяgetImageData
а такжеputImageData
Для обработки значений пикселей изображения, таких как обычная обработка изображений в градациях серого, инвертирование цвета и т. д. Также были рассмотрены некоторые связанные методы и свойства рисования текста на холсте.Эти знания аналогичны CSS, и их легче и проще понять.