Учим холст по смайликам

Canvas
Учим холст по смайликам

предисловие

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

Сделайте пакет горьких выражений, похожий на WeChat

В этом выпуске вы познакомитесь с использованием холста и реализуете выражение через холст. Следующее выражение, которое вышло из рыбы утром, приступим.

что такое холст

Canvas — это новый элемент тега в HTML5. Обычно мы используем его для создания интересных анимаций или составных изображений. Наиболее распространенное применение на рынке — страницы активности H5, большинство из которых реализовано с помощью canvas.

canvas

холст

Холст также является элементом dom, но следует отметить, что ширина и высота холста контролируются атрибутами на этикетке.Если ширина и высота CSS меньше, чем атрибуты лейбла, они будут растягиваться и деформироваться.

<canvas id="canvas" width="800" height="800">
	抱歉,您不配拥有canvas
</canvas>

система координат

Началом системы координат холста по умолчанию является верхний левый угол элемента холста, ось X тянется вправо, а ось Y тянется вниз.С точки зрения управления координатами, аналогично CSS, можно использовать перевод, поворот, масштабирование, преобразование и другие методы.

  1. translate(dx, dy): перевести систему координат. Это эквивалентно перемещению начала координат, первоначально расположенного в (0, 0), в точку (dx, dy).
  2. rotate(angle): Повернуть систему координат. Этот метод управляет поворотом системы координат на угол в радианах по часовой стрелке.
  3. масштаб(sx, sy): Масштаб системы координат. Этот метод управляет системой координат для масштабирования sx по горизонтали и sy по вертикали.
  4. transform(a,b,c,d,e,f): позволяет масштабировать, вращать, перемещать и наклонять текущую систему координат среды, где a: масштабирует рисунок по горизонтали, b: наклоняет рисунок по горизонтали, c: наклоняет рисунок по вертикали , d: масштабирование рисунка по вертикали, e: перемещение рисунка по горизонтали, f: перемещение рисунка по вертикали.
  5. setTransform(a,b,c,d,e,f): этот метод сбрасывает текущую матрицу преобразования в матрицу идентичности, а затем выполняет преобразование с теми же параметрами.

Общий API рисования

  1. дуга
arc(x, y, r,startAngle, endAngle, anticlosewise)
// 以(x,y)为圆心 r为半径的圆  绘制startAngle弧度 到endAngle弧度的圆弧 anticlosewise默认为false 即顺时针方向 true为逆时针方向。
arcTo( x1 , y1 , x2 , y2 , radius ) //根据 两个控制点 (x1,y1) 和 (x2, y2)以及半径绘制弧线 同时连接两个控制点。
  1. дорожка
beginPath() 新建一条路径
moveTo( x, y ) 移动画笔到(x , y)的位置
closePath() 关闭该路径
stroke() 将绘制的路径进行描边
fill() 将绘制的封闭区域进行填充
  1. прямоугольник
fillRect( x , y , width , height)  //填充以(x,y)为起点宽高分别为width、height的矩形 默认为黑色
stokeRect( x , y , width , height) //绘制一个空心以(x,y)为起点宽高分别为width、height的矩形
clearRect( x, y , width , height ) // 清除以(x,y)为起点宽高分别为width、height的矩形 为透明
  1. Кривая Безье

Canvas рисует различную графику, состоящую из дуг, путей, прямоугольников и кривых Безье.

quadraticCurveTo( cp1x, cp1y , x ,y ) // (cp1x,cp1y) 控制点    (x,y)结束点
bezierCurveTo(cp1x, cp1y ,cp2x , cp2y ,x , y )//(cp1x,cp1y)控制点1   (cp2x,cp2y) 控制点2  (x,y)结束点
  1. Градиент
let gradient = ctx.createLinearGradient( x1 ,y1 ,x2 ,y2); //线性渐变
let gradient = ctx.createRadialGradient(x1 ,y1 ,r1 ,x2 ,y2 ,r2);//径向渐变
gradient.addColorStop( position , color )// position:介于0~1    color:position所处颜色
  1. текст
fillText(text, x, y, maxWidth) 在(x,y)位置绘制text文本
strokeText(text,x, y, maxWidth]) 在(x,y)位置绘制text文本边框

Сохранение и восстановление состояния

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

save()
// 平移或者绘图
restore()

Эмодзи чехол

контур круглого лица

