Сведения об API холста

внешний интерфейс алгоритм WebGL Canvas

Canvas API

Обзор

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


начать использовать

  • Сначала создайте новый<canvas>Элементы веб-страницы.
<canvas id="myCanvas" width="400" height="200">
你的浏览器不支持canvas!
</canvas>

В приведенном выше коде, если браузер не поддерживает этот API, он отобразит<canvas>Текст посередине надписи - "Ваш браузер не поддерживает холст!".

  • каждыйcanvasУзлы имеют соответствующиеcontextobject (объект контекста), здесь определяется Canvas APIcontextнад объектом, поэтому вам нужно получить этот объект, используяgetContextметод.
var canvas = document.getElementById('myCanvas');

if (canvas.getContext) {
  var ctx = canvas.getContext('2d');
}

В приведенном выше кодеgetContextметод, задающий параметры2d, указывая на то, чтоcanvasУзлы используются для создания 2D-шаблонов (т. е. плоских шаблонов). если параметрwebgl, что означает, что он используется для создания 3D-изображений (т. е. трехмерных паттернов), и эта часть на самом деле называется WebGL API.


метод рисования

canvasХолст предоставляет плоское пространство для рисования, каждая точка в этом пространстве имеет свои собственные координаты, x представляет собой горизонтальную координату, а y представляет собой вертикальную координату. Начало (0, 0) расположено в верхнем левом углу изображения, положительная ось x — это начало справа, а положительная ось y — начало координат вниз.

(1) Нарисуйте путь

beginPathметод указывает, чтобы начать рисовать путь,moveTo(x, y)метод установки начальной точки сегмента линии,lineTo(x, y)метод установки конечной точки сегмента линии,strokeМетод используется для окрашивания прозрачных сегментов линий.

ctx.beginPath(); // 开始路径绘制
ctx.moveTo(20, 20); // 设置路径起点,坐标为(20,20)
ctx.lineTo(200, 20); // 绘制一条到(200,20)的直线
ctx.lineWidth = 1.0; // 设置线宽
ctx.strokeStyle = '#CC0000'; // 设置线的颜色
ctx.stroke(); // 进行线的着色,这时整条线才变得可见

Методы moveto и lineto можно использовать несколько раз. Наконец, вы также можете использовать метод closePath для автоматического рисования прямой линии от текущей точки до начальной точки для формирования замкнутого графика, устраняя необходимость использовать метод lineto один раз.

(2) Нарисуйте прямоугольник

fillRect(x, y, width, height)Метод используется для рисования прямоугольника, и его четырьмя параметрами являются координата x и координата y вершины левого верхнего угла прямоугольника, а также ширина и высота прямоугольника. Свойство fillStyle используется для установки цвета заливки прямоугольника.

ctx.fillStyle = 'yellow';
ctx.fillRect(50, 50, 200, 100); 

Метод strokeRect похож на fillRect и используется для рисования пустотелого прямоугольника.

ctx.strokeRect(10,10,200,100); 

Метод clearRect используется для очистки содержимого прямоугольной области.

ctx.clearRect(100,50,50,50); 

(3) Нарисовать текст

fillText(string, x, y)Используется для рисования текста, его три параметра — текстовое содержимое, координата x начальной точки и координата y. Прежде чем использовать его, вам нужно использовать шрифт для установки шрифта, размера и стиля (написание аналогично свойству шрифта в CSS). Подобно этомуstrokeTextметод добавления пустых слов.

// 设置字体
ctx.font = "Bold 20px Arial"; 
// 设置对齐方式
ctx.textAlign = "left";
// 设置填充颜色
ctx.fillStyle = "#008600"; 
// 设置字体内容,以及在画布上的位置
ctx.fillText("Hello!", 10, 50); 
// 绘制空心字
ctx.strokeText("Hello!", 10, 100); 

Метод fillText не поддерживает разрыв текста, т. е. весь текст отображается на одной строке. Итак, если вы хотите сгенерировать несколько строк текста, просто вызовите метод fillText несколько раз.

(4) Нарисуйте круги и сектора

arcметод рисования сектора

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

Параметры x и y метода дуги — это координаты центра окружности, radius — радиус, startAngle и endAngle — начальный и конечный углы веера (в радианах), а против часовой стрелки указывает, следует ли рисовать рисунок. против часовой стрелки (истина) или по часовой стрелке (ложь).

