소스 검색

first commit

张延森 3 년 전
커밋
54a4e184cc
14개의 변경된 파일6465개의 추가작업 그리고 0개의 파일을 삭제
  1. 23
    0
      .gitignore
  2. 24
    0
      README.md
  3. 5
    0
      babel.config.js
  4. 19
    0
      jsconfig.json
  5. 44
    0
      package.json
  6. BIN
      public/favicon.ico
  7. 17
    0
      public/index.html
  8. 43
    0
      src/App.vue
  9. BIN
      src/assets/logo.png
  10. 58
    0
      src/components/HelloWorld.vue
  11. 202
    0
      src/game.js
  12. 4
    0
      src/main.js
  13. 4
    0
      vue.config.js
  14. 6022
    0
      yarn.lock

+ 23
- 0
.gitignore 파일 보기

@@ -0,0 +1,23 @@
1
+.DS_Store
2
+node_modules
3
+/dist
4
+
5
+
6
+# local env files
7
+.env.local
8
+.env.*.local
9
+
10
+# Log files
11
+npm-debug.log*
12
+yarn-debug.log*
13
+yarn-error.log*
14
+pnpm-debug.log*
15
+
16
+# Editor directories and files
17
+.idea
18
+.vscode
19
+*.suo
20
+*.ntvs*
21
+*.njsproj
22
+*.sln
23
+*.sw?

+ 24
- 0
README.md 파일 보기

@@ -0,0 +1,24 @@
1
+# roundabout
2
+
3
+## Project setup
4
+```
5
+yarn install
6
+```
7
+
8
+### Compiles and hot-reloads for development
9
+```
10
+yarn serve
11
+```
12
+
13
+### Compiles and minifies for production
14
+```
15
+yarn build
16
+```
17
+
18
+### Lints and fixes files
19
+```
20
+yarn lint
21
+```
22
+
23
+### Customize configuration
24
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 5
- 0
babel.config.js 파일 보기

@@ -0,0 +1,5 @@
1
+module.exports = {
2
+  presets: [
3
+    '@vue/cli-plugin-babel/preset'
4
+  ]
5
+}

+ 19
- 0
jsconfig.json 파일 보기

@@ -0,0 +1,19 @@
1
+{
2
+  "compilerOptions": {
3
+    "target": "es5",
4
+    "module": "esnext",
5
+    "baseUrl": "./",
6
+    "moduleResolution": "node",
7
+    "paths": {
8
+      "@/*": [
9
+        "src/*"
10
+      ]
11
+    },
12
+    "lib": [
13
+      "esnext",
14
+      "dom",
15
+      "dom.iterable",
16
+      "scripthost"
17
+    ]
18
+  }
19
+}

+ 44
- 0
package.json 파일 보기

@@ -0,0 +1,44 @@
1
+{
2
+  "name": "roundabout",
3
+  "version": "0.1.0",
4
+  "private": true,
5
+  "scripts": {
6
+    "serve": "vue-cli-service serve",
7
+    "build": "vue-cli-service build",
8
+    "lint": "vue-cli-service lint"
9
+  },
10
+  "dependencies": {
11
+    "core-js": "^3.8.3",
12
+    "two.js": "^0.8.4",
13
+    "vue": "^3.2.13"
14
+  },
15
+  "devDependencies": {
16
+    "@babel/core": "^7.12.16",
17
+    "@babel/eslint-parser": "^7.12.16",
18
+    "@vue/cli-plugin-babel": "~5.0.0",
19
+    "@vue/cli-plugin-eslint": "~5.0.0",
20
+    "@vue/cli-service": "~5.0.0",
21
+    "eslint": "^7.32.0",
22
+    "eslint-plugin-vue": "^8.0.3"
23
+  },
24
+  "eslintConfig": {
25
+    "root": true,
26
+    "env": {
27
+      "node": true
28
+    },
29
+    "extends": [
30
+      "plugin:vue/vue3-essential",
31
+      "eslint:recommended"
32
+    ],
33
+    "parserOptions": {
34
+      "parser": "@babel/eslint-parser"
35
+    },
36
+    "rules": {}
37
+  },
38
+  "browserslist": [
39
+    "> 1%",
40
+    "last 2 versions",
41
+    "not dead",
42
+    "not ie 11"
43
+  ]
44
+}