function face(ctx, options) {
    const { x, y, r } = this.options
    ctx.save()
    ctx.translate(x, y)
    ctx.arc(0,0, r, 0, Math.PI * 2);
    ctx.strokeStyle = this.options.color;
    const radialGradient = ctx.createRadialGradient( -1 * r / 3,  - 1 * r / 3, 4 * r / 5 ,  -1 * r / 3,  -1 * r / 3 , r)
    radialGradient.addColorStop(0, 'rgb(255,255,0)')
    radialGradient.addColorStop(1, 'rgb(255,215,0)')
    ctx.fillStyle = radialGradient
    ctx.fill()
    ctx.stroke();
    ctx.restore();
}

face(ctx, {
  x: 100,
  y: 100,
  r: 50
})

Глаз

Реализация глаз делится на два этапа: сначала нарисуйте белок глаз, а затем используйте квадратичную кривую Безье для реализации кривой углов.Общая библиотека canvas2d также использует эту технику для реализации графики с закругленными углами. Наконец, щелкните черную точку в центре, чтобы завершить рисование глаз.

function eye(ctx, options) {
  const radius = options.radius || 4
  const width = options.width || 20
  const height = options.height || 12
  const x = options.x || 0
  const y = options.y || 0
  ctx.save()
  ctx.translate(this.options.x ,this.options.y)
  ctx.strokeStyle = this.options.color
  ctx.fillStyle = '#fff'
  ctx.beginPath();
  ctx.moveTo(0 + radius, 0)
  ctx.lineTo(width - radius, 0)
  ctx.quadraticCurveTo(width ,0, width,  0 + radius)
  ctx.lineTo(width, height -radius)
  ctx.quadraticCurveTo(width, height, width - radius, height)
  ctx.lineTo(0 + radius, height)
  ctx.quadraticCurveTo(0, height, 0, height -radius)
  ctx.lineTo(0, 0 + radius)
  ctx.quadraticCurveTo(0, 0, 0 + radius, 0)
  ctx.closePath()
  ctx.fill()
  ctx.stroke();
  ctx.restore()

  ctx.save()
  ctx.fillStyle = 'black'
  ctx.translate(x, y)
  ctx.beginPath()
  ctx.arc(width / 2, height / 2, 3, 0, Math.PI * 2)
  ctx.stroke()
  ctx.fill()
  ctx.restore()
 }

eye(ctx, { // 左眼
  x: 50,
  y: 80
})
eye(ctx, { //右眼
 x: 90,
 y: 80
})

рот

function mouth(ctx, options) {
  const width = options.size || 40
  const height = options.height || 10
  const x = options.x || 40
  const y = options.y || 10
  ctx.save()
  ctx.translate(x, y)
  ctx.beginPath()
  ctx.moveTo(0, 0)
  ctx.quadraticCurveTo(width / 2, height, size, 0)
  ctx.stroke()
  ctx.restore()
}
mouth(ctx, {
  x: 60,
  y: 118
})

слезы

function tear(ctx, options) {
  const width = options.width || 6
  const height = options.height || 20
  const radius = options.radius || 4
  const x = options.x || 0
  const y = options.y || 0
  ctx.save()
  const gradient = ctx.createLinearGradient(0, 0 , width, height)
  gradient.addColorStop(0, 'rgba(0,191,255,1)')
  gradient.addColorStop(1, 'rgba(135,206,250,1)')
  ctx.fillStyle = gradient
  ctx.translate(this.options.x, this.options.y)
  ctx.beginPath()
  ctx.moveTo(0, 0)
  ctx.lineTo(width, 0)
  ctx.lineTo(width, height - radius)
  ctx.quadraticCurveTo(width, height, width - radius, height)
  ctx.lineTo(0 + radius, height)
  ctx.quadraticCurveTo(0, height, 0, height -radius)
  ctx.lineTo(0, 0)
  ctx.closePath()
  ctx.fill()
  ctx.stroke()
  ctx.restore()
}

eye(ctx, { // 左眼泪
 x: 90,
 y: 80
})
eye(ctx, { // 右眼泪
 x: 90,
 y: 80
})

До сих пор было реализовано простое выражение.

Просто и продвинуто — абстрагируйте код

В общих библиотеках холста, таких как pixi.js и create.js, графика обычно имеет базовый класс display, который используется для управления отображением и скрытием, масштабированием и перемещением или графическими событиями графики. Каждое изображение имеет метод рисования, и каждое изображение будет по очереди отображаться в основном классе Вот простая абстракция приведенного выше бизнес-кода.

Display

// 写点伪代码, 不具体扩展
class Display {
 options = {}
 constructor(options) {
   Object.assign(this.options, options)
 }
 // 控制隐藏显示
 show(isShow) {
   this.options.show = isShow
 }
 //  图形事件
 on(eventType, callBack) {
 }
}

Фейс-класс

