最近在开发H5上的网页挂机游戏,其中有一个需求是,挂机场景中要很多小人在移动,但因为整体还是网页实现的,为了其中这一小块引入一个框架属实没有必要,所以还是决定自己再造一个小轱辘,能走就成
一、全局动画渲染进程
动画的绘制,是使用window.requestAnimationFrame
这个方法来实现的,根据文档,这个方法相比setTimeout
会更加的丝滑。由于这个方法本身几乎没有调用时间间隔,所以需要人工来实现一下帧率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| window.requestAnimationFrame(mainDraw); let drawLastTime = 0 const mainDraw = (time) => { if (time - drawLastTime < 10) { window.requestAnimationFrame(mainDraw); return; } for (let i in animateCb) { animateCb[i] && animateCb[i]() } drawLastTime = time; window.requestAnimationFrame(mainDraw); }
|
其中animateCb
为动画绘制回调方法集,交由画布对象来实现绘制。另外还暴露两个方法,供添加或删除回调。
1 2 3 4 5 6 7
| let animateCb = {} const addAnimateCb = (key, fun) => { animateCb[key] = fun } const removeAnimateCb = (key) => { animateCb[key] && delete animateCb[key] }
|
二、图片加载及初始化
在启动动画进行之前,先把所有要用到的图片进行缓存。在所有图片加载完之后,启动动画进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const theimgs = [ 'images/image1.png', 'images/image2.png', 'images/image3.png', 'images/image4.png', 'images/image5.png', 'images/image6.png', 'images/image7.png', 'images/image8.png', ]
const pageMainAnimate = async () => { let imgs1 = [] let idx1 = 0 for (let i in theimgs) { const img = new Image() img.src = theimgs[i] imgs1.push(img) img.onload = () => { if (++idx1 >= littleimgs.length) { window.requestAnimationFrame(mainDraw); } } } await store.dispatch('system/saveNewImg', {key: 'man', imgs: imgs1}) }
onMounted(() => { pageMainAnimate() })
|
三、画布组件
由于考虑到页面会有多个场景,所以画布采用组件的形式,在初始化时,将自身的绘制方法,写入回调方法集中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import TheMan from '@/modules/Man.js'
const theWidth = 800 const theHeight = 400 const theCanvas = ref() let theCtx
const { proxy, root } = getCurrentInstance()
let manList = []
const startTimer = () => { root.exposed.addAnimateCb('main', mainTimer) }
onMounted(() => { theCtx = proxy.$refs.theCanvas.getContext("2d"); startTimer() })
onUnmounted(() => { root.exposed.removeAnimateCb('main' + props.type) })
|
在绘画回调中,针对对象的各种属性,进行绘制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| const maxMan = 50 let deading = false
let addTimeLast = 0 const addSpace = 50
const mainTimer = () => { theCtx.clearRect(0, 0, theWidth, theHeight)
if (manList.length <= 0 || (addTimeLast >= addSpace && manList.length <= maxMan)) { manList.push(new TheMan(theWidth, theHeight)) addTimeLast = 0 } addTimeLast++ manList.sort((a, b) => a.ts - b.ts) if (manList.length > maxMan && !deading) { deading = true manList[0].doDead() }
manList.sort((a, b) => a.y - b.y) for (let i in manList) { let man = manList[i]
if (man.alpha < 1 && !man.deading) { man.showMe() } else if (man.alpha >= 1) { man.doMove() } theCtx.save(); theCtx.globalAlpha = man.alpha
if (man.deading) { theCtx.translate(man.x + Math.floor(man.myWidth / 2), man.y + man.myHeight); theCtx.rotate(Math.PI / 180 * man.deadAngle); man.doDeading() theCtx.drawImage(imageStorage.value['man'][man.getStep()], -1 * man.myWidth / 2, -1 * man.myHeight); } else { theCtx.drawImage(imageStorage.value['man'][man.getStep()], man.x, man.y); } theCtx.restore(); if (man.deadAngle >= 90) { man.remove = true } }
let deadidx = manList.findIndex(e => e.remove) if (deadidx >= 0) { deading = false manList.splice(deadidx, 1) } }
|
四、动画对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| import { getRandom } from '@/util/main.js' export default class Msk { x = 0 y = 0 theWidth = 0 theHeight = 0 alpha = 0 myWidth = 49 myHeight = 83 theStep = 0
moveNum = 0 moveDir = 0
deading = false deadAngle = 0
ts = 0
heightLimit = 0.1
TheMainFrame = 5 moveFrame = this.TheMainFrame stepFrame = this.TheMainFrame deadFrame = this.TheMainFrame showFrame = this.TheMainFrame
constructor(cW, cH, inMove = false) { this.ts = new Date().getTime() this.theWidth = cW this.theHeight = cH if (inMove) { this.alpha = 1 } this.x = getRandom(0, cW - this.myWidth) this.y = getRandom(this.theHeight * this.heightLimit, cH - this.myHeight) - 10 }
showMe () { if (this.showFrame < this.TheMainFrame) { this.showFrame-- if (this.showFrame === 0) { this.showFrame = this.TheMainFrame } return } this.showFrame--
this.alpha += 0.2 this.y += 2 if (this.alpha > 1) { this.alpha = 1 } }
doMove () { if (this.moveFrame < this.TheMainFrame) { this.moveFrame-- if (this.moveFrame === 0) { this.moveFrame = this.TheMainFrame } return } this.moveFrame-- if (this.moveNum === 0) { this.moveDir = getRandom(0, 50) > 25 ? 1 : -1 this.moveNum = getRandom(20, 50) } this.y += getRandom(0, 50) > 25 ? -1 : 1 if (this.y < this.theHeight * this.heightLimit) { this.y = Math.floor(this.theHeight * this.heightLimit) } else if (this.y > this.theHeight - this.myHeight) { this.y = this.theHeight - this.myHeight }
this.x += this.moveDir * 2 this.moveNum-- if (this.x < 0) { this.x = 0 this.moveNum = 0 } else if (this.x > this.theWidth - this.myWidth) { this.x = this.theWidth - this.myWidth this.moveNum = 0 } }
doDead () { this.deading = true this.alpha -= 0.1 }
doDeading () { if (this.deadFrame < this.TheMainFrame) { this.deadFrame-- if (this.deadFrame === 0) { this.deadFrame = this.TheMainFrame } return } this.deadFrame--
this.deadAngle += 9 this.alpha -= 0.1 }
getStep () { if (this.theStep > 7) { this.theStep = 0 } if (this.stepFrame <= 0) { this.stepFrame = this.TheMainFrame return this.theStep++ } else { this.stepFrame-- return this.theStep } } }
|