BIN
public/favicon.ico 파일 보기


+ 17
- 0
public/index.html 파일 보기

@@ -0,0 +1,17 @@
1
+<!DOCTYPE html>
2
+<html lang="">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
8
+    <title><%= htmlWebpackPlugin.options.title %></title>
9
+  </head>
10
+  <body>
11
+    <noscript>
12
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
13
+    </noscript>
14
+    <div id="app"></div>
15
+    <!-- built files will be auto injected -->
16
+  </body>
17
+</html>

+ 43
- 0
src/App.vue 파일 보기

@@ -0,0 +1,43 @@
1
+<template>
2
+  <div ref="el"></div>
3
+</template>
4
+
5
+<script setup>
6
+  import { onBeforeUnmount, onMounted, ref } from 'vue';
7
+  import game from "./game";
8
+
9
+  const el = ref()
10
+  // const twoRef = ref()
11
+
12
+  onMounted(() => {
13
+    game({
14
+      el: el.value,
15
+      onError: () => {
16
+        alert('oo ~')
17
+      },
18
+      onSuccess: () => {
19
+        alert('你真牛逼')
20
+      }
21
+    })
22
+  })
23
+
24
+  onBeforeUnmount(() => {
25
+    // if (twoRef.value) {
26
+    //   twoRef.value.unbind('update');
27
+    //   twoRef.value.pause();
28
+    //   el.value.removeChild(twoRef.value.renderer.domElement);
29
+    // }
30
+  })
31
+
32
+</script>
33
+
34
+<style>
35
+#app {
36
+  font-family: Avenir, Helvetica, Arial, sans-serif;
37
+  -webkit-font-smoothing: antialiased;
38
+  -moz-osx-font-smoothing: grayscale;
39
+  text-align: center;
40
+  color: #2c3e50;
41
+  margin-top: 60px;
42
+}
43
+</style>

BIN
src/assets/logo.png 파일 보기


+ 58
- 0
src/components/HelloWorld.vue 파일 보기

@@ -0,0 +1,58 @@
1
+<template>
2
+  <div class="hello">
3
+    <h1>{{ msg }}</h1>
4
+    <p>
5
+      For a guide and recipes on how to configure / customize this project,<br>
6
+      check out the
7
+      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
8
+    </p>
9
+    <h3>Installed CLI Plugins</h3>
10
+    <ul>
11
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
12
+      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
13
+    </ul>
14
+    <h3>Essential Links</h3>
15
+    <ul>
16
+      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
17
+      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
18
+      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
19
+      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
20
+      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
21
+    </ul>
22
+    <h3>Ecosystem</h3>
23
+    <ul>
24
+      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
25
+      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
26
+      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
27
+      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
28
+      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
29
+    </ul>
30
+  </div>
31
+</template>
32
+
33
+<script>
34
+export default {
35
+  name: 'HelloWorld',
36
+  props: {
37
+    msg: String
38
+  }
39
+}
40
+</script>
41
+
42
+<!-- Add "scoped" attribute to limit CSS to this component only -->
43
+<style scoped>
44
+h3 {
45
+  margin: 40px 0 0;
46
+}
47
+ul {
48
+  list-style-type: none;
49
+  padding: 0;
50
+}
51
+li {
52
+  display: inline-block;
53
+  margin: 0 10px;
54
+}
55
+a {
56
+  color: #42b983;
57
+}
58
+</style>

+ 202
- 0
src/game.js 파일 보기

