First Commit

This commit is contained in:
jiutianzhiyu 2021-07-21 10:06:12 +08:00
commit 517fb6537d
15 changed files with 9902 additions and 0 deletions

1
.browserslistrc Normal file
View File

@ -0,0 +1 @@
last 2 versions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

817
dist/bundle.js vendored Normal file
View File

@ -0,0 +1,817 @@
/******/ (function() { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_cjs_js_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_node_modules_less_loader_dist_cjs_js_index_less__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
var options = {};
options.insert = "head";
options.singleton = false;
var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_node_modules_less_loader_dist_cjs_js_index_less__WEBPACK_IMPORTED_MODULE_1__.default, options);
/* harmony default export */ __webpack_exports__["default"] = (_node_modules_css_loader_dist_cjs_js_node_modules_postcss_loader_dist_cjs_js_ruleSet_1_rules_1_use_2_node_modules_less_loader_dist_cjs_js_index_less__WEBPACK_IMPORTED_MODULE_1__.default.locals || {});
/***/ }),
/* 2 */
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var isOldIE = function isOldIE() {
var memo;
return function memorize() {
if (typeof memo === 'undefined') {
// Test for IE <= 9 as proposed by Browserhacks
// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
// Tests for existence of standard globals is to allow style-loader
// to operate correctly into non-standard environments
// @see https://github.com/webpack-contrib/style-loader/issues/177
memo = Boolean(window && document && document.all && !window.atob);
}
return memo;
};
}();
var getTarget = function getTarget() {
var memo = {};
return function memorize(target) {
if (typeof memo[target] === 'undefined') {
var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself
if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
try {
// This will throw an exception if access to iframe is blocked
// due to cross-origin restrictions
styleTarget = styleTarget.contentDocument.head;
} catch (e) {
// istanbul ignore next
styleTarget = null;
}
}
memo[target] = styleTarget;
}
return memo[target];
};
}();
var stylesInDom = [];
function getIndexByIdentifier(identifier) {
var result = -1;
for (var i = 0; i < stylesInDom.length; i++) {
if (stylesInDom[i].identifier === identifier) {
result = i;
break;
}
}
return result;
}
function modulesToDom(list, options) {
var idCountMap = {};
var identifiers = [];
for (var i = 0; i < list.length; i++) {
var item = list[i];
var id = options.base ? item[0] + options.base : item[0];
var count = idCountMap[id] || 0;
var identifier = "".concat(id, " ").concat(count);
idCountMap[id] = count + 1;
var index = getIndexByIdentifier(identifier);
var obj = {
css: item[1],
media: item[2],
sourceMap: item[3]
};
if (index !== -1) {
stylesInDom[index].references++;
stylesInDom[index].updater(obj);
} else {
stylesInDom.push({
identifier: identifier,
updater: addStyle(obj, options),
references: 1
});
}
identifiers.push(identifier);
}
return identifiers;
}
function insertStyleElement(options) {
var style = document.createElement('style');
var attributes = options.attributes || {};
if (typeof attributes.nonce === 'undefined') {
var nonce = true ? __webpack_require__.nc : 0;
if (nonce) {
attributes.nonce = nonce;
}
}
Object.keys(attributes).forEach(function (key) {
style.setAttribute(key, attributes[key]);
});
if (typeof options.insert === 'function') {
options.insert(style);
} else {
var target = getTarget(options.insert || 'head');
if (!target) {
throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
}
target.appendChild(style);
}
return style;
}
function removeStyleElement(style) {
// istanbul ignore if
if (style.parentNode === null) {
return false;
}
style.parentNode.removeChild(style);
}
/* istanbul ignore next */
var replaceText = function replaceText() {
var textStore = [];
return function replace(index, replacement) {
textStore[index] = replacement;
return textStore.filter(Boolean).join('\n');
};
}();
function applyToSingletonTag(style, index, remove, obj) {
var css = remove ? '' : obj.media ? "@media ".concat(obj.media, " {").concat(obj.css, "}") : obj.css; // For old IE
/* istanbul ignore if */
if (style.styleSheet) {
style.styleSheet.cssText = replaceText(index, css);
} else {
var cssNode = document.createTextNode(css);
var childNodes = style.childNodes;
if (childNodes[index]) {
style.removeChild(childNodes[index]);
}
if (childNodes.length) {
style.insertBefore(cssNode, childNodes[index]);
} else {
style.appendChild(cssNode);
}
}
}
function applyToTag(style, options, obj) {
var css = obj.css;
var media = obj.media;
var sourceMap = obj.sourceMap;
if (media) {
style.setAttribute('media', media);
} else {
style.removeAttribute('media');
}
if (sourceMap && typeof btoa !== 'undefined') {
css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
} // For old IE
/* istanbul ignore if */
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
while (style.firstChild) {
style.removeChild(style.firstChild);
}
style.appendChild(document.createTextNode(css));
}
}
var singleton = null;
var singletonCounter = 0;
function addStyle(obj, options) {
var style;
var update;
var remove;
if (options.singleton) {
var styleIndex = singletonCounter++;
style = singleton || (singleton = insertStyleElement(options));
update = applyToSingletonTag.bind(null, style, styleIndex, false);
remove = applyToSingletonTag.bind(null, style, styleIndex, true);
} else {
style = insertStyleElement(options);
update = applyToTag.bind(null, style, options);
remove = function remove() {
removeStyleElement(style);
};
}
update(obj);
return function updateStyle(newObj) {
if (newObj) {
if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) {
return;
}
update(obj = newObj);
} else {
remove();
}
};
}
module.exports = function (list, options) {
options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
// tags it will allow on a page
if (!options.singleton && typeof options.singleton !== 'boolean') {
options.singleton = isOldIE();
}
list = list || [];
var lastIdentifiers = modulesToDom(list, options);
return function update(newList) {
newList = newList || [];
if (Object.prototype.toString.call(newList) !== '[object Array]') {
return;
}
for (var i = 0; i < lastIdentifiers.length; i++) {
var identifier = lastIdentifiers[i];
var index = getIndexByIdentifier(identifier);
stylesInDom[index].references--;
}
var newLastIdentifiers = modulesToDom(newList, options);
for (var _i = 0; _i < lastIdentifiers.length; _i++) {
var _identifier = lastIdentifiers[_i];
var _index = getIndexByIdentifier(_identifier);
if (stylesInDom[_index].references === 0) {
stylesInDom[_index].updater();
stylesInDom.splice(_index, 1);
}
}
lastIdentifiers = newLastIdentifiers;
};
};
/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);
// Imports
var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()(function(i){return i[1]});
// Module
___CSS_LOADER_EXPORT___.push([module.id, "* {\n margin: 0;\n padding: 0;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n}\nbody {\n width: 100vw;\n height: 100vh;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-pack: center;\n -ms-flex-pack: center;\n justify-content: center;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n}\n.main {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n -ms-flex-direction: column;\n flex-direction: column;\n -webkit-box-align: center;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: distribute;\n justify-content: space-around;\n width: 360px;\n height: 420px;\n background-color: #ccc;\n border: 10px solid black;\n border-radius: 40px;\n}\n.stage {\n position: relative;\n width: 304px;\n height: 304px;\n border: 2px solid black;\n}\n.stage .snake > div {\n position: absolute;\n width: 10px;\n height: 10px;\n background-color: #000;\n border: 1px solid #ccc;\n}\n.stage .food {\n position: absolute;\n top: 0;\n left: 20px;\n width: 10px;\n height: 10px;\n background-color: yellow;\n border: 1px solid #ccc;\n border-radius: 50%;\n}\n.point {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-pack: distribute;\n justify-content: space-around;\n width: 100%;\n}\n", ""]);
// Exports
/* harmony default export */ __webpack_exports__["default"] = (___CSS_LOADER_EXPORT___);
/***/ }),
/* 4 */
/***/ (function(module) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
// eslint-disable-next-line func-names
module.exports = function (cssWithMappingToString) {
var list = []; // return the list of modules as css string
list.toString = function toString() {
return this.map(function (item) {
var content = cssWithMappingToString(item);
if (item[2]) {
return "@media ".concat(item[2], " {").concat(content, "}");
}
return content;
}).join("");
}; // import a list of modules into the list
// eslint-disable-next-line func-names
list.i = function (modules, mediaQuery, dedupe) {
if (typeof modules === "string") {
// eslint-disable-next-line no-param-reassign
modules = [[null, modules, ""]];
}
var alreadyImportedModules = {};
if (dedupe) {
for (var i = 0; i < this.length; i++) {
// eslint-disable-next-line prefer-destructuring
var id = this[i][0];
if (id != null) {
alreadyImportedModules[id] = true;
}
}
}
for (var _i = 0; _i < modules.length; _i++) {
var item = [].concat(modules[_i]);
if (dedupe && alreadyImportedModules[item[0]]) {
// eslint-disable-next-line no-continue
continue;
}
if (mediaQuery) {
if (!item[2]) {
item[2] = mediaQuery;
} else {
item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
}
}
list.push(item);
}
};
return list;
};
/***/ }),
/* 5 */
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _snake__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
/* harmony import */ var _food__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
//游戏控制器
var GameControl = /*#__PURE__*/function () {
function GameControl() {
_classCallCheck(this, GameControl);
// 存储移动的方向
this.direction = ''; // 游戏是否继续
this.isLive = true;
this.snake = new _snake__WEBPACK_IMPORTED_MODULE_0__.default();
this.food = new _food__WEBPACK_IMPORTED_MODULE_1__.default();
this.point = new _point__WEBPACK_IMPORTED_MODULE_2__.default(10, 3);
this.init();
} // 游戏初始化的方法
_createClass(GameControl, [{
key: "init",
value: function init() {
// 绑定键盘事件
document.addEventListener('keydown', this.keydownHandle.bind(this)); // document调用这个函数, 这里的this是document 所以要么改成箭头函数要么用bind
this.run();
} // 按键按下事件
}, {
key: "keydownHandle",
value: function keydownHandle(e) {
// console.log(this)
// console.log(e.key);
this.direction = e.key;
} // 蛇移动的方法
}, {
key: "run",
value: function run() {
var X = this.snake.X;
var Y = this.snake.Y; // 检查有没有吃到食物
this.checkEat(X, Y); // 控制移动
switch (this.direction) {
// 上
case "ArrowUp":
case "Up":
Y -= 10;
break;
//下
case "ArrowDown":
case "Down":
Y += 10;
break;
// 左
case "ArrowLeft":
case "Left":
X -= 10;
break;
// 右
case "ArrowRight":
case "Right":
X += 10;
break;
} // 先检查有没有撞墙
try {
this.snake.X = X;
this.snake.Y = Y;
} catch (e) {
// 捕获到异常, 结束运行
alert(e.message);
clearTimeout(this.timeID);
this.isLive = false;
}
this.timeID && clearTimeout(this.timeID); // 短路运算
if (this.isLive) {
this.timeID = setTimeout(this.run.bind(this), 300 - (this.point.level - 1) * 30);
}
} // 检查是否吃到食物
}, {
key: "checkEat",
value: function checkEat(X, Y) {
if (X === this.food.X && Y === this.food.Y) {
console.log('吃到食物');
this.food.change();
this.point.addScore();
this.snake.addBody();
}
}
}]);
return GameControl;
}();
/* harmony default export */ __webpack_exports__["default"] = (GameControl);
/***/ }),
/* 6 */
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var Snake = /*#__PURE__*/function () {
function Snake() {
_classCallCheck(this, Snake);
this.element = document.getElementById('snake');
this.head = document.querySelector('#snake>div'); // 提示类型不匹配,断言
this.bodies = document.getElementById('snake').getElementsByTagName('div'); //query选择符选出来的元素及元素数组是静态的而getElement这种方法选出的元素是动态的。静态的就是说选出的所有元素的数组不会随着文档操作而改变
// 在使用的时候getElement这种方法性能比较好query选择符则比较方便
} // 蛇头的坐标
_createClass(Snake, [{
key: "X",
get: function get() {
return this.head.offsetLeft;
},
set: // 设置蛇头的坐标
function set(value) {
if (this.X == value) return; // 蛇撞墙
if (value < 0 || value > 290) {
throw new Error('蛇撞墙了');
} // 没撞墙
// 长度大于1时禁止掉头
if (this.bodies[1] && this.bodies[1].offsetLeft === value) {
console.log("发生了掉头");
if (value > this.X) {
// 向右掉头, 则要往左修正value
value = this.X - 10;
} else {
value = this.X + 10;
}
} // 移动身体
this.moveBody(); // 这里同样要先移动身体, 再移动头
// 移动头
this.head.style.left = value + 'px'; // 检查头是否撞到了身体
if (this.bodies.length > 4) {
this.checkBreak();
}
}
}, {
key: "Y",
get: function get() {
return this.head.offsetTop;
},
set: function set(value) {
if (this.Y == value) return; // 蛇撞墙
if (value < 0 || value > 290) {
throw new Error('蛇撞墙了');
} // 没撞墙
// 长度大于1 禁止掉头
if (this.bodies[1] && this.bodies[1].offsetTop === value) {
console.log("发生了掉头");
if (value > this.Y) {
// 向下掉头, 则要往上修正value
value = this.Y - 10;
} else {
value = this.Y + 10;
}
}
this.moveBody();
this.head.style.top = value + 'px'; // 检查头是否撞到了身体
this.checkBreak();
} // 增加身体
}, {
key: "addBody",
value: function addBody() {
this.element.insertAdjacentHTML("beforeend", "<div></div>");
} // 移动身体
// 最后一节移到倒数第二节, 把倒数第二节移到倒数第三节 ... 从后开始移动
// 蛇头不用管
}, {
key: "moveBody",
value: function moveBody() {
for (var i = this.bodies.length - 1; i > 0; i--) {
// 前一截的位置
var x = this.bodies[i - 1].offsetLeft;
var y = this.bodies[i - 1].offsetTop; // HTMLElement 对象表示 HTML 中的一个元素。
// HTMLElement 对象继承了Element 对象的标准属性,也实现了一些非标准属性
// HTMLElement 简单的看成Element的子类
// 给当前的一截赋值
this.bodies[i].style.left = x + 'px';
this.bodies[i].style.top = y + 'px';
}
} // 检查撞身体
}, {
key: "checkBreak",
value: function checkBreak() {
for (var i = 4; i < this.bodies.length; i++) {
var bd = this.bodies[i];
if (bd.offsetLeft === this.X && bd.offsetTop === this.Y) {
throw new Error('蛇撞到自己了');
}
}
}
}]);
return Snake;
}();
/* harmony default export */ __webpack_exports__["default"] = (Snake);
/***/ }),
/* 7 */
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
// 定义食物类
var Food = /*#__PURE__*/function () {
function Food() {
_classCallCheck(this, Food);
this.element = document.getElementById('food'); // 这里的感叹号是非null和非undefined的类型断言
this.change();
} // 获取食物的坐标
_createClass(Food, [{
key: "X",
get: function get() {
return this.element.offsetLeft;
}
}, {
key: "Y",
get: function get() {
return this.element.offsetTop;
} // 修改食物的坐标
}, {
key: "change",
value: function change() {
this.element.style.left = Math.round(Math.random() * 29) * 10 + 'px';
this.element.style.top = Math.round(Math.random() * 29) * 10 + 'px';
}
}]);
return Food;
}();
/* harmony default export */ __webpack_exports__["default"] = (Food);
/***/ }),
/* 8 */
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
//定义统计类
var Point = /*#__PURE__*/function () {
function Point() {
var maxLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 10;
var upScore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
_classCallCheck(this, Point);
this.score = 0;
this.level = 1;
this.maxLevel = maxLevel;
this.upScore = upScore;
this.scoreEle = document.getElementById('score');
this.levelEle = document.getElementById('level');
} // 增加积分
_createClass(Point, [{
key: "addScore",
value: function addScore() {
this.scoreEle.innerHTML = ++this.score + ''; // 整除时升级
if (this.score % this.upScore === 0) {
this.levelUp();
}
} // 增加等级
}, {
key: "levelUp",
value: function levelUp() {
if (this.level < this.maxLevel) {
this.levelEle.innerHTML = ++this.level + '';
}
}
}]);
return Point;
}();
/* harmony default export */ __webpack_exports__["default"] = (Point);
/***/ })
/******/ ]);
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ !function() {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
!function() {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _index_less__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var _modules_gameControl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
new _modules_gameControl__WEBPACK_IMPORTED_MODULE_1__.default(); // 调试
}();
/******/ })()
;

