创建 fruits.ts,果实是由海葵生成的,所有果实应该出现在海葵

    1. import ane from "./anemones";
    2. import { cvs_height, ctx_two } from "./init";
    3. class Fruits{
    4. num: number = 30; // 绘画果实的数量
    5. alive: boolean[] =[]; // 判断果实是否存活
    6. x : number[]= []; // 果实的 x 坐标数组
    7. y :number[] = []; // 果实的 y 坐标数组
    8. orange = new Image(); // 黄色果实
    9. blue = new Image(); // 蓝色果实
    10. init(){
    11. for (let i = 0; i < this.num; ++i) {
    12. this.alive[i] = false;
    13. this.born(i);
    14. }
    15. this.orange.src = 'assets/img/fruit.png';
    16. this.blue.src = 'assets/img/blue.png';
    17. }
    18. // 绘制果实
    19. draw(){
    20. for (let i = 0; i < this.num; ++i) {
    21. // 把果实绘制出来,为了让果实居中,所以要减去图片高度一半,宽度一半
    22. // 就像实现水平居中一样 left: 50px; margin-left: -(图片宽度 / 2);
    23. ctx_two.drawImage(this.orange, this.x[i] - this.orange.width / 2, this.y[i] - this.orange.width / 2);
    24. }
    25. }
    26. // 初始化果实的坐标
    27. born(i){
    28. let aneId = Math.floor( Math.random() * ane.num ) // 随机拿到一个果实的 ID
    29. this.x[i] = ane.x[aneId]; // 设置果实的 x 坐标为海葵的 x 坐标
    30. this.y[i] = cvs_height - ane.height[aneId]; // 设置果实的 y 坐标,为海葵高度的顶点坐标
    31. }
    32. }
    33. let fruits = new Fruits()
    34. export default fruits;
    1. // init.ts
    2. import ane from "./anemones";
    3. function init() {
    4. ane.init()
    5. }
    1. // game-loop.ts
    2. import ane from "./anemones";
    3. function gameLoop() {
    4. ane.draw()
    5. }

    此时我们来重构一下我们的项目,我们发现,每一个绘制累都有一个 init,其实构造器就是一个 init,为什么我们还要多此一举去写一个 init 呢?

    所以我们把 init 的内容提到 constructor 里面去,在全局 init 函数里面去 new,然后再导出。

    所有需要初始化的,都应该从 init.ts 文件里面导出。

    anemones.ts

    1. import { ctx_two, cvs_height } from "./init";
    2. class Anemones{
    3. x: number[] = []; // x 轴的坐标
    4. height: number[] = []; // 高度
    5. num = 50; // 绘制数量
    6. /**
    7. * 其实就跟在 PS 里面画一样,只不过都是通过代码进行操作,不能通过鼠标进行操作。
    8. *
    9. * save() - restore() 做作用就是只对他们之间的代码应用这些画笔样式
    10. *
    11. * save() 就相当于暂存一下画笔的状态。开启一个快照,可以对这个快照进行任何操作
    12. *
    13. * restore() 恢复之前画笔的状态
    14. */
    15. draw(){
    16. ctx_two.save() // 暂存画笔状态
    17. // 设置画笔样式
    18. ctx_two.globalAlpha = 0.6 // 设置透明度
    19. ctx_two.strokeStyle = '#3b154e' // 设置画笔颜色
    20. ctx_two.lineWidth = 20; // 画笔的宽度
    21. ctx_two.lineCap = "round" // 圆角的线
    22. for (let i = 0; i < this.num; ++i) {
    23. ctx_two.beginPath() // 开始绘画
    24. ctx_two.moveTo(this.x[i], cvs_height) // 把画笔移动到 x 点,画布的最下方出,从下往上画海葵
    25. ctx_two.lineTo(this.x[i], cvs_height - this.height[i]) // 画到 cvs_height - this.height[i] 的地方为止
    26. ctx_two.stroke() // 确认,开始渲染
    27. }
    28. ctx_two.restore() // 恢复之前暂存的画笔状态
    29. }
    30. /**
    31. * 初始化海葵的 x 坐标和高度
    32. */
    33. constructor(){
    34. for (let i = 0; i < this.num; ++i) {
    35. this.x[i] = i * 16 + Math.random() * 20;
    36. this.height[i] = 200 + Math.random() * 50;
    37. }
    38. }
    39. }
    40. export default Anemones;

    fruits.ts

    1. import { cvs_height, ctx_two, anemones } from "./init";
    2. class Fruits{
    3. num: number = 30; // 绘画果实的数量
    4. alive: boolean[] =[]; // 判断果实是否存活
    5. x : number[]= []; // 果实的 x 坐标数组
    6. y :number[] = []; // 果实的 y 坐标数组
    7. orange = new Image(); // 黄色果实
    8. blue = new Image(); // 蓝色果实
    9. constructor(){
    10. for (let i = 0; i < this.num; ++i) {
    11. this.alive[i] = false;
    12. this.born(i);
    13. }
    14. this.orange.src = 'assets/img/fruit.png';
    15. this.blue.src = 'assets/img/blue.png';
    16. }
    17. // 绘制果实
    18. draw(){
    19. for (let i = 0; i < this.num; ++i) {
    20. // 把果实绘制出来,为了让果实居中,所以要减去图片高度一半,宽度一半
    21. // 就像实现水平居中一样 left: 50px; margin-left: -(图片宽度 / 2);
    22. ctx_two.drawImage(this.orange, this.x[i] - this.orange.width / 2, this.y[i] - this.orange.width / 2);
    23. }
    24. }
    25. // 初始化果实的坐标
    26. born(i){
    27. let aneId = Math.floor( Math.random() * anemones.num ) // 随机拿到一个果实的 ID
    28. this.x[i] = anemones.x[aneId]; // 设置果实的 x 坐标为海葵的 x 坐标
    29. this.y[i] = cvs_height - anemones.height[aneId]; // 设置果实的 y 坐标,为海葵高度的顶点坐标
    30. }
    31. }
    32. export default Fruits;

    init.ts

    1. import Anemones from "./anemones";
    2. import Fruits from "./fruits";
    3. let cvs_one: HTMLCanvasElement,
    4. cvs_two: HTMLCanvasElement,
    5. ctx_one: CanvasRenderingContext2D,
    6. ctx_two: CanvasRenderingContext2D;
    7. let cvs_width: number,
    8. cvs_height: number;
    9. let anemones: Anemones , fruits: Fruits;
    10. const bgPic = new Image();
    11. function getCanvasAndContextById(id: string): [HTMLCanvasElement, CanvasRenderingContext2D] {
    12. const dom = <HTMLCanvasElement>document.querySelector('#' + id);
    13. const ctx = dom.getContext('2d');
    14. return [dom, ctx];
    15. }
    16. function init() {
    17. [cvs_one, ctx_one] = getCanvasAndContextById('one');
    18. [cvs_two, ctx_two] = getCanvasAndContextById('two');
    19. bgPic.src = 'assets/img/background.jpg';
    20. cvs_width = cvs_one.width;
    21. cvs_height = cvs_one.height;
    22. anemones = new Anemones()
    23. fruits = new Fruits()
    24. }
    25. export default init;
    26. export {
    27. bgPic,
    28. cvs_width,
    29. cvs_height,
    30. cvs_one,
    31. cvs_two,
    32. ctx_one,
    33. ctx_two,
    34. anemones,
    35. fruits
    36. };

    game-loop.ts

    1. import { bgPic, cvs_width , cvs_height, ctx_two, anemones, fruits } from "./init";
    2. let lastTime: number = Date.now(), // 记录上一次绘制的时间
    3. deltaTime: number = 0; // requestAnimationFrame 执行完成所用的时间 = 当前时间 - 上一次绘制的世界
    4. function gameLoop() {
    5. const now = Date.now()
    6. deltaTime = now - lastTime;
    7. lastTime = now;
    8. console.log(deltaTime);
    9. drawBackbround()
    10. anemones.draw()
    11. fruits.draw()
    12. requestAnimationFrame(gameLoop);
    13. }
    14. function drawBackbround() {
    15. ctx_two.drawImage(bgPic, 0, 0, cvs_width, cvs_height)
    16. }
    17. export default gameLoop;

    重构搞定,这样看起来是不是并不复杂,假如全写在一个文件里面,我们绝对会头皮发麻。

    绘制果实 - 图1