好家伙,
我继续尝试着将我的飞机大战使用ES6模块化分离开来,出了点问题
edge,chrome等一系列浏览器,会为了安全,禁止你跨域访问
目录如下:
主程序
index.html
main_1.js
main.js
完整代码如下:
1 /* //plane封装成类
2 //实例化后使用
3 //plane方法有:
4 // cteate
5 config
6 start
7 stop
8 pause
9
10
11 */
12 let plane = {
13 create(dom) {
14 let canvas = document.createElement('canvas');
15 dom.appendChild(canvas);
16 canvas.width = 480;
17 canvas.height = 650;
18
19 // 初始化画布对象
20 // const canvas = document.getElementById("canvas");
21 const context = canvas.getContext("2d");
22
23 // 定义游戏的状态
24 // 开始
25 const START = 0;
26 // 开始时
27 const STARTING = 1;
28 // 运行时
29 const RUNNING = 2;
30 // 暂停时
31 const PAUSE = 3;
32 // 结束时
33 const END = 4;
34
35 //创建一个配置文件 收藏所有的图片路径
36 const IMAGES = {
37 b: "img/bullet1.png",
38 bg: "img/4.jpg",
39 copyright: "img/shoot_copyright.png",
40 pause: "img/game_pause.png",
41 loading_frame: ["img/game_loading1.png", "img/game_loading2.png", "img/game_loading3.png",
42 "img/game_loading4.png"
43 ],
44 hero_frame_live: ["img/hero1.png", "img/hero2.png"],
45 hero_frame_death: ["img/hero_blowup_n1.png", "img/hero_blowup_n2.png", "img/hero_blowup_n3.png",
46 "img/hero_blowup_n4.png"
47 ],
48 e1_live: ["img/enemy1.png"],
49 e1_death: ["img/enemy1_down1.png", "img/enemy1_down2.png", "img/enemy1_down3.png", "img/enemy1_down4.png"],
50 e2_live: ["img/enemy2.png"],
51 e2_death: ["img/enemy2_down1.png", "img/enemy2_down2.png", "img/enemy2_down3.png", "img/enemy2_down4.png"],
52 e3_live: ["img/enemy3_n1.png", "img/enemy3_n2.png"],
53 e3_death: ["img/enemy3_down1.png", "img/enemy3_down2.png", "img/enemy3_down3.png", "img/enemy3_down4.png",
54 "img/enemy3_down5.png", "img/enemy3_down6.png"
55 ],
56 c1: "img/lanqiu.jpg"
57 };
58 //初始化各个图片
59 const b = createImage(IMAGES.b);
60 const bg = createImage(IMAGES.bg);
61 const copyright = createImage(IMAGES.copyright);
62 const pause = createImage(IMAGES.pause);
63 const loading_frame = createImage(IMAGES.loading_frame);
64 const hero_frame = {
65 live: createImage(IMAGES.hero_frame_live),
66 death: createImage(IMAGES.hero_frame_death),
67 };
68 const e1 = {
69 live: createImage(IMAGES.e1_live),
70 death: createImage(IMAGES.e1_death),
71 };
72 const e2 = {
73 live: createImage(IMAGES.e2_live),
74 death: createImage(IMAGES.e2_death),
75 };
76 const e3 = {
77 live: createImage(IMAGES.e3_live),
78 death: createImage(IMAGES.e3_death),
79 };
80 const c1 = createImage(IMAGES.c1);
81
82 function createImage(src) {
83 let img;
84 if (typeof src === "string") {
85 img = new Image();
86 img.src = src;
87 } else {
88 img = [];
89 for (let i = 0; i < src.length; i++) {
90 img[i] = new Image();
91 img[i].src = src[i];
92 }
93 }
94 return img;
95 }
96
97 //天空类的配置项
98 const SKY = {
99 bg: bg,
100 width: 480,
101 height: 650,
102 speed: 10,
103 };
104
105 // 飞机加载界面的配置项
106 const LOADING = {
107 frame: loading_frame,
108 width: 186,
109 height: 38,
110 x: 0,
111 y: 650 - 38,
112 speed: 400,
113 };
114
115 // 英雄配置项
116 const HERO = {
117 frame: hero_frame,
118 width: 99,
119 height: 124,
120 speed: 100,
121 };
122
123 // 子弹配置项
124 const BULLET = {
125 img: b,
126 width: 9,
127 height: 21,
128 };
129
130 //小敌机配置项
131 const E1 = {
132 type: 1,
133 width: 57,
134 height: 51,
135 life: 10,
136 score: 1,
137 frame: e1,
138 minSpeed: 20,
139 maxSpeed: 10
140 };
141 //中敌机配置项
142 const E2 = {
143 type: 2,
144 width: 69,
145 height: 95,
146 life: 50,
147 score: 5,
148 frame: e2,
149 minSpeed: 50,
150 maxSpeed: 20
151 };
152 //打敌机配置项
153 const E3 = {
154 type: 3,
155 width: 169,
156 height: 258,
157 life: 100,
158 score: 20,
159 frame: e3,
160 minSpeed: 100,
161 maxSpeed: 100
162 };
163 //奖励类配置项
164 const C1 = {
165 type: 4,
166 width: 75,
167 height: 75,
168 life: 1,
169 score: 1,
170 img: c1,
171 minSpeed: 5,
172 maxSpeed: 10
173 };
174
175 //初始化一个天空类
176 class Sky {
177 constructor(config) {
178 this.bg = config.bg;
179 this.width = config.width;
180 this.height = config.height;
181 this.x1 = 0;
182 this.y1 = 0;
183 this.x2 = 0;
184 this.y2 = -this.height;
185 this.speed = config.speed;
186 this.lastTime = new Date().getTime();
187 }
188 judge() {
189 let currentTime = new Date().getTime();
190 if (currentTime - this.lastTime > this.speed) {
191 this.y1++;
192 this.y2++;
193 this.lastTime = currentTime;
194 }
195 if (this.y2 === 0) {
196 this.y1 = 0;
197 this.y2 = -this.height;
198 }
199 }
200 paint(context) {
201 context.drawImage(this.bg, this.x1, this.y1, this.width, this.height);
202 context.drawImage(this.bg, this.x2, this.y2, this.width, this.height);
203 }
204 }
205
206 // 初始化一个飞机界面加载类
207 class Loading {
208 constructor(config) {
209 this.frame = config.frame;
210 this.frameIndex = 0;
211 this.width = config.width;
212 this.height = config.height;
213 this.x = config.x;
214 this.y = config.y;
215 this.speed = config.speed;
216 this.lastTime = new Date().getTime();
217 }
218 judge() {
219 const currentTime = new Date().getTime();
220 if (currentTime - this.lastTime > this.speed) {
221 this.frameIndex++;
222 if (this.frameIndex === 4) {
223 state = RUNNING;
224 }
225 this.lastTime = currentTime;
226 }
227 }
228 paint(context) {
229 context.drawImage(this.frame[this.frameIndex], this.x, this.y);
230 }
231 }
232
233 // 初始化一个英雄类
234 class Hero {
235 constructor(config) {
236 this.width = config.width;
237 this.height = config.height;
238 this.x = (480 - config.width) / 2;
239 this.y = 650 - config.height;
240 this.frame = config.frame;
241 this.frameLiveIndex = 0;
242 this.frameDeathIndex = 0;
243 this.lastTime = new Date().getTime();
244 this.speed = config.speed;
245 this.img = null;
246 this.live = true;
247 this.lastShootTime = new Date().getTime();
248 this.shootInterval = 50; //直接控制子弹刷新速率
249 this.bulletList = [];
250 this.destory = false;
251 }
252 judge() {
253 const currentTime = new Date().getTime();
254 if (currentTime - this.lastTime > this.speed) {
255 if (this.live) {
256 this.img = this.frame.live[this.frameLiveIndex++ % this.frame.live.length];
257 } else {
258 this.img = this.frame.death[this.frameDeathIndex++];
259 if (this.frameDeathIndex === this.frame.death.length) {
260 this.destory = true;
261 }
262 }
263 this.lastTime = currentTime;
264 }
265 }
266 paint(context) {
267 context.drawImage(this.img, this.x, this.y, this.width, this.height);
268 }
269 shoot() {
270 const currentTime = new Date().getTime();
271 if (currentTime - this.lastShootTime > this.shootInterval) {
272 let bullet = new Bullet(BULLET, this.x + this.width / 2 - BULLET.width / 2, this.y - BULLET.height);
273 this.bulletList.push(bullet);
274 bullet.paint(context);
275 this.lastShootTime = currentTime;
276 }
277 }
278 collide() {
279 this.live = false;
280 }
281 }
282
283 //初始化一个子弹类
284 class Bullet {
285 constructor(config, x, y) {
286 this.img = config.img;
287 this.width = config.width;
288 this.height = config.height;
289 this.x = x;
290 this.y = y;
291 this.destory = false;
292 }
293 paint(context) {
294 context.drawImage(this.img, this.x, this.y);
295 }
296 move() {
297 this.y -= 8;
298 }
299 outOfBounds() {
300 return this.y < -this.height;
301 }
302 collide() {
303 this.destory = true;
304 }
305 }
306
307 // 初始化一个敌机类
308 class Enemy {
309 constructor(config) {
310 this.type = config.type;
311 this.width = config.width;
312 this.height = config.height;
313 this.x = Math.floor(Math.random() * (480 - config.width));
314 this.y = -config.height;
315 this.life = config.life;
316 this.score = config.score;
317 this.frame = config.frame;
318 this.img = this.frame.live[0];
319 this.live = true;
320 this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
321 this.lastTime = new Date().getTime();
322 this.deathIndex = 0;
323 this.destory = false;
324 }
325 move() {
326 const currentTime = new Date().getTime();
327 if (currentTime - this.lastTime >= this.speed) {
328 if (this.live) {
329 this.img = this.frame.live[0];
330 this.y++;
331 this.lastTime = currentTime;
332 } else {
333 this.img = this.frame.death[this.deathIndex++];
334 if (this.deathIndex === this.frame.death.length) {
335 this.destory = true;
336 }
337 }
338 }
339 }
340 paint(context) {
341 context.drawImage(this.img, this.x, this.y);
342 }
343 outOfBounds() {
344 if (this.y > 650) {
345 return true;
346 }
347 }
348 hit(o) {
349 let ol = o.x;
350 let or = o.x + o.width;
351 let ot = o.y;
352 let ob = o.y + o.height;
353 let el = this.x;
354 let er = this.x + this.width;
355 let et = this.y;
356 let eb = this.y + this.height;
357 if (ol > er || or < el || ot > eb || ob < et) {
358 return false;
359 } else {
360 return true;
361 }
362 }
363 collide() {
364 this.life--;
365 if (this.life === 0) {
366 this.live = false;
367 score += this.score;
368 }
369 }
370 }
371 //初始化奖励类
372 class award {
373 constructor(config) {
374 this.type = config.type;
375 this.width = config.width;
376 this.height = config.height;
377 this.x = Math.floor(Math.random() * (480 - config.width));
378 this.y = -config.height;
379 this.life = config.life;
380 this.score = config.score;
381 this.img = config.img;
382 this.live = true;
383 this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
384 this.lastTime = new Date().getTime();
385 this.deathIndex = 0;
386 this.destory = false;
387 }
388 move() {
389 const currentTime = new Date().getTime();
390 if (currentTime - this.lastTime >= this.speed) {
391 if (this.live) {
392 this.y = this.y + 6;
393 this.lastTime = currentTime;
394 } else {
395 this.destory = true;
396
397 }
398 }
399 }
400 paint(context) {
401 context.drawImage(this.img, this.x, this.y, this.width, this.height);
402 }
403 outOfBounds() {
404 if (this.y > 650) {
405 return true;
406 }
407 }
408 hit(o) {
409 let ol = o.x;
410 let or = o.x + o.width;
411 let ot = o.y;
412 let ob = o.y + o.height;
413 let el = this.x;
414 let er = this.x + this.width;
415 let et = this.y;
416 let eb = this.y + this.height;
417 if (ol > er || or < el || ot > eb || ob < et) {
418 return false;
419 } else {
420 return true;
421 }
422 }
423 // collide() {
424 // this.life--;
425 // if (this.life === 0) {
426 // this.live = false;
427 // score += this.score;
428 // }
429 // }
430 }
431 //初始化一个天空实例
432 const sky = new Sky(SKY);
433 //初始化一个飞机界面加载实例
434 const loading = new Loading(LOADING);
435 //初始化一个英雄实例 英雄是会变的
436 let hero = new Hero(HERO);
437 //state表示游戏的状态 取值必须是以上的五种状态
438 let state = START;
439 //score 分数变量 life 变量
440 let score = 0;
441 let life = 3;
442 //为canvas绑定一个点击事件 且他如果是START状态的时候需要修改成STARTING状态
443 canvas.addEventListener("click", () => {
444 if (state === START) {
445 state = STARTING;
446 }
447 });
448 // 为canvas绑定一个鼠标移动事件 鼠标正好在飞机图片的正中心
449 canvas.addEventListener("mousemove", (e) => {
450 let x = e.offsetX;
451 let y = e.offsetY;
452 hero.x = x - hero.width / 2;
453 hero.y = y - hero.height / 2;
454 });
455
456 // //为canvas绑定一个屏幕触碰事件 触碰点正好在飞机图片的正中心
457 // canvas.addEventListener("touchstart",(e)=>{
458 // let x = e.offsetX;
459 // let y = e.offsetY;
460 // hero.x = x - hero.width / 2;
461 // hero.y = y - hero.height / 2;
462 // })
463 //为canvas绑定一个屏幕移动触摸点事件 触碰点正好在飞机图片的正中心
464 canvas.addEventListener("touchmove", (e) => {
465 // let x = e.pageX;
466 // let y = e.pageY;
467 console.log(e);
468 // let x = e.touches[0].clientX;
469 // let y = e.touches[0].clinetY;
470 let x = e.touches[0].pageX;
471 let y = e.touches[0].pageY;
472 // let x = e.touches[0].screenX;
473 // let y = e.touches[0].screenY;
474 let write1 = (document.body.clientWidth - 480) / 2;
475 let write2 = (document.body.clientHeight - 650) / 2;
476 hero.x = x - write1 - hero.width / 2;
477 hero.y = y - write2 - hero.height / 2;
478
479 // hero.x = x - hero.width / 2;
480 // hero.y = y - hero.height / 2;
481 console.log(x, y);
482 console.log(document.body.clientWidth, document.body.clientHeight);
483 e.preventDefault(); // 阻止屏幕滚动的默认行为
484
485 })
486 // 为canvas绑定一个鼠标离开事件 鼠标离开时 RUNNING -> PAUSE
487 canvas.addEventListener("mouseleave", () => {
488 if (state === RUNNING) {
489 state = PAUSE;
490 }
491 });
492
493 // 为canvas绑定一个鼠标进入事件 鼠标进入时 PAUSE => RUNNING
494 canvas.addEventListener("mouseenter", () => {
495 if (state === PAUSE) {
496 state = RUNNING;
497 }
498 });
499 // 碰撞检测函数
500 //此处的碰撞检测包括
501 //1.子弹与敌机的碰撞
502 //2.英雄与敌机的碰撞
503 //3.英雄与随机奖励的碰撞
504 function checkHit() {
505 // 遍历所有的敌机
506 for (let i = 0; i < awards.length; i++) {
507 //检测英雄是否碰到奖励类
508 if (awards[i].hit(hero)) {
509 //当然了,这个随机奖励的样式也要删了
510 awards.splice(i, 1);
511 //清除所有的敌机
512 // for (let i = 0; i < enemies.length; i++) {
513 // enemies.splice(i, 1);
514 // }
515 enemies.length = 0;
516
517 }
518 }
519 for (let i = 0; i < enemies.length; i++) {
520 //检测英雄是否撞到敌机
521 if (enemies[i].hit(hero)) {
522 //将敌机和英雄的destory属性改为true
523 enemies[i].collide();
524 hero.collide();
525 }
526 for (let j = 0; j < hero.bulletList.length; j++) {
527 enemies[i].hit(hero.bulletList[j]);
528 //检测子弹是否撞到敌机
529 if (enemies[i].hit(hero.bulletList[j])) {
530 //将敌机和子弹的destory属性改为true
531 enemies[i].collide();
532 hero.bulletList[j].collide();
533 }
534 }
535 }
536 }
537 //该变量中有所有的敌机实例
538 let enemies = [];
539 //该变量中存放所有的奖励实例
540 let awards = [];
541 //敌机产生的速率
542 let ENEMY_CREATE_INTERVAL = 800;
543 let ENEMY_LASTTIME = new Date().getTime();
544 // 全局函数 隔一段时间就来初始化一架敌机/奖励
545 function createComponent() {
546 const currentTime = new Date().getTime();
547 if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {
548 let ran = Math.floor(Math.random() * 100);
549 if (ran < 55) {
550 enemies.push(new Enemy(E1));
551 } else if (ran < 85 && ran > 55) {
552 enemies.push(new Enemy(E2));
553 } else if (ran < 95 && ran > 85) {
554 enemies.push(new Enemy(E3));
555 } else if (ran > 95) {
556 awards.push(new award(C1));
557
558 }
559
560 ENEMY_LASTTIME = currentTime;
561 }
562 }
563 // 全局函数 来判断所有的子弹/敌人组件 "负责移动"
564 function judgeComponent() {
565 for (let i = 0; i < hero.bulletList.length; i++) {
566 hero.bulletList[i].move();
567 }
568 for (let i = 0; i < enemies.length; i++) {
569 enemies[i].move();
570 }
571 for (let i = 0; i < awards.length; i++) {
572 awards[i].move();
573 }
574 }
575 // 全局函数 来绘制所有的子弹/敌人组件 绘制score&life面板
576 function paintComponent() {
577 for (let i = 0; i < hero.bulletList.length; i++) {
578 hero.bulletList[i].paint(context);
579 }
580 for (let i = 0; i < enemies.length; i++) {
581 enemies[i].paint(context);
582 }
583 for (let i = 0; i < awards.length; i++) {
584 awards[i].paint(context);
585 }
586 context.font = "20px 微软雅黑";
587 context.fillStyle = "green";
588 context.textAlign = "left";
589 context.fillText("score: " + score, 10, 20);
590 context.textAlign = "right";
591 context.fillText("life: " + life, 480 - 10, 20);
592 //重置样式
593 context.fillStyle = "black";
594 context.textAlign = "left";
595 }
596 // 全局函数 来销毁所有的子弹/敌人组件 销毁掉英雄
597 function deleteComponent() {
598 if (hero.destory) {
599 life--;
600 hero.destory = false;
601 if (life === 0) {
602 state = END;
603 } else {
604 hero = new Hero(HERO);
605 }
606 }
607 for (let i = 0; i < hero.bulletList.length; i++) {
608 if (hero.bulletList[i].outOfBounds() || hero.bulletList[i].destory) {
609 hero.bulletList.splice(i, 1);
610 }
611 }
612 for (let i = 0; i < enemies.length; i++) {
613 if (enemies[i].outOfBounds() || enemies[i].destory) {
614 enemies.splice(i, 1);
615 }
616 }
617 }
618
619
620
621
622 //当图片加载完毕时,需要做某些事情
623 bg.addEventListener("load", () => {
624 setInterval(() => {
625 switch (state) {
626 case START:
627 sky.judge();
628 sky.paint(context);
629 let logo_x = (480 - copyright.naturalWidth) / 2;
630 let logo_y = (650 - copyright.naturalHeight) / 2;
631 context.drawImage(copyright, logo_x, logo_y);
632 break;
633 case STARTING:
634 sky.judge();
635 sky.paint(context);
636 loading.judge();
637 loading.paint(context);
638 break;
639 case RUNNING:
640 sky.judge();
641 sky.paint(context);
642 hero.judge();
643 hero.paint(context);
644 hero.shoot();
645 createComponent();
646 judgeComponent();
647 deleteComponent();
648 paintComponent();
649 checkHit();
650 break;
651 case PAUSE:
652 let pause_x = (480 - pause.naturalWidth) / 2;
653 let pause_y = (650 - pause.naturalHeight) / 2;
654 context.drawImage(pause, pause_x, pause_y);
655 break;
656 case END:
657 //给我的画笔设置一个字的样式
658 //后面写出来的字都是这个样式的
659 context.font = "bold 24px 微软雅黑";
660 context.textAlign = "center";
661 context.textBaseline = "middle";
662 context.fillText("GAME_OVER", 480 / 2, 650 / 2);
663 break;
664 }
665 }, 10);
666 });
667
668 //背景切换方法
669 function changebg() {
670 console.log("changebg方法被触发")
671 bg.src = "img/background.png"
672 }
673
674 }
675 };
676
677
678 export {
679 plane
680 };
然后打开index.html
直接报错,跨域问题
网上搜了一大片问题解决方案:
方案一:使用npm模块 anywhere
anywhere可以将你的当前目录变成一个静态文件服务器的根目录。
全局安装:
npm install anywhere -g
在index.html目录下;
anywhere
随后自动打开网页,不再报错
方案二:
使用Live Server插件
安装后,直接在右下角启动服务
方案三:
买个服务器,部署到服务器上
.....