30
dist/index.html vendored Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>贪食蛇</title>
<script defer src="bundle.js"></script></head>
<body>
<!-- 主容器 -->
<div class="main">
<!-- 游戏区域 -->
<div class="stage">
<!---->
<div class="snake" id="snake">
<div></div>
</div>
<!-- 食物 -->
<div class="food" id="food">
<div></div>
</div>
</div>
<!-- 统计区域 -->
<div class="point">
<div class="score">Score: <span id="score">0</span></div>
<div class="level">Level: <span id="level">1</span></div>
</div>
</div>
</body>
</html>

8538
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

34
package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack serve --open chrome.exe"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/preset-env": "^7.13.10",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"core-js": "^3.9.1",
"css-loader": "^5.1.3",
"html-webpack-plugin": "^5.3.1",
"less": "^4.1.1",
"less-loader": "^8.0.0",
"postcss": "^8.2.8",
"postcss-loader": "^5.2.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^2.0.0",
"ts-loader": "^8.0.18",
"typescript": "^4.2.3",
"webpack": "^5.26.0",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.2"
}
}

30
src/index.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>贪食蛇</title>
</head>
<body>
<!-- 主容器 -->
<div class="main">
<!-- 游戏区域 -->
<div class="stage">
<!---->
<div class="snake" id="snake">
<div></div>
</div>
<!-- 食物 -->
<div class="food" id="food">
<div></div>
</div>
</div>
<!-- 统计区域 -->
<div class="point">
<div class="score">Score: <span id="score">0</span></div>
<div class="level">Level: <span id="level">1</span></div>
</div>
</div>
</body>
</html>