@@ -0,0 +1,202 @@
1
+
2
+import Two from "two.js";
3
+
4
+const colorList = ['#f5222d', '#d4380d', '#d46b08', '#d48806', '#d4b106', '#7cb305', '#389e0d', '#08979c', '#096dd9', '#531dab']
5
+
6
+export default function game({ el, onError, onSuccess }) {
7
+
8
+  // 是否游戏结束
9
+  let isFinished = false;
10
+
11
+  // 是否失败
12
+  // eslint-disable-next-line no-unused-vars
13
+  let isError = false;
14
+
15
+  // 每个区域的旋转角度, 10 个轿厢, 分 20 等份
16
+  const perAngle = 2 * Math.PI / 20 // 分20等份
17
+
18
+  // 大转盘半径
19
+  const raduis = 150;
20
+
21
+  // 初始化
22
+  const two = new Two({
23
+    fullscreen: true,
24
+    autostart: true
25
+  }).appendTo(el);
26
+
27
+  // 转盘中心坐标
28
+  const center = { x: two.width / 2, y: two.height / 2 }
29
+
30
+  // 轿厢 - 子弹
31
+  const bullets = []
32
+  
33
+  // 子弹是否在射击状态, 如果是, 不能进行其他操作
34
+  let isShooting = false
35
+  
36
+  // 子弹飞行速度, 因为是向上飞, y 值是不端减小的过程
37
+  const speed = -0.01;
38
+
39
+  // 目标轿厢列表, 该列表主要用来判断子弹是否击中目标
40
+  const cageList = [];
41
+
42
+  // 轿厢的初始旋转弧度
43
+  let rotateAngle = 0
44
+  
45
+  // 轿厢的旋转速度 - 单位弧度
46
+  const rotateSpeed = 0.01
47
+
48
+  // 目标轿厢与子弹轿厢的映射字典
49
+  const mntMap = {}
50
+
51
+  // 绘制转盘
52
+  const drawRoundAbout = () => {
53
+    const { x, y } = center
54
+
55
+    // 绘制扇形区域
56
+    // 该区域对游戏实际上没有用, 主要是梳理逻辑
57
+    const arcList = Array(10).fill().map((_, inx) => {
58
+      const startAngle = 2 * inx * perAngle
59
+      const endAngle = startAngle + perAngle
60
+      const arc = two.makeArcSegment(x, y, 0, raduis, startAngle, endAngle)
61
+      arc.fill = colorList[inx]
62
+      arc.noStroke()
63
+
64
+      // 绘制目标轿厢
65
+      // 1、计算扇形边上的中心坐标
66
+      const cx = x + Math.cos(startAngle + perAngle / 2) * raduis
67
+      const cy = y + Math.sin(startAngle + perAngle / 2) * raduis
68
+
69
+      // 2、轿厢的宽度, 等于圆周长的 1/20
70
+      const w = Math.PI * 2 * raduis / 20 // 周长 / 20
71
+      const rect = two.makeRectangle(cx, cy, w, w);
72
+      rect.id = `mnt-${inx}`
73
+      rect.fill = colorList[inx]
74
+      rect.noStroke()
75
+      rect.__$angle = startAngle  // 轿厢的初始弧度
76
+
77
+      cageList.push(rect)
78
+
79
+      return arc
80
+    })
81
+
82
+    return arcList
83
+  }
84
+  const roundAbout = drawRoundAbout()
85
+
86
+  // 旋转
87
+  const rotate = () => {
88
+    rotateAngle += rotateSpeed
89
+    roundAbout.forEach((x, inx) => {
90
+      x.rotation = rotateAngle
91
+            
92
+      // 计算旋转后的轿厢坐标
93
+      const cage = cageList[inx]
94
+      const angle = cage.__$angle + rotateAngle + perAngle / 2
95
+      const cx = center.x + Math.cos(angle) * raduis
96
+      const cy = center.y + Math.sin(angle) * raduis
97
+      cage.position = new Two.Vector(cx, cy)
98
+
99
+      // 如果有对应的挂载物
100
+      if (mntMap[cage.id]) {
101
+        mntMap[cage.id].position = cage.position
102
+      }
103
+    })
104
+  }
105
+
106
+  // 绘制子弹
107
+  const drawBullets = (x, y) => {
108
+    const w = 20, h = 20;
109
+
110
+    const list = Array(10).fill().map((_, inx) => {
111
+      const rect = two.makeRectangle(x, y, w, h)
112
+      rect.id = `bullet-${inx}`
113
+      rect.fill = 'black'
114
+      rect.noStroke()
115
+
116
+      return rect
117
+    })
118
+
119
+    bullets.push(...list)
120
+  }
121
+  drawBullets(center.x, center.y + 400) // 400 是距离转盘中心的长度
122
+  
123
+  // 待发射子弹
124
+  const clip = bullets.slice()
125
+
126
+  // 挂载到轮盘
127
+  const mountToCage = (targ, cage) => {
128
+    targ.position = cage.position
129
+    // 写入映射表
130
+    mntMap[cage.id] = targ
131
+  }
132
+  
133
+  // 当前第几个子弹
134
+  let currentBullet = null;
135
+  // 射击
136
+  const shooting = () => {
137
+    isShooting = true
138
+    const { left, width, top } = currentBullet.getBoundingClientRect()
139
+    currentBullet.position = new Two.Vector(left + width / 2, top + speed)
140
+
141
+    const hitted = isHit(currentBullet)
142
+    if (hitted === 'error') {
143
+      isError = true
144
+      isFinished = true
145
+      onError()
146
+      return
147
+    }
148
+
149
+    if (hitted) {
150
+      mountToCage(currentBullet, hitted)
151
+      currentBullet = null;
152
+      isShooting = false;
153
+
154
+      if (!clip.length) {
155
+        onSuccess()
156
+        isFinished = true
157
+      }
158
+    }
159
+  }
160
+
161
+  // 是否击中
162
+  // 如果击中, 则返回目标轿厢
163
+  const isHit = (bullet) => {
164
+    const rect2 = bullet.getBoundingClientRect();
165
+    const x = rect2.left + rect2.width / 2
166
+    const y = rect2.top + rect2.height / 2
167
+
168
+    if (y < (center.y + raduis)) {
169
+      return 'error';
170
+    }
171
+
172
+    const cage = cageList.filter((it) => {
173
+      const rect1 = it.getBoundingClientRect();
174
+      return x >= rect1.left && x <= rect1.right &&
175
+          y >= rect1.top && y <= rect1.bottom;
176
+    })[0]
177
+
178
+    if (!cage) return false;
179
+
180
+    return !cage ? false : cage
181
+  }
182
+  
183
+  // 重复绘制内容
184
+  two.bind('update', () => {
185
+    if (!isFinished) {
186
+      rotate()
187
+  
188
+      if (currentBullet) {
189
+        shooting()
190
+      }
191
+    }
192
+  })
193
+
194
+  // 绑定 dom click 事件 触发子弹发射
195
+  el.addEventListener('click', () => {
196
+    if (!isFinished) {
197
+      if (clip.length && !isShooting) {
198
+        currentBullet = clip.pop()
199
+      }
200
+    }
201
+  })
202
+}

+ 4
- 0
src/main.js 파일 보기

@@ -0,0 +1,4 @@
1
+import { createApp } from 'vue'
2
+import App from './App.vue'
3
+
4
+createApp(App).mount('#app')

+ 4
- 0
vue.config.js 파일 보기

@@ -0,0 +1,4 @@
1
+const { defineConfig } = require('@vue/cli-service')
2
+module.exports = defineConfig({
3
+  transpileDependencies: true
4
+})

+ 6022
- 0
yarn.lock
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기