├── .gitignore
├── README.md
├── app.js
├── bin
└── www
├── jump.js
├── package-lock.json
├── package.json
├── public
├── images
│ ├── demos
│ │ ├── 1.png
│ │ ├── 10.png
│ │ ├── 11.png
│ │ ├── 12.png
│ │ ├── 13.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ └── 9.png
│ └── jump_screencap
│ │ └── screencap.png
├── javascripts
│ ├── demo.js
│ ├── index.js
│ ├── jump-test.js
│ ├── jump.js
│ ├── jump2.js
│ ├── test.js
│ └── zepto.min.js
└── stylesheets
│ └── style.css
├── routes
├── demo.js
├── index.js
├── jump.js
└── test.js
├── screencapture.png
└── views
├── demo.ejs
├── error.ejs
├── index.ejs
└── test.ejs
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 微信小游戏跳一跳作弊器
2 |
3 | 最近这么火,没有必要不玩一下!
4 |
5 | ## 使用
6 | ```bash
7 | npm i
8 | npm start
9 | // open localhost:3000/demo 查看识别效果
10 | ```
11 |
12 | 1. 安卓手机打开开发者选项usb调试,小米要允许模拟点击事件
13 | 2. 按照上面步骤启动程序
14 | 3. 数据线连上电脑,打开微信「跳一跳」点击开始
15 | 4. 打开 localhost:3000 自动跳转,打开devtools查看日志
16 |
17 | ## 进展
18 |
19 | 1. 目前完成图片canvas识别位置
20 |
21 | 
22 |
23 | 2. 自动跳转完成
24 |
25 | ## 原理
26 | **只支持安卓**
27 |
28 | 1. 首先通过adb截图拉取到本地
29 | 2. 对本地图片通过Canvas获取当前和跳转位置
30 | 3. 通过计算两点距离,根据720等比例缩放,然后乘以系数2.04即为时间
31 | 4. 通过adb发送长按事件,事件为第三步计算的时间
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 |
8 | var index = require('./routes/index');
9 | var demo = require('./routes/demo');
10 | var jump = require('./routes/jump');
11 | var test = require('./routes/test');
12 | var app = express();
13 |
14 | // view engine setup
15 | app.set('views', path.join(__dirname, 'views'));
16 | app.set('view engine', 'ejs');
17 |
18 | // uncomment after placing your favicon in /public
19 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
20 | app.use(logger('dev'));
21 | app.use(bodyParser.json());
22 | app.use(bodyParser.urlencoded({extended: false}));
23 | app.use(cookieParser());
24 | app.use(express.static(path.join(__dirname, 'public')));
25 | app.use(function(err, req, res, next) {
26 | res.json = function(data) {
27 | res.send(JSON.stringify(data));
28 | };
29 | next();
30 | });
31 |
32 | app.use('/', index);
33 | app.use('/demo', demo);
34 | app.use('/test', test);
35 | app.use('/jumponejump', jump);
36 | // catch 404 and forward to error handler
37 | app.use(function(req, res, next) {
38 | var err = new Error('Not Found');
39 | err.status = 404;
40 | next(err);
41 | });
42 |
43 | // error handler
44 | app.use(function(err, req, res, next) {
45 | // set locals, only providing error in development
46 | res.locals.message = err.message;
47 | res.locals.error = req.app.get('env') === 'development' ? err : {};
48 |
49 | // render the error page
50 | res.status(err.status || 500);
51 | res.render('error');
52 | });
53 |
54 | module.exports = app;
55 |
--------------------------------------------------------------------------------
/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('express:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string'
87 | ? 'pipe ' + addr
88 | : 'port ' + addr.port;
89 | debug('Listening on ' + bind);
90 | }
91 |
--------------------------------------------------------------------------------
/jump.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by beiwan on 2017/12/29.
3 | */
4 | const util = require('util');
5 | const fs = require('fs');
6 | const path = require('path');
7 | const exec = util.promisify(require('child_process').exec);
8 |
9 | const ADB_PATH = 'adb';
10 | const SCREENCAP_REMOTE_PATH = '/sdcard/screencap.png';
11 | const SCREENCAP_PATH = path.resolve('.', 'public/images/jump_screencap');
12 |
13 | const BOOM = 2.04;
14 |
15 | jumpGo = async (timeout) => {
16 | let r = Math.random() * 20;
17 | if (timeout > 0 && !isNaN(timeout)) {
18 | const {stdout} = await exec(`${ADB_PATH} shell input swipe ${r + 10} ${r + 20} ${r - 10} ${r - 2} ${timeout}`);
19 | console.log(stdout, timeout);
20 | }
21 | };
22 |
23 | fetchScreenCap = async () => {
24 | const {stdout, stderr} = await exec(`${ADB_PATH} shell screencap -p ${SCREENCAP_REMOTE_PATH}`);
25 | console.log('fetch...');
26 | };
27 |
28 | pullScreenCap = async () => {
29 | const {stdout, stderr} = await exec(
30 | `${ADB_PATH} pull ${SCREENCAP_REMOTE_PATH} ${SCREENCAP_PATH}/screencap.png`,
31 | []
32 | );
33 | console.log('pull...');
34 | };
35 |
36 | iJump = async (distance) => {
37 | await jumpGo(parseInt(distance * BOOM));
38 | await setTimeout(async () => {
39 | await fetchScreenCap();
40 | await pullScreenCap();
41 | }, 2000);
42 | };
43 |
44 | refreshScreencap = async () => {
45 | await fetchScreenCap();
46 | await pullScreenCap();
47 | };
48 |
49 | module.exports = {
50 | iJump,
51 | refreshScreencap
52 | };
53 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express",
3 | "version": "0.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.4",
9 | "resolved": "http://pnpm.baidu.com/accepts/-/accepts-1.3.4.tgz",
10 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
11 | "requires": {
12 | "mime-types": "2.1.17",
13 | "negotiator": "0.6.1"
14 | }
15 | },
16 | "array-flatten": {
17 | "version": "1.1.1",
18 | "resolved": "http://pnpm.baidu.com/array-flatten/-/array-flatten-1.1.1.tgz",
19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
20 | },
21 | "basic-auth": {
22 | "version": "2.0.0",
23 | "resolved": "http://pnpm.baidu.com/basic-auth/-/basic-auth-2.0.0.tgz",
24 | "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
25 | "requires": {
26 | "safe-buffer": "5.1.1"
27 | }
28 | },
29 | "body-parser": {
30 | "version": "1.18.2",
31 | "resolved": "http://pnpm.baidu.com/body-parser/-/body-parser-1.18.2.tgz",
32 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
33 | "requires": {
34 | "bytes": "3.0.0",
35 | "content-type": "1.0.4",
36 | "debug": "2.6.9",
37 | "depd": "1.1.1",
38 | "http-errors": "1.6.2",
39 | "iconv-lite": "0.4.19",
40 | "on-finished": "2.3.0",
41 | "qs": "6.5.1",
42 | "raw-body": "2.3.2",
43 | "type-is": "1.6.15"
44 | }
45 | },
46 | "bytes": {
47 | "version": "3.0.0",
48 | "resolved": "http://pnpm.baidu.com/bytes/-/bytes-3.0.0.tgz",
49 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
50 | },
51 | "content-disposition": {
52 | "version": "0.5.2",
53 | "resolved": "http://pnpm.baidu.com/content-disposition/-/content-disposition-0.5.2.tgz",
54 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
55 | },
56 | "content-type": {
57 | "version": "1.0.4",
58 | "resolved": "http://pnpm.baidu.com/content-type/-/content-type-1.0.4.tgz",
59 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
60 | },
61 | "cookie": {
62 | "version": "0.3.1",
63 | "resolved": "http://pnpm.baidu.com/cookie/-/cookie-0.3.1.tgz",
64 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
65 | },
66 | "cookie-parser": {
67 | "version": "1.4.3",
68 | "resolved": "http://pnpm.baidu.com/cookie-parser/-/cookie-parser-1.4.3.tgz",
69 | "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
70 | "requires": {
71 | "cookie": "0.3.1",
72 | "cookie-signature": "1.0.6"
73 | }
74 | },
75 | "cookie-signature": {
76 | "version": "1.0.6",
77 | "resolved": "http://pnpm.baidu.com/cookie-signature/-/cookie-signature-1.0.6.tgz",
78 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
79 | },
80 | "debug": {
81 | "version": "2.6.9",
82 | "resolved": "http://pnpm.baidu.com/debug/-/debug-2.6.9.tgz",
83 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
84 | "requires": {
85 | "ms": "2.0.0"
86 | }
87 | },
88 | "depd": {
89 | "version": "1.1.1",
90 | "resolved": "http://pnpm.baidu.com/depd/-/depd-1.1.1.tgz",
91 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
92 | },
93 | "destroy": {
94 | "version": "1.0.4",
95 | "resolved": "http://pnpm.baidu.com/destroy/-/destroy-1.0.4.tgz",
96 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
97 | },
98 | "ee-first": {
99 | "version": "1.1.1",
100 | "resolved": "http://pnpm.baidu.com/ee-first/-/ee-first-1.1.1.tgz",
101 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
102 | },
103 | "ejs": {
104 | "version": "2.5.7",
105 | "resolved": "http://pnpm.baidu.com/ejs/-/ejs-2.5.7.tgz",
106 | "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo="
107 | },
108 | "encodeurl": {
109 | "version": "1.0.1",
110 | "resolved": "http://pnpm.baidu.com/encodeurl/-/encodeurl-1.0.1.tgz",
111 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
112 | },
113 | "escape-html": {
114 | "version": "1.0.3",
115 | "resolved": "http://pnpm.baidu.com/escape-html/-/escape-html-1.0.3.tgz",
116 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
117 | },
118 | "etag": {
119 | "version": "1.8.1",
120 | "resolved": "http://pnpm.baidu.com/etag/-/etag-1.8.1.tgz",
121 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
122 | },
123 | "express": {
124 | "version": "4.15.5",
125 | "resolved": "http://pnpm.baidu.com/express/-/express-4.15.5.tgz",
126 | "integrity": "sha1-ZwI1ypWYiQpa6BcLg9tyK4Qu2Sc=",
127 | "requires": {
128 | "accepts": "1.3.4",
129 | "array-flatten": "1.1.1",
130 | "content-disposition": "0.5.2",
131 | "content-type": "1.0.4",
132 | "cookie": "0.3.1",
133 | "cookie-signature": "1.0.6",
134 | "debug": "2.6.9",
135 | "depd": "1.1.1",
136 | "encodeurl": "1.0.1",
137 | "escape-html": "1.0.3",
138 | "etag": "1.8.1",
139 | "finalhandler": "1.0.6",
140 | "fresh": "0.5.2",
141 | "merge-descriptors": "1.0.1",
142 | "methods": "1.1.2",
143 | "on-finished": "2.3.0",
144 | "parseurl": "1.3.2",
145 | "path-to-regexp": "0.1.7",
146 | "proxy-addr": "1.1.5",
147 | "qs": "6.5.0",
148 | "range-parser": "1.2.0",
149 | "send": "0.15.6",
150 | "serve-static": "1.12.6",
151 | "setprototypeof": "1.0.3",
152 | "statuses": "1.3.1",
153 | "type-is": "1.6.15",
154 | "utils-merge": "1.0.0",
155 | "vary": "1.1.2"
156 | },
157 | "dependencies": {
158 | "qs": {
159 | "version": "6.5.0",
160 | "resolved": "http://pnpm.baidu.com/qs/-/qs-6.5.0.tgz",
161 | "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg=="
162 | },
163 | "statuses": {
164 | "version": "1.3.1",
165 | "resolved": "http://pnpm.baidu.com/statuses/-/statuses-1.3.1.tgz",
166 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
167 | }
168 | }
169 | },
170 | "finalhandler": {
171 | "version": "1.0.6",
172 | "resolved": "http://pnpm.baidu.com/finalhandler/-/finalhandler-1.0.6.tgz",
173 | "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=",
174 | "requires": {
175 | "debug": "2.6.9",
176 | "encodeurl": "1.0.1",
177 | "escape-html": "1.0.3",
178 | "on-finished": "2.3.0",
179 | "parseurl": "1.3.2",
180 | "statuses": "1.3.1",
181 | "unpipe": "1.0.0"
182 | },
183 | "dependencies": {
184 | "statuses": {
185 | "version": "1.3.1",
186 | "resolved": "http://pnpm.baidu.com/statuses/-/statuses-1.3.1.tgz",
187 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
188 | }
189 | }
190 | },
191 | "forwarded": {
192 | "version": "0.1.2",
193 | "resolved": "http://pnpm.baidu.com/forwarded/-/forwarded-0.1.2.tgz",
194 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
195 | },
196 | "fresh": {
197 | "version": "0.5.2",
198 | "resolved": "http://pnpm.baidu.com/fresh/-/fresh-0.5.2.tgz",
199 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
200 | },
201 | "http-errors": {
202 | "version": "1.6.2",
203 | "resolved": "http://pnpm.baidu.com/http-errors/-/http-errors-1.6.2.tgz",
204 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
205 | "requires": {
206 | "depd": "1.1.1",
207 | "inherits": "2.0.3",
208 | "setprototypeof": "1.0.3",
209 | "statuses": "1.4.0"
210 | }
211 | },
212 | "iconv-lite": {
213 | "version": "0.4.19",
214 | "resolved": "http://pnpm.baidu.com/iconv-lite/-/iconv-lite-0.4.19.tgz",
215 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
216 | },
217 | "inherits": {
218 | "version": "2.0.3",
219 | "resolved": "http://pnpm.baidu.com/inherits/-/inherits-2.0.3.tgz",
220 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
221 | },
222 | "ipaddr.js": {
223 | "version": "1.4.0",
224 | "resolved": "http://pnpm.baidu.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz",
225 | "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA="
226 | },
227 | "media-typer": {
228 | "version": "0.3.0",
229 | "resolved": "http://pnpm.baidu.com/media-typer/-/media-typer-0.3.0.tgz",
230 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
231 | },
232 | "merge-descriptors": {
233 | "version": "1.0.1",
234 | "resolved": "http://pnpm.baidu.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
235 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
236 | },
237 | "methods": {
238 | "version": "1.1.2",
239 | "resolved": "http://pnpm.baidu.com/methods/-/methods-1.1.2.tgz",
240 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
241 | },
242 | "mime": {
243 | "version": "1.3.4",
244 | "resolved": "http://pnpm.baidu.com/mime/-/mime-1.3.4.tgz",
245 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
246 | },
247 | "mime-db": {
248 | "version": "1.30.0",
249 | "resolved": "http://pnpm.baidu.com/mime-db/-/mime-db-1.30.0.tgz",
250 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
251 | },
252 | "mime-types": {
253 | "version": "2.1.17",
254 | "resolved": "http://pnpm.baidu.com/mime-types/-/mime-types-2.1.17.tgz",
255 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
256 | "requires": {
257 | "mime-db": "1.30.0"
258 | }
259 | },
260 | "morgan": {
261 | "version": "1.9.0",
262 | "resolved": "http://pnpm.baidu.com/morgan/-/morgan-1.9.0.tgz",
263 | "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
264 | "requires": {
265 | "basic-auth": "2.0.0",
266 | "debug": "2.6.9",
267 | "depd": "1.1.1",
268 | "on-finished": "2.3.0",
269 | "on-headers": "1.0.1"
270 | }
271 | },
272 | "ms": {
273 | "version": "2.0.0",
274 | "resolved": "http://pnpm.baidu.com/ms/-/ms-2.0.0.tgz",
275 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
276 | },
277 | "negotiator": {
278 | "version": "0.6.1",
279 | "resolved": "http://pnpm.baidu.com/negotiator/-/negotiator-0.6.1.tgz",
280 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
281 | },
282 | "on-finished": {
283 | "version": "2.3.0",
284 | "resolved": "http://pnpm.baidu.com/on-finished/-/on-finished-2.3.0.tgz",
285 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
286 | "requires": {
287 | "ee-first": "1.1.1"
288 | }
289 | },
290 | "on-headers": {
291 | "version": "1.0.1",
292 | "resolved": "http://pnpm.baidu.com/on-headers/-/on-headers-1.0.1.tgz",
293 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
294 | },
295 | "parseurl": {
296 | "version": "1.3.2",
297 | "resolved": "http://pnpm.baidu.com/parseurl/-/parseurl-1.3.2.tgz",
298 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
299 | },
300 | "path-to-regexp": {
301 | "version": "0.1.7",
302 | "resolved": "http://pnpm.baidu.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
303 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
304 | },
305 | "proxy-addr": {
306 | "version": "1.1.5",
307 | "resolved": "http://pnpm.baidu.com/proxy-addr/-/proxy-addr-1.1.5.tgz",
308 | "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=",
309 | "requires": {
310 | "forwarded": "0.1.2",
311 | "ipaddr.js": "1.4.0"
312 | }
313 | },
314 | "qs": {
315 | "version": "6.5.1",
316 | "resolved": "http://pnpm.baidu.com/qs/-/qs-6.5.1.tgz",
317 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
318 | },
319 | "range-parser": {
320 | "version": "1.2.0",
321 | "resolved": "http://pnpm.baidu.com/range-parser/-/range-parser-1.2.0.tgz",
322 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
323 | },
324 | "raw-body": {
325 | "version": "2.3.2",
326 | "resolved": "http://pnpm.baidu.com/raw-body/-/raw-body-2.3.2.tgz",
327 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
328 | "requires": {
329 | "bytes": "3.0.0",
330 | "http-errors": "1.6.2",
331 | "iconv-lite": "0.4.19",
332 | "unpipe": "1.0.0"
333 | }
334 | },
335 | "safe-buffer": {
336 | "version": "5.1.1",
337 | "resolved": "http://pnpm.baidu.com/safe-buffer/-/safe-buffer-5.1.1.tgz",
338 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
339 | },
340 | "send": {
341 | "version": "0.15.6",
342 | "resolved": "http://pnpm.baidu.com/send/-/send-0.15.6.tgz",
343 | "integrity": "sha1-IPI6nJJbdiq4JwX+L52yUqzkfjQ=",
344 | "requires": {
345 | "debug": "2.6.9",
346 | "depd": "1.1.1",
347 | "destroy": "1.0.4",
348 | "encodeurl": "1.0.1",
349 | "escape-html": "1.0.3",
350 | "etag": "1.8.1",
351 | "fresh": "0.5.2",
352 | "http-errors": "1.6.2",
353 | "mime": "1.3.4",
354 | "ms": "2.0.0",
355 | "on-finished": "2.3.0",
356 | "range-parser": "1.2.0",
357 | "statuses": "1.3.1"
358 | },
359 | "dependencies": {
360 | "statuses": {
361 | "version": "1.3.1",
362 | "resolved": "http://pnpm.baidu.com/statuses/-/statuses-1.3.1.tgz",
363 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
364 | }
365 | }
366 | },
367 | "serve-favicon": {
368 | "version": "2.4.5",
369 | "resolved": "http://pnpm.baidu.com/serve-favicon/-/serve-favicon-2.4.5.tgz",
370 | "integrity": "sha512-s7F8h2NrslMkG50KxvlGdj+ApSwaLex0vexuJ9iFf3GLTIp1ph/l1qZvRe9T9TJEYZgmq72ZwJ2VYiAEtChknw==",
371 | "requires": {
372 | "etag": "1.8.1",
373 | "fresh": "0.5.2",
374 | "ms": "2.0.0",
375 | "parseurl": "1.3.2",
376 | "safe-buffer": "5.1.1"
377 | }
378 | },
379 | "serve-static": {
380 | "version": "1.12.6",
381 | "resolved": "http://pnpm.baidu.com/serve-static/-/serve-static-1.12.6.tgz",
382 | "integrity": "sha1-uXN3P2NEmTTaVOW+ul4x2fQhFXc=",
383 | "requires": {
384 | "encodeurl": "1.0.1",
385 | "escape-html": "1.0.3",
386 | "parseurl": "1.3.2",
387 | "send": "0.15.6"
388 | }
389 | },
390 | "setprototypeof": {
391 | "version": "1.0.3",
392 | "resolved": "http://pnpm.baidu.com/setprototypeof/-/setprototypeof-1.0.3.tgz",
393 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
394 | },
395 | "statuses": {
396 | "version": "1.4.0",
397 | "resolved": "http://pnpm.baidu.com/statuses/-/statuses-1.4.0.tgz",
398 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
399 | },
400 | "type-is": {
401 | "version": "1.6.15",
402 | "resolved": "http://pnpm.baidu.com/type-is/-/type-is-1.6.15.tgz",
403 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
404 | "requires": {
405 | "media-typer": "0.3.0",
406 | "mime-types": "2.1.17"
407 | }
408 | },
409 | "unpipe": {
410 | "version": "1.0.0",
411 | "resolved": "http://pnpm.baidu.com/unpipe/-/unpipe-1.0.0.tgz",
412 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
413 | },
414 | "utils-merge": {
415 | "version": "1.0.0",
416 | "resolved": "http://pnpm.baidu.com/utils-merge/-/utils-merge-1.0.0.tgz",
417 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
418 | },
419 | "vary": {
420 | "version": "1.1.2",
421 | "resolved": "http://pnpm.baidu.com/vary/-/vary-1.1.2.tgz",
422 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
423 | }
424 | }
425 | }
426 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "body-parser": "~1.18.2",
10 | "cookie-parser": "~1.4.3",
11 | "debug": "~2.6.9",
12 | "ejs": "~2.5.7",
13 | "express": "~4.15.5",
14 | "morgan": "~1.9.0",
15 | "serve-favicon": "~2.4.5"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/public/images/demos/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/1.png
--------------------------------------------------------------------------------
/public/images/demos/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/10.png
--------------------------------------------------------------------------------
/public/images/demos/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/11.png
--------------------------------------------------------------------------------
/public/images/demos/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/12.png
--------------------------------------------------------------------------------
/public/images/demos/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/13.png
--------------------------------------------------------------------------------
/public/images/demos/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/2.png
--------------------------------------------------------------------------------
/public/images/demos/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/3.png
--------------------------------------------------------------------------------
/public/images/demos/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/4.png
--------------------------------------------------------------------------------
/public/images/demos/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/5.png
--------------------------------------------------------------------------------
/public/images/demos/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/6.png
--------------------------------------------------------------------------------
/public/images/demos/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/7.png
--------------------------------------------------------------------------------
/public/images/demos/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/8.png
--------------------------------------------------------------------------------
/public/images/demos/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/demos/9.png
--------------------------------------------------------------------------------
/public/images/jump_screencap/screencap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ksky521/wechat-jump-game-hack/ffe340aa834e964f4556fd503529e94e836e295e/public/images/jump_screencap/screencap.png
--------------------------------------------------------------------------------
/public/javascripts/demo.js:
--------------------------------------------------------------------------------
1 | function getPointHtml(top, left) {
2 | return `
`;
3 | }
4 | let html = '';
5 | for (let i = 1; i <= 13; i++) {
6 | let url = `/images/demos/${i}.png`;
7 | html += `

`;
8 | getPosition(url).then(({pos1, pos2, width, height}) => {
9 | let scale = 720 / width;
10 | pos1[0] = pos1[0] * scale;
11 | pos1[1] = pos1[1] * scale;
12 | pos2[0] = pos2[0] * scale;
13 | pos2[1] = pos2[1] * scale;
14 | let {top, left} = $(`#img${i}`).position();
15 | let $curPoint = $(getPointHtml(top + pos1[1] - 5, left + pos1[0] - 5));
16 | $curPoint.appendTo($('body'));
17 |
18 | let $nextPoint = $(getPointHtml(top + pos2[1] - 5, left + pos2[0] - 5));
19 | $nextPoint.appendTo($('body'));
20 | });
21 | }
22 | $('#list').html(html);
23 |
--------------------------------------------------------------------------------
/public/javascripts/index.js:
--------------------------------------------------------------------------------
1 | function getScreenCap() {
2 | return new Promise((resolve, reject) => {
3 | $.ajax({
4 | url: '/jumponejump/getscreencap',
5 | type: 'POST',
6 | dataType: 'json',
7 | data: {},
8 | success: function(result) {
9 | if (result.error) {
10 | reject(result.error);
11 | } else {
12 | console.log('获取图片成功');
13 | resolve(`/images/jump_screencap/screencap.png?_t=${Date.now()}`);
14 | }
15 | }
16 | });
17 | });
18 | }
19 | function jump({pos1, pos2, width}) {
20 | return new Promise((resolve, reject) => {
21 | if (pos1[0] > 10 && pos2[0] > 10 && pos1[1] > 10 && pos2[1] > 10 && pos1[0] < 3e4 && pos2[1] < 3e4) {
22 | } else {
23 | reject(pos1, pos2, width);
24 | }
25 | // 720修正
26 | let scale = 720 / width;
27 | pos1[0] = pos1[0] * scale;
28 | pos1[1] = pos1[1] * scale;
29 | pos2[0] = pos2[0] * scale;
30 | pos2[1] = pos2[1] * scale;
31 |
32 | const distance = Math.sqrt(Math.pow(pos1[0] - pos2[0], 2) + Math.pow(pos1[1] - pos2[1], 2));
33 | $.ajax({
34 | url: '/jumponejump/',
35 | type: 'POST',
36 | dataType: 'json',
37 | data: {
38 | distance: distance
39 | },
40 | success: function(result) {
41 | if (result.error) {
42 | reject(result.error);
43 | } else {
44 | console.log('跳转成功');
45 | resolve();
46 | }
47 | }
48 | });
49 | });
50 | }
51 | function start() {
52 | getScreenCap()
53 | .then(getPosition)
54 | .then(jump)
55 | .then(function() {
56 | setTimeout(start, 1e3);
57 | })
58 | .catch((e) => {
59 | console.error(e);
60 | if (e.code === 1) {
61 | //发现存在图片获取失败的问题,增加reload页面
62 | location.reload();
63 | }
64 | });
65 | }
66 |
67 | start();
68 |
--------------------------------------------------------------------------------
/public/javascripts/jump-test.js:
--------------------------------------------------------------------------------
1 | function getImageData(src) {
2 | const img = new Image();
3 | return new Promise((resolve, reject) => {
4 | img.onload = () => {
5 | img.onerror = img.onload = null;
6 | let canvas = document.createElement('canvas');
7 | let width = (canvas.width = img.width);
8 | let height = (canvas.height = img.height);
9 | let ctx = canvas.getContext('2d');
10 | ctx.drawImage(img, 0, 0);
11 | let imgData = ctx.getImageData(0, 0, width, height);
12 | resolve(imgData);
13 | };
14 | img.onerror = (e) => {
15 | img.onerror = img.onload = null;
16 | e.message = '图片获取失败';
17 | e.code = 1;
18 | reject(e);
19 | };
20 | img.src = src;
21 | });
22 | }
23 | function tolerenceHelper(r, g, b, rt, gt, bt, t) {
24 | return r > rt - t && r < rt + t && g > gt - t && g < gt + t && b > bt - t && b < bt + t;
25 | }
26 |
27 | function getCurCenter(data, width, height) {
28 | // 小人的颜色值
29 | const playerR = 40;
30 | const playerG = 43;
31 | const playerB = 86;
32 |
33 | let minX = Infinity;
34 | let minY = Infinity;
35 | let maxX = -1;
36 | let maxY = -1;
37 | // 找到小人当前的底部位置
38 | let pos = [0, 0];
39 |
40 | let startY = Math.floor(height / 4);
41 | let endY = Math.floor(height * 3 / 4);
42 | for (let x = 0; x < width; x++) {
43 | for (let y = startY; y < endY; y++) {
44 | let i = y * (width * 4) + x * 4;
45 | let r = data[i];
46 | let g = data[i + 1];
47 | let b = data[i + 2];
48 | if (y > pos[1] && tolerenceHelper(r, g, b, playerR, playerG, playerB, 16)) {
49 | // 将匹配到的像素点设置为红色
50 | data[i] = 255;
51 | data[i + 1] = 0;
52 | data[i + 2] = 0;
53 | minX = Math.min(minX, x);
54 | maxX = Math.max(maxX, x);
55 | maxY = Math.max(maxY, y);
56 | minY = Math.min(minY, y);
57 | }
58 | }
59 | }
60 | pos[0] = Math.floor((maxX + minX) / 2);
61 | pos[1] = maxY;
62 | pos[2] = [minX, maxX, minY, maxY];
63 | // console.log(`player position (x, y)= (${pos[0]}, ${pos[1]})`);
64 | return pos;
65 | }
66 | function getNextCenter(data, width, height, curPos) {
67 | let startY = Math.floor(height / 4);
68 | let endY = Math.floor(height * 3 / 4);
69 |
70 | // 去除背景色
71 | let startX = startY * width * 4;
72 | let r = data[startX],
73 | g = data[startX + 1],
74 | b = data[startX + 2];
75 |
76 | let maxY = -1;
77 | let apex = [];
78 | let pos = [0, 0];
79 | // 保证从当前小人位置底部点往上
80 | endY = Math.min(endY, curPos[1]);
81 | let endX = width;
82 | let gapCount = 0;
83 | for (let y = startY; y < endY; y++) {
84 | let find = 0;
85 | for (let x = 1; x < endX; x++) {
86 | // 解决小人比下一个台阶高的情况,需要遇见在小人范围内的值就跳出
87 | if (curPos[3] && curPos[3].length) {
88 | let [x1, x2, y1, y2] = curPos[3];
89 | if (x >= x1 && x <= x2 && y >= y1 && y <= y2) {
90 | continue;
91 | }
92 | }
93 |
94 | let i = y * (width * 4) + x * 4;
95 | let rt = data[i];
96 | let gt = data[i + 1];
97 | let bt = data[i + 2];
98 | // 不是默认背景颜色
99 | if (!tolerenceHelper(rt, gt, bt, r, g, b, 30)) {
100 | if (apex.length === 0) {
101 | if (!tolerenceHelper(data[i + 4], data[i + 5], data[i + 6], r, g, b, 30)) {
102 | //椭圆形找中心,往后找30个像素点
103 | let len = 2;
104 | while (len++ !== 30) {
105 | i += len * 4;
106 | if (tolerenceHelper(data[i + 4], data[i + 5], data[i + 6], r, g, b, 30)) {
107 | break;
108 | }
109 | }
110 | x += len;
111 | }
112 | //找出顶点
113 | apex = [rt, gt, bt, x, y];
114 | pos[0] = x;
115 | // 减少循环范围
116 | endX = x;
117 | break;
118 | } else if (tolerenceHelper(rt, gt, bt, apex[0], apex[1], apex[2], 5)) {
119 | // 物体顶面边涂红
120 | data[i] = 255;
121 | data[i + 1] = 0;
122 | data[i + 2] = 0;
123 |
124 | //存在顶点了,则根据颜色值开始匹配
125 | maxY = Math.max(maxY, y);
126 | find = x;
127 | break;
128 | }
129 | } else {
130 | // 背景色涂红
131 | // data[i] = 255;
132 | // data[i + 1] = 0;
133 | // data[i + 2] = 0;
134 | }
135 | }
136 | if (apex.length !== 0 && !find) {
137 | gapCount++;
138 | }
139 | if (gapCount === 3) {
140 | break;
141 | }
142 | }
143 | pos[1] = Math.floor((maxY + apex[4]) / 2);
144 | // console.log(points_top, points_left, points_right);
145 | // console.log(`next position center (x,y)=${pos[0]},${pos[1]}`);
146 | return pos;
147 | }
148 | async function getPosition(img) {
149 | let {data, width, height} = await getImageData(img);
150 | let pos1 = getCurCenter(data, width, height);
151 | let pos2 = getNextCenter(data, width, height, pos1);
152 | return {pos1, pos2, data, width, height};
153 | }
154 |
--------------------------------------------------------------------------------
/public/javascripts/jump.js:
--------------------------------------------------------------------------------
1 | function getImageData(src) {
2 | const img = new Image();
3 | return new Promise((resolve, reject) => {
4 | img.onload = () => {
5 | img.onerror = img.onload = null;
6 | let canvas = document.createElement('canvas');
7 | let width = (canvas.width = img.width);
8 | let height = (canvas.height = img.height);
9 | let ctx = canvas.getContext('2d');
10 | ctx.drawImage(img, 0, 0);
11 | let imgData = ctx.getImageData(0, 0, width, height);
12 | resolve(imgData);
13 | };
14 | img.onerror = (e) => {
15 | img.onerror = img.onload = null;
16 | e.message = '图片获取失败';
17 | e.code = 1;
18 | reject(e);
19 | };
20 | img.src = src;
21 | });
22 | }
23 | function tolerenceHelper(r, g, b, rt, gt, bt, t) {
24 | return r > rt - t && r < rt + t && g > gt - t && g < gt + t && b > bt - t && b < bt + t;
25 | }
26 |
27 | function getCurCenter(data, width, height) {
28 | // 小人的颜色值
29 | const playerR = 40;
30 | const playerG = 43;
31 | const playerB = 86;
32 |
33 | let minX = Infinity;
34 | let minY = Infinity;
35 | let maxX = -1;
36 | let maxY = -1;
37 | // 找到小人当前的底部位置
38 | let pos = [0, 0];
39 |
40 | let startY = Math.floor(height / 4);
41 | let endY = Math.floor(height * 3 / 4);
42 | for (let x = 0; x < width; x++) {
43 | for (let y = startY; y < endY; y++) {
44 | let i = y * (width * 4) + x * 4;
45 | let r = data[i];
46 | let g = data[i + 1];
47 | let b = data[i + 2];
48 | if (y > pos[1] && tolerenceHelper(r, g, b, playerR, playerG, playerB, 16)) {
49 | minX = Math.min(minX, x);
50 | maxX = Math.max(maxX, x);
51 | maxY = Math.max(maxY, y);
52 | minY = Math.min(minY, y);
53 | }
54 | }
55 | }
56 | pos[0] = Math.floor((maxX + minX) / 2);
57 | pos[1] = maxY;
58 | pos[2] = [minX, maxX, minY, maxY];
59 |
60 | console.log(`player position (x, y)= (${pos[0]}, ${pos[1]})`);
61 | return pos;
62 | }
63 | function getNextCenter(data, width, height, curPos) {
64 | let startY = Math.floor(height / 4);
65 | let endY = Math.floor(height * 3 / 4);
66 |
67 | // 去除背景色
68 | let startX = startY * width * 4;
69 | let r = data[startX],
70 | g = data[startX + 1],
71 | b = data[startX + 2];
72 | let maxY = -1;
73 | let apex = [];
74 | let pos = [0, 0];
75 | // 保证从当前小人位置底部点往上
76 | endY = Math.min(endY, curPos[1]);
77 | let endX = width;
78 | let gapCount = 0;
79 | for (let y = startY; y < endY; y++) {
80 | let find = 0;
81 | for (let x = 1; x < endX; x++) {
82 | // 解决小人比下一个台阶高的情况,需要遇见在小人范围内的值就跳出
83 | if (curPos[3] && curPos[3].length) {
84 | let [x1, x2, y1, y2] = curPos[3];
85 | if (x >= x1 && x <= x2 && y >= y1 && y <= y2) {
86 | continue;
87 | }
88 | }
89 | let i = y * (width * 4) + x * 4;
90 | let rt = data[i];
91 | let gt = data[i + 1];
92 | let bt = data[i + 2];
93 | // 不是默认背景颜色
94 | if (!tolerenceHelper(rt, gt, bt, r, g, b, 30)) {
95 | if (apex.length === 0) {
96 | if (!tolerenceHelper(data[i + 4], data[i + 5], data[i + 6], r, g, b, 30)) {
97 | //椭圆形找中心,往后找30个像素点
98 | let len = 2;
99 | while (len++ !== 30) {
100 | i += len * 4;
101 | if (tolerenceHelper(data[i + 4], data[i + 5], data[i + 6], r, g, b, 30)) {
102 | break;
103 | }
104 | }
105 | x += len;
106 | }
107 | //找出顶点
108 | apex = [rt, gt, bt, x, y];
109 | pos[0] = x;
110 | // 减少循环范围
111 | endX = x;
112 | break;
113 | } else if (tolerenceHelper(rt, gt, bt, apex[0], apex[1], apex[2], 5)) {
114 | //存在顶点了,则根据颜色值开始匹配
115 | maxY = Math.max(maxY, y);
116 | find = x;
117 | break;
118 | }
119 | }
120 | }
121 | if (apex.length !== 0 && !find) {
122 | gapCount++;
123 | }
124 | if (gapCount === 3) {
125 | break;
126 | }
127 | }
128 | pos[1] = Math.floor((maxY + apex[4]) / 2);
129 | // console.log(points_top, points_left, points_right);
130 | console.log(`next position center (x,y)=${pos[0]},${pos[1]}`);
131 | return pos;
132 | }
133 | async function getPosition(img) {
134 | let {data, width, height} = await getImageData(img);
135 | let pos1 = getCurCenter(data, width, height);
136 | let pos2 = getNextCenter(data, width, height, pos1);
137 | return {pos1, pos2, data, width, height};
138 | }
139 |
--------------------------------------------------------------------------------
/public/javascripts/jump2.js:
--------------------------------------------------------------------------------
1 | function getImageData(src) {
2 | const img = new Image();
3 | return new Promise((resolve, reject) => {
4 | img.onload = () => {
5 | img.onerror = img.onload = null;
6 | let canvas = document.createElement('canvas');
7 | // 720 * 1280
8 | let width = img.width;
9 | let height = img.height;
10 | let scale = width / 720;
11 | height = Math.floor(height / scale);
12 | width = canvas.width = 720;
13 | canvas.height = height;
14 | let ctx = canvas.getContext('2d');
15 | ctx.drawImage(img, 0, 0, width, height);
16 | let imgData = ctx.getImageData(0, 0, width, height);
17 | resolve(imgData);
18 | };
19 | img.onerror = (e) => {
20 | img.onerror = img.onload = null;
21 | reject(e);
22 | };
23 | img.src = src;
24 | });
25 | }
26 | function tolerenceHelper(r, g, b, rt, gt, bt, t) {
27 | return r > rt - t && r < rt + t && g > gt - t && g < gt + t && b > bt - t && b < bt + t;
28 | }
29 |
30 | function getCurCenter(data, width, height) {
31 | // 小人的颜色值
32 | const playerR = 40;
33 | const playerG = 43;
34 | const playerB = 86;
35 |
36 | let minX = Infinity;
37 | let maxX = -1;
38 | let maxY = -1;
39 | // 找到小人当前的底部位置
40 | let pos = [0, 0];
41 |
42 | let startY = Math.floor(height / 4);
43 | let endY = Math.floor(height * 3 / 4);
44 | for (let x = 0; x < width; x++) {
45 | for (let y = startY; y < endY; y++) {
46 | let i = y * (width * 4) + x * 4;
47 | let r = data[i];
48 | let g = data[i + 1];
49 | let b = data[i + 2];
50 | if (y > pos[1] && tolerenceHelper(r, g, b, playerR, playerG, playerB, 16)) {
51 | minX = Math.min(minX, x);
52 | maxX = Math.max(maxX, x);
53 | maxY = Math.max(maxY, y);
54 | }
55 | }
56 | }
57 | pos[0] = Math.floor((maxX + minX) / 2);
58 | pos[1] = maxY;
59 | // console.log(`player position (x, y)= (${pos[0]}, ${pos[1]})`);
60 | return pos;
61 | }
62 | function getGray(r, g, b) {
63 | return Math.floor((r + g + b) / 3);
64 | }
65 | function grayHelper(a, b, t) {
66 | if (a > b - t && a < b + t) {
67 | return true;
68 | }
69 | return false;
70 | }
71 | function getNextCenter(data, width, height) {
72 | let startY = Math.floor(height / 4);
73 | let endY = Math.floor(height * 3 / 4);
74 | let startX = startY * width * 4;
75 |
76 | // 边缘检测
77 | for (let i = startX, len = endY * width * 4; i < len; i += 4) {
78 | let r = data[i];
79 | let g = data[i + 1];
80 | let b = data[i + 2];
81 |
82 | if (!tolerenceHelper(r, g, b, data[i + 8], data[i + 9], data[i + 10], 5)) {
83 | data[i] = 255;
84 | data[i + 1] = 0;
85 | data[i + 2] = 0;
86 | }
87 | }
88 |
89 | // 去除背景色
90 | let r = data[startX],
91 | g = data[startX + 1],
92 | b = data[startX + 2];
93 |
94 | let maxY = -1;
95 | let apex = [];
96 | let pos = [0, 0];
97 | // 保证从当前小人位置底部点往上
98 | let endX = width;
99 | let gapCount = 0;
100 |
101 | for (let y = startY; y < endY; y++) {
102 | let find = 0;
103 | for (let x = 1; x < endX; x++) {
104 | let i = y * (width * 4) + x * 4;
105 | let rt = data[i];
106 | let gt = data[i + 1];
107 | let bt = data[i + 2];
108 | // 不是默认背景颜色
109 | if (!tolerenceHelper(rt, gt, bt, r, g, b, 30) && !tolerenceHelper(rt, gt, bt, 255, 0, 0, 10)) {
110 | if (apex.length === 0) {
111 | //找出顶点
112 | apex = [rt, gt, bt, x, y];
113 | pos[0] = x;
114 | // 减少循环范围
115 | endX = x;
116 | maxY = y + 60;
117 | // y+60行,加快循环,找不到中点也保证不会出去
118 | y += 60;
119 | break;
120 | } else if (tolerenceHelper(rt, gt, bt, apex[0], apex[1], apex[2], 10)) {
121 | //存在顶点了,则根据顶点颜色值开始匹配
122 | maxY = Math.max(maxY, y);
123 | find = x;
124 | break;
125 | // console.log(rt, gt, bt, apex.join(','));
126 | }
127 | }
128 | }
129 | if (apex.length !== 0 && !find) {
130 | // console.log(find, maxY);
131 | gapCount++;
132 | }
133 | if (gapCount === 3) {
134 | //↑次找不到就跳出
135 | break;
136 | }
137 | }
138 |
139 | pos[1] = Math.floor((maxY + apex[4]) / 2);
140 | // console.log(`next position center (x,y)=${pos[0]},${pos[1]}, ${maxY},${apex[4]}`);
141 | return pos;
142 | }
143 | async function getPosition(img) {
144 | let {data, width, height} = await getImageData(img);
145 | let pos1 = getCurCenter(data, width, height);
146 | let pos2 = getNextCenter(data, width, height);
147 | return {pos1, pos2, data, width, height};
148 | }
149 |
--------------------------------------------------------------------------------
/public/javascripts/test.js:
--------------------------------------------------------------------------------
1 | const testUrl = '/images/demos/1.png';
2 | const canvas = document.createElement('canvas');
3 |
4 | getPosition(testUrl).then(({pos1, pos2, data, width, height}) => {
5 | canvas.width = width;
6 | canvas.height = height;
7 | const ctx = canvas.getContext('2d');
8 | const imgData = ctx.createImageData(width, height);
9 |
10 | const start = pos1[1] * width * 4 + pos1[0] * 4;
11 | const end = pos2[1] * width * 4 + pos2[0] * 4;
12 | for (let i = 0; i < data.length; i += 4) {
13 | if (near(i, start, end, 16)) {
14 | imgData.data[i] = 255;
15 | imgData.data[i + 1] = 0;
16 | imgData.data[i + 2] = 0;
17 | } else {
18 | imgData.data[i] = data[i];
19 | imgData.data[i + 1] = data[i + 1];
20 | imgData.data[i + 2] = data[i + 2];
21 | }
22 | imgData.data[i + 3] = 255;
23 | }
24 |
25 | ctx.putImageData(imgData, 0, 0);
26 | $('#canvas').append(canvas)
27 | });
28 |
29 | function near(i, start, end, t) {
30 | if ((i > start - t && i < start + t) || (i > end - t && i < end + t)) {
31 | return true;
32 | }
33 | return false;
34 | }
35 |
--------------------------------------------------------------------------------
/public/javascripts/zepto.min.js:
--------------------------------------------------------------------------------
1 | /* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */
2 | !function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t)}):e(t)}(this,function(t){var e=function(){function $(t){return null==t?String(t):S[C.call(t)]||"object"}function F(t){return"function"==$(t)}function k(t){return null!=t&&t==t.window}function M(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function R(t){return"object"==$(t)}function Z(t){return R(t)&&!k(t)&&Object.getPrototypeOf(t)==Object.prototype}function z(t){var e=!!t&&"length"in t&&t.length,n=r.type(t);return"function"!=n&&!k(t)&&("array"==n||0===e||"number"==typeof e&&e>0&&e-1 in t)}function q(t){return a.call(t,function(t){return null!=t})}function H(t){return t.length>0?r.fn.concat.apply([],t):t}function I(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function V(t){return t in l?l[t]:l[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function _(t,e){return"number"!=typeof e||h[I(t)]?e:e+"px"}function B(t){var e,n;return c[t]||(e=f.createElement(t),f.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),c[t]=n),c[t]}function U(t){return"children"in t?u.call(t.children):r.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function X(t,e){var n,r=t?t.length:0;for(n=0;r>n;n++)this[n]=t[n];this.length=r,this.selector=e||""}function J(t,r,i){for(n in r)i&&(Z(r[n])||L(r[n]))?(Z(r[n])&&!Z(t[n])&&(t[n]={}),L(r[n])&&!L(t[n])&&(t[n]=[]),J(t[n],r[n],i)):r[n]!==e&&(t[n]=r[n])}function W(t,e){return null==e?r(t):r(t).filter(e)}function Y(t,e,n,r){return F(e)?e.call(t,n,r):e}function G(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function K(t,n){var r=t.className||"",i=r&&r.baseVal!==e;return n===e?i?r.baseVal:r:void(i?r.baseVal=n:t.className=n)}function Q(t){try{return t?"true"==t||("false"==t?!1:"null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?r.parseJSON(t):t):t}catch(e){return t}}function tt(t,e){e(t);for(var n=0,r=t.childNodes.length;r>n;n++)tt(t.childNodes[n],e)}var e,n,r,i,O,P,o=[],s=o.concat,a=o.filter,u=o.slice,f=t.document,c={},l={},h={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},p=/^\s*<(\w+|!)[^>]*>/,d=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,m=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,g=/^(?:body|html)$/i,v=/([A-Z])/g,y=["val","css","html","text","data","width","height","offset"],x=["after","prepend","before","append"],b=f.createElement("table"),E=f.createElement("tr"),j={tr:f.createElement("tbody"),tbody:b,thead:b,tfoot:b,td:E,th:E,"*":f.createElement("div")},w=/complete|loaded|interactive/,T=/^[\w-]*$/,S={},C=S.toString,N={},A=f.createElement("div"),D={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},L=Array.isArray||function(t){return t instanceof Array};return N.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=A).appendChild(t),r=~N.qsa(i,e).indexOf(t),o&&A.removeChild(t),r},O=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},P=function(t){return a.call(t,function(e,n){return t.indexOf(e)==n})},N.fragment=function(t,n,i){var o,s,a;return d.test(t)&&(o=r(f.createElement(RegExp.$1))),o||(t.replace&&(t=t.replace(m,"<$1>$2>")),n===e&&(n=p.test(t)&&RegExp.$1),n in j||(n="*"),a=j[n],a.innerHTML=""+t,o=r.each(u.call(a.childNodes),function(){a.removeChild(this)})),Z(i)&&(s=r(o),r.each(i,function(t,e){y.indexOf(t)>-1?s[t](e):s.attr(t,e)})),o},N.Z=function(t,e){return new X(t,e)},N.isZ=function(t){return t instanceof N.Z},N.init=function(t,n){var i;if(!t)return N.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&p.test(t))i=N.fragment(t,RegExp.$1,n),t=null;else{if(n!==e)return r(n).find(t);i=N.qsa(f,t)}else{if(F(t))return r(f).ready(t);if(N.isZ(t))return t;if(L(t))i=q(t);else if(R(t))i=[t],t=null;else if(p.test(t))i=N.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==e)return r(n).find(t);i=N.qsa(f,t)}}return N.Z(i,t)},r=function(t,e){return N.init(t,e)},r.extend=function(t){var e,n=u.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){J(t,n,e)}),t},N.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,s=T.test(o);return t.getElementById&&s&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:u.call(s&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},r.contains=f.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},r.type=$,r.isFunction=F,r.isWindow=k,r.isArray=L,r.isPlainObject=Z,r.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},r.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},r.inArray=function(t,e,n){return o.indexOf.call(e,t,n)},r.camelCase=O,r.trim=function(t){return null==t?"":String.prototype.trim.call(t)},r.uuid=0,r.support={},r.expr={},r.noop=function(){},r.map=function(t,e){var n,i,o,r=[];if(z(t))for(i=0;i
=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return o.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return F(t)?this.not(this.not(t)):r(a.call(this,function(e){return N.matches(e,t)}))},add:function(t,e){return r(P(this.concat(r(t,e))))},is:function(t){return this.length>0&&N.matches(this[0],t)},not:function(t){var n=[];if(F(t)&&t.call!==e)this.each(function(e){t.call(this,e)||n.push(this)});else{var i="string"==typeof t?this.filter(t):z(t)&&F(t.item)?u.call(t):r(t);this.forEach(function(t){i.indexOf(t)<0&&n.push(t)})}return r(n)},has:function(t){return this.filter(function(){return R(t)?r.contains(this,t):r(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!R(t)?t:r(t)},last:function(){var t=this[this.length-1];return t&&!R(t)?t:r(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?r(t).filter(function(){var t=this;return o.some.call(n,function(e){return r.contains(e,t)})}):1==this.length?r(N.qsa(this[0],t)):this.map(function(){return N.qsa(this,t)}):r()},closest:function(t,e){var n=[],i="object"==typeof t&&r(t);return this.each(function(r,o){for(;o&&!(i?i.indexOf(o)>=0:N.matches(o,t));)o=o!==e&&!M(o)&&o.parentNode;o&&n.indexOf(o)<0&&n.push(o)}),r(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=r.map(n,function(t){return(t=t.parentNode)&&!M(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return W(e,t)},parent:function(t){return W(P(this.pluck("parentNode")),t)},children:function(t){return W(this.map(function(){return U(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||u.call(this.childNodes)})},siblings:function(t){return W(this.map(function(t,e){return a.call(U(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return r.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=B(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=F(t);if(this[0]&&!e)var n=r(t).get(0),i=n.parentNode||this.length>1;return this.each(function(o){r(this).wrapAll(e?t.call(this,o):i?n.cloneNode(!0):n)})},wrapAll:function(t){if(this[0]){r(this[0]).before(t=r(t));for(var e;(e=t.children()).length;)t=e.first();r(t).append(this)}return this},wrapInner:function(t){var e=F(t);return this.each(function(n){var i=r(this),o=i.contents(),s=e?t.call(this,n):t;o.length?o.wrapAll(s):i.append(s)})},unwrap:function(){return this.parent().each(function(){r(this).replaceWith(r(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var n=r(this);(t===e?"none"==n.css("display"):t)?n.show():n.hide()})},prev:function(t){return r(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return r(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;r(this).empty().append(Y(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=Y(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,r){var i;return"string"!=typeof t||1 in arguments?this.each(function(e){if(1===this.nodeType)if(R(t))for(n in t)G(this,n,t[n]);else G(this,t,Y(this,r,e,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(i=this[0].getAttribute(t))?i:e},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){G(this,t)},this)})},prop:function(t,e){return t=D[t]||t,1 in arguments?this.each(function(n){this[t]=Y(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=D[t]||t,this.each(function(){delete this[t]})},data:function(t,n){var r="data-"+t.replace(v,"-$1").toLowerCase(),i=1 in arguments?this.attr(r,n):this.attr(r);return null!==i?Q(i):e},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=Y(this,t,e,this.value)})):this[0]&&(this[0].multiple?r(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(e){if(e)return this.each(function(t){var n=r(this),i=Y(this,e,t,n.offset()),o=n.offsetParent().offset(),s={top:i.top-o.top,left:i.left-o.left};"static"==n.css("position")&&(s.position="relative"),n.css(s)});if(!this.length)return null;if(f.documentElement!==this[0]&&!r.contains(f.documentElement,this[0]))return{top:0,left:0};var n=this[0].getBoundingClientRect();return{left:n.left+t.pageXOffset,top:n.top+t.pageYOffset,width:Math.round(n.width),height:Math.round(n.height)}},css:function(t,e){if(arguments.length<2){var i=this[0];if("string"==typeof t){if(!i)return;return i.style[O(t)]||getComputedStyle(i,"").getPropertyValue(t)}if(L(t)){if(!i)return;var o={},s=getComputedStyle(i,"");return r.each(t,function(t,e){o[e]=i.style[O(e)]||s.getPropertyValue(e)}),o}}var a="";if("string"==$(t))e||0===e?a=I(t)+":"+_(t,e):this.each(function(){this.style.removeProperty(I(t))});else for(n in t)t[n]||0===t[n]?a+=I(n)+":"+_(n,t[n])+";":this.each(function(){this.style.removeProperty(I(n))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(r(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?o.some.call(this,function(t){return this.test(K(t))},V(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){i=[];var n=K(this),o=Y(this,t,e,n);o.split(/\s+/g).forEach(function(t){r(this).hasClass(t)||i.push(t)},this),i.length&&K(this,n+(n?" ":"")+i.join(" "))}}):this},removeClass:function(t){return this.each(function(n){if("className"in this){if(t===e)return K(this,"");i=K(this),Y(this,t,n,i).split(/\s+/g).forEach(function(t){i=i.replace(V(t)," ")}),K(this,i.trim())}})},toggleClass:function(t,n){return t?this.each(function(i){var o=r(this),s=Y(this,t,i,K(this));s.split(/\s+/g).forEach(function(t){(n===e?!o.hasClass(t):n)?o.addClass(t):o.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var n="scrollTop"in this[0];return t===e?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var n="scrollLeft"in this[0];return t===e?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),i=g.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(r(t).css("margin-top"))||0,n.left-=parseFloat(r(t).css("margin-left"))||0,i.top+=parseFloat(r(e[0]).css("border-top-width"))||0,i.left+=parseFloat(r(e[0]).css("border-left-width"))||0,{top:n.top-i.top,left:n.left-i.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||f.body;t&&!g.test(t.nodeName)&&"static"==r(t).css("position");)t=t.offsetParent;return t})}},r.fn.detach=r.fn.remove,["width","height"].forEach(function(t){var n=t.replace(/./,function(t){return t[0].toUpperCase()});r.fn[t]=function(i){var o,s=this[0];return i===e?k(s)?s["inner"+n]:M(s)?s.documentElement["scroll"+n]:(o=this.offset())&&o[t]:this.each(function(e){s=r(this),s.css(t,Y(this,i,e,s[t]()))})}}),x.forEach(function(n,i){var o=i%2;r.fn[n]=function(){var n,a,s=r.map(arguments,function(t){var i=[];return n=$(t),"array"==n?(t.forEach(function(t){return t.nodeType!==e?i.push(t):r.zepto.isZ(t)?i=i.concat(t.get()):void(i=i.concat(N.fragment(t)))}),i):"object"==n||null==t?t:N.fragment(t)}),u=this.length>1;return s.length<1?this:this.each(function(e,n){a=o?n:n.parentNode,n=0==i?n.nextSibling:1==i?n.firstChild:2==i?n:null;var c=r.contains(f.documentElement,a);s.forEach(function(e){if(u)e=e.cloneNode(!0);else if(!a)return r(e).remove();a.insertBefore(e,n),c&&tt(e,function(e){if(!(null==e.nodeName||"SCRIPT"!==e.nodeName.toUpperCase()||e.type&&"text/javascript"!==e.type||e.src)){var n=e.ownerDocument?e.ownerDocument.defaultView:t;n.eval.call(n,e.innerHTML)}})})})},r.fn[o?n+"To":"insert"+(i?"Before":"After")]=function(t){return r(t)[n](this),this}}),N.Z.prototype=X.prototype=r.fn,N.uniq=P,N.deserializeValue=Q,r.zepto=N,r}();return t.Zepto=e,void 0===t.$&&(t.$=e),function(e){function h(t){return t._zid||(t._zid=n++)}function p(t,e,n,r){if(e=d(e),e.ns)var i=m(e.ns);return(a[h(t)]||[]).filter(function(t){return t&&(!e.e||t.e==e.e)&&(!e.ns||i.test(t.ns))&&(!n||h(t.fn)===h(n))&&(!r||t.sel==r)})}function d(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function m(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function g(t,e){return t.del&&!f&&t.e in c||!!e}function v(t){return l[t]||f&&c[t]||t}function y(t,n,i,o,s,u,f){var c=h(t),p=a[c]||(a[c]=[]);n.split(/\s/).forEach(function(n){if("ready"==n)return e(document).ready(i);var a=d(n);a.fn=i,a.sel=s,a.e in l&&(i=function(t){var n=t.relatedTarget;return!n||n!==this&&!e.contains(this,n)?a.fn.apply(this,arguments):void 0}),a.del=u;var c=u||i;a.proxy=function(e){if(e=T(e),!e.isImmediatePropagationStopped()){e.data=o;var n=c.apply(t,e._args==r?[e]:[e].concat(e._args));return n===!1&&(e.preventDefault(),e.stopPropagation()),n}},a.i=p.length,p.push(a),"addEventListener"in t&&t.addEventListener(v(a.e),a.proxy,g(a,f))})}function x(t,e,n,r,i){var o=h(t);(e||"").split(/\s/).forEach(function(e){p(t,e,n,r).forEach(function(e){delete a[o][e.i],"removeEventListener"in t&&t.removeEventListener(v(e.e),e.proxy,g(e,i))})})}function T(t,n){return(n||!t.isDefaultPrevented)&&(n||(n=t),e.each(w,function(e,r){var i=n[e];t[e]=function(){return this[r]=b,i&&i.apply(n,arguments)},t[r]=E}),t.timeStamp||(t.timeStamp=Date.now()),(n.defaultPrevented!==r?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(t.isDefaultPrevented=b)),t}function S(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===r||(n[e]=t[e]);return T(n,t)}var r,n=1,i=Array.prototype.slice,o=e.isFunction,s=function(t){return"string"==typeof t},a={},u={},f="onfocusin"in t,c={focus:"focusin",blur:"focusout"},l={mouseenter:"mouseover",mouseleave:"mouseout"};u.click=u.mousedown=u.mouseup=u.mousemove="MouseEvents",e.event={add:y,remove:x},e.proxy=function(t,n){var r=2 in arguments&&i.call(arguments,2);if(o(t)){var a=function(){return t.apply(n,r?r.concat(i.call(arguments)):arguments)};return a._zid=h(t),a}if(s(n))return r?(r.unshift(t[n],t),e.proxy.apply(null,r)):e.proxy(t[n],t);throw new TypeError("expected function")},e.fn.bind=function(t,e,n){return this.on(t,e,n)},e.fn.unbind=function(t,e){return this.off(t,e)},e.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var b=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,w={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};e.fn.delegate=function(t,e,n){return this.on(e,t,n)},e.fn.undelegate=function(t,e,n){return this.off(e,t,n)},e.fn.live=function(t,n){return e(document.body).delegate(this.selector,t,n),this},e.fn.die=function(t,n){return e(document.body).undelegate(this.selector,t,n),this},e.fn.on=function(t,n,a,u,f){var c,l,h=this;return t&&!s(t)?(e.each(t,function(t,e){h.on(t,n,a,e,f)}),h):(s(n)||o(u)||u===!1||(u=a,a=n,n=r),(u===r||a===!1)&&(u=a,a=r),u===!1&&(u=E),h.each(function(r,o){f&&(c=function(t){return x(o,t.type,u),u.apply(this,arguments)}),n&&(l=function(t){var r,s=e(t.target).closest(n,o).get(0);return s&&s!==o?(r=e.extend(S(t),{currentTarget:s,liveFired:o}),(c||u).apply(s,[r].concat(i.call(arguments,1)))):void 0}),y(o,t,u,a,n,l||c)}))},e.fn.off=function(t,n,i){var a=this;return t&&!s(t)?(e.each(t,function(t,e){a.off(t,n,e)}),a):(s(n)||o(i)||i===!1||(i=n,n=r),i===!1&&(i=E),a.each(function(){x(this,t,i,n)}))},e.fn.trigger=function(t,n){return t=s(t)||e.isPlainObject(t)?e.Event(t):T(t),t._args=n,this.each(function(){t.type in c&&"function"==typeof this[t.type]?this[t.type]():"dispatchEvent"in this?this.dispatchEvent(t):e(this).triggerHandler(t,n)})},e.fn.triggerHandler=function(t,n){var r,i;return this.each(function(o,a){r=S(s(t)?e.Event(t):t),r._args=n,r.target=a,e.each(p(a,t.type||t),function(t,e){return i=e.proxy(r),r.isImmediatePropagationStopped()?!1:void 0})}),i},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(t){e.fn[t]=function(e){return 0 in arguments?this.bind(t,e):this.trigger(t)}}),e.Event=function(t,e){s(t)||(e=t,t=e.type);var n=document.createEvent(u[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),T(n)}}(e),function(e){function p(t,n,r){var i=e.Event(n);return e(t).trigger(i,r),!i.isDefaultPrevented()}function d(t,e,n,i){return t.global?p(e||r,n,i):void 0}function m(t){t.global&&0===e.active++&&d(t,null,"ajaxStart")}function g(t){t.global&&!--e.active&&d(t,null,"ajaxStop")}function v(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||d(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void d(e,n,"ajaxSend",[t,e])}function y(t,e,n,r){var i=n.context,o="success";n.success.call(i,t,o,e),r&&r.resolveWith(i,[t,o,e]),d(n,i,"ajaxSuccess",[e,n,t]),b(o,e,n)}function x(t,e,n,r,i){var o=r.context;r.error.call(o,n,e,t),i&&i.rejectWith(o,[n,e,t]),d(r,o,"ajaxError",[n,r,t||e]),b(e,n,r)}function b(t,e,n){var r=n.context;n.complete.call(r,e,t),d(n,r,"ajaxComplete",[e,n]),g(n)}function E(t,e,n){if(n.dataFilter==j)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function j(){}function w(t){return t&&(t=t.split(";",2)[0]),t&&(t==c?"html":t==f?"json":a.test(t)?"script":u.test(t)&&"xml")||"text"}function T(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function S(t){t.processData&&t.data&&"string"!=e.type(t.data)&&(t.data=e.param(t.data,t.traditional)),!t.data||t.type&&"GET"!=t.type.toUpperCase()&&"jsonp"!=t.dataType||(t.url=T(t.url,t.data),t.data=void 0)}function C(t,n,r,i){return e.isFunction(n)&&(i=r,r=n,n=void 0),e.isFunction(r)||(i=r,r=void 0),{url:t,data:n,success:r,dataType:i}}function O(t,n,r,i){var o,s=e.isArray(n),a=e.isPlainObject(n);e.each(n,function(n,u){o=e.type(u),i&&(n=r?i:i+"["+(a||"object"==o||"array"==o?n:"")+"]"),!i&&s?t.add(u.name,u.value):"array"==o||!r&&"object"==o?O(t,u,r,n):t.add(n,u)})}var i,o,n=+new Date,r=t.document,s=/
7 |
8 |
9 |
11 |
12 |
13 |
14 |