59
src/index.less Normal file
View File

@ -0,0 +1,59 @@
@bgColor: #ccc;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.main {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 360px;
height: 420px;
background-color: @bgColor;
border: 10px solid black;
border-radius: 40px;
}
.stage{
position: relative;
width: 304px;
height: 304px;
border: 2px solid black;
.snake{
&>div{
position: absolute;
width: 10px;
height: 10px;
background-color: #000;
border: 1px solid @bgColor;
}
}
.food{
position: absolute;
top: 0;
left: 20px;
width: 10px;
height: 10px;
background-color: yellow;
border: 1px solid @bgColor;
border-radius: 50%;
}
}
.point{
display: flex;
justify-content: space-around;
width: 100%;
}

5
src/index.ts Normal file
View File

@ -0,0 +1,5 @@
import './index.less'
import gameControl from './modules/gameControl'
new gameControl()
// 调试

28
src/modules/food.ts Normal file
View File

@ -0,0 +1,28 @@
// 定义食物类
class Food {
// 定义一个属性, 食物对应的元素
element: HTMLElement // HTMLElement为ts带的类型
constructor(){
this.element = document.getElementById('food')!; // 这里的感叹号是非null和非undefined的类型断言
this.change()
}
// 获取食物的坐标
get X(){
return this.element.offsetLeft
}
get Y(){
return this.element.offsetTop
}
// 修改食物的坐标
change(){
this.element.style.left = Math.round(Math.random() * 29 ) * 10 + 'px'
this.element.style.top = Math.round(Math.random() * 29 ) * 10 + 'px'
}
}
export default Food

