предисловие
Здравствуйте, рад, что вы увидели первый пост Арона. Я работаю над интерфейсом некоторое время, и в последнее время я планирую разобраться в полученных знаниях по визуализации.Заинтересованные друзья могут взглянуть.
Сделайте пакет горьких выражений, похожий на WeChat
В этом выпуске вы познакомитесь с использованием холста и реализуете выражение через холст. Следующее выражение, которое вышло из рыбы утром, приступим.
что такое холст
Canvas — это новый элемент тега в HTML5. Обычно мы используем его для создания интересных анимаций или составных изображений. Наиболее распространенное применение на рынке — страницы активности H5, большинство из которых реализовано с помощью canvas.
canvas
холст
Холст также является элементом dom, но следует отметить, что ширина и высота холста контролируются атрибутами на этикетке.Если ширина и высота CSS меньше, чем атрибуты лейбла, они будут растягиваться и деформироваться.
<canvas id="canvas" width="800" height="800">
抱歉,您不配拥有canvas
</canvas>
система координат
Началом системы координат холста по умолчанию является верхний левый угол элемента холста, ось X тянется вправо, а ось Y тянется вниз.С точки зрения управления координатами, аналогично CSS, можно использовать перевод, поворот, масштабирование, преобразование и другие методы.
- translate(dx, dy): перевести систему координат. Это эквивалентно перемещению начала координат, первоначально расположенного в (0, 0), в точку (dx, dy).
- rotate(angle): Повернуть систему координат. Этот метод управляет поворотом системы координат на угол в радианах по часовой стрелке.
- масштаб(sx, sy): Масштаб системы координат. Этот метод управляет системой координат для масштабирования sx по горизонтали и sy по вертикали.
- transform(a,b,c,d,e,f): позволяет масштабировать, вращать, перемещать и наклонять текущую систему координат среды, где a: масштабирует рисунок по горизонтали, b: наклоняет рисунок по горизонтали, c: наклоняет рисунок по вертикали , d: масштабирование рисунка по вертикали, e: перемещение рисунка по горизонтали, f: перемещение рисунка по вертикали.
- setTransform(a,b,c,d,e,f): этот метод сбрасывает текущую матрицу преобразования в матрицу идентичности, а затем выполняет преобразование с теми же параметрами.
Общий API рисования
- дуга
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)以及半径绘制弧线 同时连接两个控制点。
- дорожка
beginPath() 新建一条路径
moveTo( x, y ) 移动画笔到(x , y)的位置
closePath() 关闭该路径
stroke() 将绘制的路径进行描边
fill() 将绘制的封闭区域进行填充
- прямоугольник
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的矩形 为透明
- Кривая Безье
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)结束点
- Градиент
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所处颜色
- текст
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