class Face extends Display {
  constructor(options) {
    super(options)
  }
  draw() {
    const { x, y, r } = this.options
    ctx.save()
    ctx.translate(this.options.x ,this.options.y)
    ctx.arc(x, y, r, 0, Math.PI * 2);
    ctx.strokeStyle = this.options.color;
    const radialGradient = ctx.createRadialGradient(x -1 * r / 3, y - 1 * r / 3, 40, x -1 * r / 3, y -1 * r / 3 , r)
    radialGradient.addColorStop(0, 'rgb(255,255,0)')
    radialGradient.addColorStop(1, 'rgb(255,215,0)')
    ctx.fillStyle = radialGradient
    ctx.fill()
    ctx.stroke();
    ctx.restore();
  }
 }

Класс глаза

class Eye extends Display {
  constructor(options) {
    super(options)
  }

  draw() {
    const radius = 4
    const width = 20
    const height = 12
    ctx.save()
    ctx.translate(this.options.x ,this.options.y)
    ctx.strokeStyle = this.options.color
    ctx.fillStyle = '#fff'
    ctx.beginPath();
    ctx.moveTo(0 + radius, 0)
    ctx.lineTo(width - radius, 0)
    ctx.quadraticCurveTo(width ,0, width,  0 + radius)
    ctx.lineTo(width, height -radius)
    ctx.quadraticCurveTo(width, height, width - radius, height)
    ctx.lineTo(0 + radius, height)
    ctx.quadraticCurveTo(0, height, 0, height -radius)
    ctx.lineTo(0, 0 + radius)
    ctx.quadraticCurveTo(0, 0, 0 + radius, 0)
    ctx.closePath()
    ctx.fill()
    ctx.stroke();
    ctx.restore()

    ctx.save()
    ctx.fillStyle = 'black'
    ctx.translate(this.options.x ,this.options.y)
    ctx.beginPath()
    ctx.arc(width / 2, height / 2, 3, 0, Math.PI * 2)
    ctx.stroke()
    ctx.fill()
    ctx.restore()
  }
}

Mouth

class Mouth extends Display {
  constructor(options) {
    super(options)
  }
  draw() {
    const size = 40
    const height = 10
    ctx.save()
    ctx.translate(this.options.x, this.options.y)
    ctx.beginPath()
    ctx.moveTo(0, 0)
    ctx.quadraticCurveTo(size / 2, height, size, 0)
    ctx.stroke()
    ctx.restore()
  }
}
Класс слезы
class Tear extends Display{
  constructor(options) {
    super(options)
  }

  draw() {
    const { width , height, radius } = this.options
    ctx.save()
    const gradient = ctx.createLinearGradient(0, 0 , width, height)
    gradient.addColorStop(0, 'rgba(0,191,255,1)')
    gradient.addColorStop(1, 'rgba(135,206,250,1)')
    ctx.fillStyle = gradient
    ctx.translate(this.options.x, this.options.y)
    ctx.beginPath()
    ctx.moveTo(0, 0)
    ctx.lineTo(width, 0)
    ctx.lineTo(width, height - radius)
    ctx.quadraticCurveTo(width, height, width - radius, height)
    ctx.lineTo(0 + radius, height)
    ctx.quadraticCurveTo(0, height, 0, height -radius)
    ctx.lineTo(0, 0)
    ctx.closePath()
    ctx.fill()
    ctx.stroke()
    ctx.restore()
  }
}

основной код рендеринга

let face = new Face({
    x: 100,
    y: 100,
    r: 50,
    color: 'rgba(0,0,0,0.2)'
  })

  const eye1 = new Eye({
    x: 50,
    y: 80,
    color: 'rgba(0,0,0,1)'
  })

  const eye2 = new Eye({
    x: 90,
    y: 80,
    color: 'rgba(0,0,0,1)'
  })

  const tearDefault = {
    width: 6,
    height: 20,
    radius: 4
  }
  const tear1 = new Tear({
    x: 57,
    y: 92,
    ...tearDefault
  })

  const tear2 = new Tear({
    x: 98,
    y: 92,
    ...tearDefault
  })

  const mouth = new Mouse({
    x: 60,
    y: 118,
    color: 'rgba(0,0,0,1)'
  })
  const graphics = []
  graphics.push(face)
  graphics.push(eye1)
  graphics.push(eye2)
  graphics.push(mouth)
  graphics.push(tear1)
  graphics.push(tear2)
  function render() {
    graphics.forEach(graphic => {
      graphic.draw()
    })
  }
  render()  

наконец

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

следующее обновлениеИзучите pixi.js из League of Legends