View File

@ -0,0 +1,95 @@
//游戏控制器
import Snake from './snake'
import Food from './food'
import Point from './point'
class GameControl {
snake: Snake
food: Food
point: Point
timeID: any
// 存储移动的方向
direction: string = ''
// 游戏是否继续
isLive: boolean = true
constructor(){
this.snake = new Snake()
this.food = new Food()
this.point = new Point(10, 3)
this.init()
}
// 游戏初始化的方法
init (){
// 绑定键盘事件
document.addEventListener('keydown',this.keydownHandle.bind(this))
// document调用这个函数, 这里的this是document 所以要么改成箭头函数要么用bind
this.run()
}
// 按键按下事件
keydownHandle(e:KeyboardEvent){
// console.log(this)
// console.log(e.key);
this.direction = e.key
}
// 蛇移动的方法
run(){
let X = this.snake.X
let Y = this.snake.Y
// 检查有没有吃到食物
this.checkEat(X, Y)
// 控制移动
switch(this.direction) {
// 上
case "ArrowUp":
case "Up": Y -= 10
break
//下
case "ArrowDown":
case "Down": Y += 10
break
// 左
case "ArrowLeft":
case "Left": X -= 10
break
// 右
case "ArrowRight":
case "Right": X += 10
break
}
// 先检查有没有撞墙
try{
this.snake.X = X
this.snake.Y = Y
}catch(e){
// 捕获到异常, 结束运行
alert(e.message)
clearTimeout(this.timeID)
this.isLive = false
}
this.timeID && clearTimeout(this.timeID) // 短路运算
if(this.isLive){
this.timeID = setTimeout (this.run.bind(this), 300 - (this.point.level - 1) * 30)
}
}
// 检查是否吃到食物
checkEat(X:number, Y:number){
if(X === this.food.X && Y === this.food.Y){
console.log('吃到食物');
this.food.change()
this.point.addScore()
this.snake.addBody()
}
}
}
export default GameControl