Вот как нарисовать сплошной круг.

ctx.beginPath(); 
ctx.arc(60, 60, 50, 0, Math.PI*2, true); 
ctx.fillStyle = "#000000"; 
ctx.fill();

Пример рисования полого круга.

ctx.beginPath(); 
ctx.arc(60, 60, 50, 0, Math.PI*2, true); 
ctx.lineWidth = 1.0; 
ctx.strokeStyle = "#000"; 
ctx.stroke();

(5) Установите цвет градиента

Метод createLinearGradient используется для установки цвета градиента.

var myGradient = ctx.createLinearGradient(0, 0, 0, 160); 

myGradient.addColorStop(0, "#BABABA"); 

myGradient.addColorStop(1, "#636363");

Параметры метода createLinearGradient: (x1, y1, x2, y2), где x1 и y1 — координаты начальной точки, а x2 и y2 — координаты конечной точки. С разными значениями координат вы можете создавать градиенты сверху вниз, слева направо и так далее.

Способ применения следующий:

ctx.fillStyle = myGradient;
ctx.fillRect(10,10,200,100);

(6) Установите тень

Ряд методов, связанных с тенями, которые можно использовать для установки теней.

ctx.shadowOffsetX = 10; // 设置水平位移
ctx.shadowOffsetY = 10; // 设置垂直位移
ctx.shadowBlur = 5; // 设置模糊度
ctx.shadowColor = "rgba(0,0,0,0.5)"; // 设置阴影颜色

ctx.fillStyle = "#CC0000"; 
ctx.fillRect(10,10,200,100);

Метод обработки изображения

метод drawImage

Canvas API позволяет вставлять файлы изображений в холст, читая изображение и используяdrawImageспособ перерисовки на холсте.

var img = new Image();
img.src = 'image.png';
ctx.drawImage(img, 0, 0); // 设置对应的图像对象,以及它在画布上的位置

Приведенный выше код загружает изображение PNG в холст. Метод drawImage() принимает три параметра, первый параметр — это элемент DOM файла изображения (т.<img>node), второй и третий параметры — это координаты левого верхнего угла изображения на холсте (0, 0) в приведенном выше примере означает, что левый верхний угол изображения помещается в левый верхний угол изображения. холст.

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

var image = new Image();

image.onload = function() {
  var canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;
  canvas.getContext('2d').drawImage(image, 0, 0);
  // 插入页面底部
  document.body.appendChild(image);
  return canvas;
}

image.src = 'image.png';

метод getImageData, метод putImageData

Метод getImageData можно использовать для чтения содержимого Canvas и возврата объекта, содержащего информацию о каждом пикселе.

var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

Объект imageData имеет свойство данных, значение которого представляет собой одномерный массив. Значения этого массива по порядку являются значениями красного, зеленого, синего и альфа-канала каждого пикселя, поэтому длина массива равна ширине пикселя изображения x высоте пикселя изображение x 4, а диапазон каждого значения составляет 0–255. Этот массив доступен не только для чтения, но и для записи, поэтому, манипулируя значением этого массива, вы можете достичь цели манипулирования изображением. После изменения этого массива используйтеputImageDataметод для перерисовки содержимого массива на Canvas.

context.putImageData(imageData, 0, 0);

метод toDataURL

После внесения изменений в данные изображения вы можете использоватьtoDataURLметод для преобразования данных Canvas в общий формат файла изображения.

function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL('image/png');
  return image;
}

Приведенный выше код преобразует данные Canvas в URI данных PNG.

метод сохранения, метод восстановления

Метод сохранения используется для сохранения контекста, а метод восстановления используется для восстановления последнего сохраненного контекста.

ctx.save();

ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 5;
ctx.shadowColor = 'rgba(0,0,0,0.5)';

ctx.fillStyle = '#CC0000';
ctx.fillRect(10,10,150,100);

ctx.restore();

ctx.fillStyle = '#000000';
ctx.fillRect(180,10,150,100);

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


анимация

С помощью JavaScript легко анимировать элемент холста.

var posX = 20,
    posY = 100;

setInterval(function() {
	context.fillStyle = "black";
    context.fillRect(0,0,canvas.width, canvas.height);

	posX += 1;
	posY += 0.25;

	context.beginPath();
	context.fillStyle = "white";

	context.arc(posX, posY, 10, 0, Math.PI*2, true); 
	context.closePath();
	context.fill();
}, 30);

