Home >Web Front-end >H5 Tutorial >HTML5 canvas implements drawing program (with code)
This article introduces to you the HTML5 canvas implementation drawing program (with code). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
The entire project is divided into two parts
Scenario
The scenario is responsible for canvas control and event monitoring , Animation processing
Elf
Elf refers to every canvas element that can be drawn
Strong scalability
class Element { constructor(options = { fillStyle: 'rgba(0,0,0,0)', lineWidth: 1, strokeStyle: 'rgba(0,0,0,255)' }) { this.options = options } setStyle(options){ this.options = Object.assign(this.options. options) } }
Attributes:
options store all drawing properties
fillStyle: Sets or returns the color, gradient, or pattern used to fill the painting
strokeStyle: Sets or returns the color, gradient, or pattern used to fill the painting Color, gradient or pattern
lineWidth: Set or return the current line width
all use the getContext("2d") object Of the native attributes, only these three attributes are listed here, and they can be expanded if necessary.
Can continue to expand if necessary
Method:
The setStyle method is used to reset the properties of the current sprite
You can continue to expand if necessary
All sprites All inherit from the Element class.
Subclass is the specific implementation of each elf element. Here we introduce the implementation of Circle element
class Circle extends Element { // 定位点的坐标(这块就是圆心),半径,配置对象 constructor(x, y, r = 0, options) { // 调用父类的构造函数 super(options) this.x = x this.y = y this.r = r } // 改变元素大小 resize(x, y) { this.r = Math.sqrt((this.x - x) ** 2 + (this.y - y) ** 2) } // 移动元素到新位置,接收两个参数,新的元素位置 moveTo(x, y) { this.x = x this.y = y } // 判断点是否在元素中,接收两个参数,点的坐标 choose(x, y) { return ((x - this.x) ** 2 + (y - this.y) ** 2) < (this.r ** 2) } // 偏移,计算点和元素定位点的相对偏移量(ofsetX, offsetY) getOffset(x, y) { return { x: x - this.x, y: y - this.y } } // 绘制元素实现,接收一个ctx对象,将当前元素绘制到指定画布上 draw(ctx) { // 取到绘制所需属性 let { fillStyle, strokeStyle, lineWidth } = this.options // 开始绘制beginPath() 方法开始一条路径,或重置当前的路径 ctx.beginPath() // 设置属性 ctx.fillStyle = fillStyle ctx.strokeStyle = strokeStyle ctx.lineWidth = lineWidth // 画圆 ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI) // 填充颜色 ctx.stroke() ctx.fill() // 绘制完成 } // 验证函数,判断当前元素是否满足指定条件,此处用来检验是否将元素添加到场景中。 validate() { return this.r >= 3 } }
arc() Methods to create arcs/curves (used to create circles or partial circles)
x x coordinate of the center of the circle.
y y coordinate of the center of the circle.
r The radius of the circle.
sAngle Starting angle, in radians. (The three o'clock position of the arc's circle is 0 degrees).
eAngle End angle, measured in radians.
counterclockwise Optional. Specifies whether the plot should be drawn counterclockwise or clockwise. False = clockwise, true = counterclockwise.
Note:
Only two formal parameters of the constructor are required, which are the coordinates of the anchor point.
Other formal parameters must have default values.
The timing of calling all methods
We call the resize method when drawing elements on the canvas.
Call the moveTo method when moving an element.
choose will be called when the mouse is pressed to determine whether the current element is selected.
getOffset is called when an element is selected to determine the selected position.
draw drawing function, called when drawing elements onto the scene.
Attribute introduction
class Sence { constructor(id, options = { width: 600, height: 400 }) { // 画布属性 this.canvas = document.querySelector('#' + id) this.canvas.width = options.width this.canvas.height = options.height this.width = options.width this.height = options.height // 绘图的对象 this.ctx = this.canvas.getContext('2d') // 离屏canvas this.outCanvas = document.createElement('canvas') this.outCanvas.width = this.width this.outCanvas.height = this.height this.outCtx = this.outCanvas.getContext('2d') // 画布状态 this.stateList = { drawing: 'drawing', moving: 'moving' } this.state = this.stateList.drawing // 鼠标状态 this.mouseState = { // 记录鼠标按下时的偏移量 offsetX: 0, offsetY: 0, down: false, //记录鼠标当前状态是否按下 target: null //当前操作的目标元素 } // 当前选中的精灵构造器 this.currentSpriteConstructor = null // 存储精灵 let sprites = [] this.sprites = sprites /* .... */ } }
Event logic
class Sence { constructor(id, options = { width: 600, height: 400 }) { /* ... */ // 监听事件 this.canvas.addEventListener('contextmenu', (e) => { console.log(e) }) // 鼠标按下时的处理逻辑 this.canvas.addEventListener('mousedown', (e) => { // 只有左键按下时才会处理鼠标事件 if (e.button === 0) { // 鼠标的位置 let x = e.offsetX let y = e.offsetY // 记录鼠标是否按下 this.mouseState.down = true // 创建一个临时target // 记录目标元素 let target = null if (this.state === this.stateList.drawing) { // 判断当前有没有精灵构造器,有的话就构造一个对应的精灵元素 if (this.currentSpriteConstructor) { target = new this.currentSpriteConstructor(x, y) } } else if (this.state === this.stateList.moving) { let sprites = this.sprites // 遍历所有的精灵,调用他们的choose方法,判断有没有被选中 for (let i = sprites.length - 1; i >= 0; i--) { if (sprites[i].choose(x, y)) { target = sprites[i] break; } } // 如果选中的话就调用target的getOffset方法,获取偏移量 if (target) { let offset = target.getOffset(x, y) this.mouseState.offsetX = offset.x this.mouseState.offsetY = offset.y } } // 存储当前目标元素 this.mouseState.target = target // 在离屏canvas保存除目标元素外的所有元素 let ctx = this.outCtx // 清空离屏canvas ctx.clearRect(0, 0, this.width, this.height) // 将目标元素外的所有的元素绘制到离屏canvas中 this.sprites.forEach(item => { if (item !== target) { item.draw(ctx) } }) if(target){ // 开始动画 this.anmite() } } }) this.canvas.addEventListener('mousemove', (e) => { // 如果鼠标按下且有目标元素,才执行下面的代码 if (this.mouseState.down && this.mouseState.target) { let x = e.offsetX let y = e.offsetY if (this.state === this.stateList.drawing) { // 调用当前target的resize方法,改变大小 this.mouseState.target.resize(x, y) } else if (this.state === this.stateList.moving) { // 取到存储的偏移量 let { offsetX, offsetY } = this.mouseState // 调用moveTo方法将target移动到新的位置 this.mouseState.target.moveTo(x - offsetX, y - offsetY) } } }) document.body.addEventListener('mouseup', (e) => { if (this.mouseState.down) { // 将鼠标按下状态记录为false this.mouseState.down = false if (this.state === this.stateList.drawing) { // 调用target的validate方法。判断他要不要被加到场景去呢 if (this.mouseState.target.validate()) { this.sprites.push(this.mouseState.target) } } else if (this.state === this.stateList.moving) { // 什么都不做 } } }) } }
Method introduction
class Sence { // 动画 anmite() { requestAnimationFrame(() => { // 清除画布 this.clear() // 将离屏canvas绘制到当前canvas上 this.paint(this.outCanvas) // 绘制target this.mouseState.target.draw(this.ctx) // 鼠标是按下状态就继续执行下一帧动画 if (this.mouseState.down) { this.anmite() } }) } // 可以将手动的创建的精灵添加到画布中 append(sprite) { this.sprites.push(sprite) sprite.draw(this.ctx) } // 根据ID值,从场景中删除对应元素 remove(id) { this.sprites.splice(id, 1) } // clearRect清除指定区域的画布内容 clear() { this.ctx.clearRect(0, 0, this.width, this.height) } // 重绘整个画布的内容 reset() { this.clear() this.sprites.forEach(element => { element.draw(this.ctx) }) } // 将离屏canvas绘制到页面的canvas画布上 paint(canvas, x = 0, y = 0) { this.ctx.drawImage(canvas, x, y, this.width, this.height) } // 设置当前选中的精灵构造器 setCurrentSprite(Element) { this.currentSpriteConstructor = Element }
Recommended related articles:
How canvas implements the code for QR code and image synthesis
HTML5 Canvas implements interactive subway line map
The above is the detailed content of HTML5 canvas implements drawing program (with code). For more information, please follow other related articles on the PHP Chinese website!