38
src/modules/point.ts Normal file
View File

@ -0,0 +1,38 @@
//定义统计类
class Point {
score = 0
level = 1
scoreEle:HTMLElement
levelEle:HTMLElement
// 最高等级限制
maxLevel: number
// 多少分升一级
upScore: number
constructor(maxLevel:number=10, upScore:number = 10){
this.maxLevel = maxLevel
this.upScore = upScore
this.scoreEle = document.getElementById('score')!
this.levelEle = document.getElementById('level')!
}
// 增加积分
addScore(){
this.scoreEle.innerHTML = ++this.score + ''
// 整除时升级
if(this.score % this.upScore === 0){
this.levelUp()
}
}
// 增加等级
levelUp(){
if(this.level < this.maxLevel){
this.levelEle.innerHTML = ++this.level + ''
}
}
}
export default Point

114
src/modules/snake.ts Normal file
View File

@ -0,0 +1,114 @@
class Snake{
element: HTMLElement
// 蛇头
head:HTMLElement
// 蛇的身体(包括蛇头)
bodies: HTMLCollection
constructor(){
this.element = document.getElementById('snake')!
this.head = document.querySelector('#snake>div') as HTMLElement // 提示类型不匹配,断言
this.bodies = document.getElementById('snake')!.getElementsByTagName('div')
//query选择符选出来的元素及元素数组是静态的而getElement这种方法选出的元素是动态的。静态的就是说选出的所有元素的数组不会随着文档操作而改变
// 在使用的时候getElement这种方法性能比较好query选择符则比较方便
}
// 蛇头的坐标
get X(){
return this.head.offsetLeft
}
get Y(){
return this.head.offsetTop
}
// 设置蛇头的坐标
set X(value: number){
if(this.X == value) return
// 蛇撞墙
if(value < 0 || value > 290){
throw new Error('蛇撞墙了')
}
// 没撞墙
// 长度大于1时禁止掉头
if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value){
console.log("发生了掉头");
if(value > this.X){
// 向右掉头, 则要往左修正value
value = this.X - 10
} else {
value = this.X + 10
}
}
// 移动身体
this.moveBody() // 这里同样要先移动身体, 再移动头
// 移动头
this.head.style.left = value + 'px'
// 检查头是否撞到了身体
if(this.bodies.length > 4){
this.checkBreak()
}
}
set Y(value: number){
if(this.Y == value) return
// 蛇撞墙
if(value < 0 || value > 290){
throw new Error('蛇撞墙了')
}
// 没撞墙
// 长度大于1 禁止掉头
if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value){
console.log("发生了掉头");
if(value > this.Y){
// 向下掉头, 则要往上修正value
value = this.Y - 10
} else {
value = this.Y + 10
}
}
this.moveBody()
this.head.style.top = value + 'px'
// 检查头是否撞到了身体
this.checkBreak()
}
// 增加身体
addBody (){
this.element.insertAdjacentHTML("beforeend", "<div></div>")
}
// 移动身体
// 最后一节移到倒数第二节, 把倒数第二节移到倒数第三节 ... 从后开始移动
// 蛇头不用管
moveBody(){
for(let i = this.bodies.length - 1; i > 0; i--){
// 前一截的位置
let x = (this.bodies[i - 1] as HTMLElement).offsetLeft
let y = (this.bodies[i - 1] as HTMLElement).offsetTop;
// HTMLElement 对象表示 HTML 中的一个元素。
// HTMLElement 对象继承了Element 对象的标准属性,也实现了一些非标准属性
// HTMLElement 简单的看成Element的子类
// 给当前的一截赋值
(this.bodies[i] as HTMLElement).style.left = x + 'px';
(this.bodies[i] as HTMLElement).style.top = y + 'px';
}
}
// 检查撞身体
checkBreak(){
for(let i = 4; i < this.bodies.length; i++){
let bd = this.bodies[i] as HTMLElement
if(bd.offsetLeft === this.X && bd.offsetTop === this.Y){
throw new Error('蛇撞到自己了')
}
}
}
}
export default Snake

