怎样创建一块画布

发布时间 2024-01-01 22:14:55作者: 惊朝

1、创造一个基类
src/scripts/AcGameObject.js

const AC_GAME_OBJECTS = []; //  存储所有游戏对象

export class AcGameObject {
    constructor(){//构造函数
        AC_GAME_OBJECTS.push(this); //   把当前对象存入 AC_GAME_OBJECTS 数组中
        this.timedelta = 0; //  当前帧与上一帧执行的时间间隔
        this.has_called_start = false;  //  判断当前对象有没有执行过
    }
    start(){    //  只执行一次
    }
    update(){   //  每一帧执行一次,除了第一帧之外
    }
    on_destory(){   //  删除之前执行
    }
    destory(){
        this.on_destory();
        for(let i in AC_GAME_OBJECTS){
            const obj = AC_GAME_OBJECTS[i];
            if(obj === this){
                AC_GAME_OBJECTS.splice(i);  //    从数组中删除一个元素
                break;
            }
        }
    }
}

let last_timestamp; //  上一次执行的时刻
const step = (timestamp) =>{
    for(let obj of AC_GAME_OBJECTS){    //  of遍历的是值,in遍历的是下标
        if(!obj.has_called_start) {
            obj.has_called_start = true;
            obj.start();
        } else {
            obj.timedelta = timestamp - last_timestamp;
            obj.update();
        }
    }
    last_timestamp = timestamp;
    requestAnimationFrame(step);    //   递归
}
requestAnimationFrame(step)
/* 
requestAnimationFrame() 告诉浏览器——你希望执行一个动画,
并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行 
顾名思义,请求动画帧,也称 帧循环。 
其实就是该API能以浏览器的显示频率来作为其动画动作的频率,
比如浏览器每16.7ms刷新一次,动画回调也每16.7ms调用一次,这样就不会存在过度绘制的问题,动画不会掉帧,自然流畅。
 */

2、写基类的继承类
src/scripts/GameMap.js

import { AcGameObject } from "./AcGameObjects"; //  export default 是不用加大括号的,每一个文件最多import一个default

export class GameMap extends AcGameObject {
    constructor(ctx,parent){
        super();    //  执行基类的构造函数
        this.ctx=ctx; //    画布
        this.parent = parent;   //  画布的父元素,用来动态修改画布的长宽
        this.L = 0; //  存储每一个格子的绝对距离
        this.rows = 13;
        this.cols = 13;
    }
    start(){
    }
    update_size(){
        this.L = Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows);   // 求每个小正方形的边长最小值
        this.ctx.canvas.width = this.L * this.cols;
        this.ctx.canvas.height = this.L * this.rows;
    }
    update(){
        this.update_size();
        this.render();
    }
    render(){
        const color_even = "#AAD751", color_odd = "#A2D149";
        for(let r = 0; r < this.rows; r++){
            for(let c = 0; c < this.cols ; c++){
                if( (r + c) % 2 == 0 ){
                    this.ctx.fillStyle = color_even;
                } else {
                    this.ctx.fillStyle = color_odd;
                }
                this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L);
            }
        }
    }
}

3、描绘地图
/src/components/GameMap.vue

<template>
    <div ref="parent" class = "gamemap">    <!--通过ref,将这里的div和script里的canvas产生关联-->
        <canvas ref="canvas">   <!-- 画布 -->
        </canvas>
    </div>
</template>

<script>
import { GameMap } from "@/assets/scripts/GameMap.js"
import { ref, onMounted } from 'vue'

export default {
    setup(){
        let parent = ref(null);
        let canvas = ref(null);
        onMounted(() => {
            new GameMap(canvas.value.getContext('2d'), parent.value);    //  .value是因为它是ref对象。获取这个元素的context,图像稍后将在此被渲染
        });

        return {
            parent,
            canvas,
        }
    },
}
</script>

<style scoped>
div.gamemap{
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
}
</style>

4、单纯用css属性开辟一块纯色区域,并把游戏地图挂载到这块区域上
/src/components/PlayGround.vue

<template>
  <div class = "playground">
    <GameMap />
  </div>
</template>

<script>
import GameMap from './GameMap.vue';
export default {
  components:{
    GameMap,
  }
}
</script>

<style>
div.playground {
    width : 60vw;
    height: 70vh;
    background: lightblue;
    margin: 40px auto
}
</style>