700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > html canvas php HTML5 canvas实现画图程序(附代码)

html canvas php HTML5 canvas实现画图程序(附代码)

时间:2019-05-25 11:57:02

相关推荐

html canvas php HTML5 canvas实现画图程序(附代码)

这篇文章给大家介绍的内容是关于HTML5 canvas实现画图程序(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

项目简介

整个项目分为两大部分场景

场景负责canvas控制,事件监听,动画处理

精灵

精灵则指的是每一种可以绘制的canvas元素

项目特点

可扩展性强

sprite精灵实现

父类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)

}

}属性:options中存储了所有的绘图属性fillStyle:设置或返回用于填充绘画的颜色、渐变或模式

strokeStyle:设置或返回用于笔触的颜色、渐变或模式

lineWidth:设置或返回当前的线条宽度

使用的都是getContext("2d")对象的原生属性,此处只列出了这三种属性,需要的话还可以继续扩充。

有需要可以继续扩充方法:setStyle方法用于重新设置当前精灵的属性

有需要可以继续扩充

所有的精灵都继承Element类。

子类

子类就是每一种精灵元素的具体实现,这里我们介绍一遍Circle元素的实现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() 方法创建弧/曲线(用于创建圆或部分圆)x 圆的中心的 x 坐标。

y 圆的中心的 y 坐标。

r 圆的半径。

sAngle 起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。

eAngle 结束角,以弧度计。

counterclockwise 可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。

注意事项:构造函数的形参只有两个是必须的,就是定位点的坐标。

其它的形参都必须有默认值。

所有方法的调用时机我们在画布上绘制元素的时候回调用resize方法。

移动元素的时候调用moveTo方法。

choose会在鼠标按下时调用,判断当前元素是否被选中。

getOffset选中元素时调用,判断选中位置。

draw绘制函数,绘制元素到场景上时调用。

scene场景的实现属性介绍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

/* .... */

}

}事件逻辑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) {

// 什么都不做

}

}

})

}

}方法介绍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

}

相关文章推荐:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。