Приведенный выше код создаст эффект маленькой точки, перемещающейся вниз вправо каждые 30 миллисекунд. В начале функции setInterval причина, по которой холст повторно отображается с черным фоном, заключается в том, чтобы стереть маленькие точки на предыдущем шаге.

Задавая координаты центра круга, можно генерировать различные траектории движения.

Сначала подняться, а потом упасть.

var vx = 10,
    vy = -10,
    gravity = 1;

setInterval(function() {
    posX += vx;
    posY += vy;
    vy += gravity;
	// ...
});

В приведенном выше коде координата x всегда увеличивается, указывая на непрерывное движение вправо. Координата y сначала становится меньше, а затем непрерывно увеличивается под действием силы тяжести, указывая на то, что она сначала повышается, а затем падает.

После того, как мяч непрерывно отскакивает, он постепенно становится неподвижным.

var vx = 10,
    vy = -10,
    gravity = 1;

setInterval(function() {
    posX += vx;
    posY += vy;

	if (posY > canvas.height * 0.75) {
          vy *= -0.6;
          vx *= 0.75;
          posY = canvas.height * 0.75;
    }
	
    vy += gravity;
	// ...
});

Приведенный выше код означает, что когда координата y мяча находится на 75% нижней части экрана, скорость перемещения по оси x становится равной 75% от исходной, а отскок к оси y составляет 40 % от предыдущей высоты отскока.


обработка пикселей

С помощью методов getImageData и putImageData можно обрабатывать каждый пиксель, а затем можно манипулировать содержимым изображения.

Предполагая, что filter — это функция, обрабатывающая пиксели, весь процесс обработки Canvas можно представить следующим кодом.

if (canvas.width > 0 && canvas.height > 0) {

	var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

    filter(imageData);

    context.putImageData(imageData, 0, 0);

}

Вот несколько распространенных методов лечения.

эффект оттенков серого

Оттенки серого — это среднее арифметическое значений красного, зеленого и синего пикселей, которое фактически преобразует изображение в черно-белое. Предполагая, что d[i] — это красное значение пикселя в массиве пикселей, тогда d[i+1] — это зелёное значение, d[i+2] — это синее значение, а d[i+3] — это значение альфа-канала. Алгоритм преобразования в оттенки серого заключается в сложении значений красного, зеленого и синего, делении на 3 и записи результата обратно в массив.

grayscale = function (pixels) {

	var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3;
    }

    return pixels;

};

ретро эффект

Эффект ретро (сепия) состоит в том, чтобы взять красный, зеленый и синий пиксели и взять средневзвешенное значение этих трех значений, чтобы изображение имело старомодный эффект.

sepia = function (pixels) {

    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i]     = (r * 0.393)+(g * 0.769)+(b * 0.189); // red
      d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green
      d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue
    }

    return pixels;

};

эффект красной маски

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

red = function (pixels) {
	
    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i] = (r+g+b)/3;        // 红色通道取平均值
      d[i + 1] = d[i + 2] = 0; // 绿色通道和蓝色通道都设为0
    }

    return pixels;

};

Эффект яркости

Яркость относится к тому, чтобы сделать изображение ярче или темнее. Алгоритм добавляет положительное или отрицательное значение к красному каналу, зеленому каналу и синему каналу.

brightness = function (pixels, delta) {

    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
          d[i] += delta;     // red
          d[i + 1] += delta; // green
          d[i + 2] += delta; // blue   
    }

	return pixels;

};

Инвертный эффект

Эффект инвертирования относится к эффекту изображения, показывающего инверсию цвета. Алгоритм принимает противоположные значения (исходное значение 255) для красного, зеленого и синего каналов.

invert = function (pixels) {

	var d = pixels.data;

	for (var i = 0; i < d.length; i += 4) {
		d[i] = 255 - d[i];
		d[i+1] = 255 - d[i + 1];
		d[i+2] = 255 - d[i + 2];
	}

	return pixels;

};

Простой маленький каштан

виджет анимации vue

<template>
  <canvas ref="bubble" :width="width" :height="height" :style="style"></canvas>
</template>