9
tsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
// 便
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true,
"noEmitOnError": true
}
}

103
webpack.config.js Normal file
View File

@ -0,0 +1,103 @@
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// webpack中的所有信息都写在这里
module.exports = {
mode: 'none',
// 指定入口文件
entry: './src/index.ts',
//指定打包文件的左右目录
output: {
// 制定打包文件的目录
path: path.resolve(__dirname, 'dist'),
// 打包后文件的文件名
filename: 'bundle.js',
// 告诉webpack不使用箭头函数,const
environment: {
arrowFunction: false,
const:false
},
},
// 指定webpack打包时要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 使用loader, loader的执行顺序是从下往上的
use: [
// 配置babel
{
// 指定加载器
loader: 'babel-loader',
// 配置项
options: {
// 设置预定义环境
presets: [
[
// 指定环境的插件
'@babel/preset-env',
// 配置信息
{
// 要兼容的浏览器
targets: {
chrome: '58',
ie: '11',
},
// 指定corejs的版本
corejs: '3',
// 使用corejs的方式, usage按需加载
useBuiltIns: 'usage',
},
],
],
},
},
'ts-loader',
],
// 排除的文件
exclude: /node-modules/,
},
// less文件的处理
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// postcss配置
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env"
]
}
}
},
'less-loader'
],
},
],
},
// webpack插件配置
plugins: [
// 大包前先清空旧的文件
new CleanWebpackPlugin(),
new htmlWebpackPlugin({
// title: "自定义标题"
template: './src/index.html',
}),
],
// 设置引用模块
resolve: {
extensions: ['.ts', '.js'],
},
}