<script type="text/ecmascript-6">
  export default {
    props: {
      y: {
        type: Number,
        default: 0
      }
    },
    data() {
      return {
        width: 50,
        height: 80
      }
    },
    computed: {
      distance() {
        return Math.max(0, Math.min(this.y * this.ratio, this.maxDistance))
      },
      style() {
        return `width:${this.width / this.ratio}px;height:${this.height / this.ratio}px`
      }
    },
    created() {
      this.ratio = window.devicePixelRatio
      this.width *= this.ratio
      this.height *= this.ratio
      this.initRadius = 18 * this.ratio
      this.minHeadRadius = 12 * this.ratio
      this.minTailRadius = 5 * this.ratio
      this.initArrowRadius = 10 * this.ratio
      this.minArrowRadius = 6 * this.ratio
      this.arrowWidth = 3 * this.ratio
      this.maxDistance = 40 * this.ratio
      this.initCenterX = 25 * this.ratio
      this.initCenterY = 25 * this.ratio
      this.headCenter = {
        x: this.initCenterX,
        y: this.initCenterY
      }
    },
    mounted() {
      this._draw()
    },
    methods: {
      _draw() {
        const bubble = this.$refs.bubble
        let ctx = bubble.getContext('2d')
        ctx.clearRect(0, 0, bubble.width, bubble.height)

        this._drawBubble(ctx)

        this._drawArrow(ctx)
      },
      _drawBubble(ctx) {
        ctx.save()
        ctx.beginPath()

        const rate = this.distance / this.maxDistance
        const headRadius = this.initRadius - (this.initRadius - this.minHeadRadius) * rate

        this.headCenter.y = this.initCenterY - (this.initRadius - this.minHeadRadius) * rate

        // 画上半弧线
        ctx.arc(this.headCenter.x, this.headCenter.y, headRadius, 0, Math.PI, true)

        // 画左侧贝塞尔
        const tailRadius = this.initRadius - (this.initRadius - this.minTailRadius) * rate
        const tailCenter = {
          x: this.headCenter.x,
          y: this.headCenter.y + this.distance
        }

        const tailPointL = {
          x: tailCenter.x - tailRadius,
          y: tailCenter.y
        }
        const controlPointL = {
          x: tailPointL.x,
          y: tailPointL.y - this.distance / 2
        }

        ctx.quadraticCurveTo(controlPointL.x, controlPointL.y, tailPointL.x, tailPointL.y)

        // 画下半弧线
        ctx.arc(tailCenter.x, tailCenter.y, tailRadius, Math.PI, 0, true)

        // 画右侧贝塞尔
        const headPointR = {
          x: this.headCenter.x + headRadius,
          y: this.headCenter.y
        }
        const controlPointR = {
          x: tailCenter.x + tailRadius,
          y: headPointR.y + this.distance / 2
        }
        ctx.quadraticCurveTo(controlPointR.x, controlPointR.y, headPointR.x, headPointR.y)

        ctx.fillStyle = 'rgb(170,170,170)'
        ctx.fill()
        ctx.strokeStyle = 'rgb(153,153,153)'
        ctx.stroke()
        ctx.restore()
      },
      _drawArrow(ctx) {
        ctx.save()
        ctx.beginPath()

        const rate = this.distance / this.maxDistance
        const arrowRadius = this.initArrowRadius - (this.initArrowRadius - this.minArrowRadius) * rate

        // 画内圆
        ctx.arc(this.headCenter.x, this.headCenter.y, arrowRadius - (this.arrowWidth - rate), -Math.PI / 2, 0, true)

        // 画外圆
        ctx.arc(this.headCenter.x, this.headCenter.y, arrowRadius, 0, Math.PI * 3 / 2, false)

        ctx.lineTo(this.headCenter.x, this.headCenter.y - arrowRadius - this.arrowWidth / 2 + rate)
        ctx.lineTo(this.headCenter.x + this.arrowWidth * 2 - rate * 2, this.headCenter.y - arrowRadius + this.arrowWidth / 2)

        ctx.lineTo(this.headCenter.x, this.headCenter.y - arrowRadius + this.arrowWidth * 3 / 2 - rate)

        ctx.fillStyle = 'rgb(255,255,255)'
        ctx.fill()
        ctx.strokeStyle = 'rgb(170,170,170)'
        ctx.stroke()
        ctx.restore()
      }
    },
    watch: {
      y() {
        this._draw()
      }
    }
  }
</script>

<style scoped>

</style>