├── .github
└── ISSUE_TEMPLATE.md
├── .gitignore
├── LICENSE
├── README.md
├── lerna.json
├── package.json
└── packages
├── core
├── .babelrc
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── examples
│ ├── grid.html
│ ├── imgs
│ │ ├── 0.png
│ │ ├── 1.png
│ │ ├── 10.png
│ │ ├── 100.png
│ │ ├── 2.png
│ │ ├── 20.png
│ │ ├── 50.png
│ │ └── bg.png
│ ├── index.html
│ ├── slot.html
│ └── wheel.html
├── index.js
├── package.json
├── postinstall.js
├── rollup.config.build.js
├── rollup.config.dev.js
├── src
│ ├── index.ts
│ ├── lib
│ │ ├── grid.ts
│ │ ├── lucky.ts
│ │ ├── slot.ts
│ │ └── wheel.ts
│ ├── observer
│ │ ├── array.ts
│ │ ├── dep.ts
│ │ ├── index.ts
│ │ ├── utils.ts
│ │ └── watcher.ts
│ ├── types
│ │ ├── grid.ts
│ │ ├── index.ts
│ │ ├── slot.ts
│ │ └── wheel.ts
│ └── utils
│ │ ├── image.ts
│ │ ├── index.ts
│ │ ├── math.ts
│ │ ├── polyfill.js
│ │ └── tween.ts
├── tsconfig.json
└── types
│ └── index.d.ts
├── mini
├── .babelrc
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── gulpfile.js
├── package.json
├── src
│ ├── lucky-grid
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── lucky-wheel
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── slot-machine
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ └── utils.js
├── test
│ ├── index.test.js
│ ├── utils.js
│ └── wx.test.js
├── tools
│ ├── build.js
│ ├── checkcomponents.js
│ ├── checkwxss.js
│ ├── config.js
│ ├── demo
│ │ ├── app.js
│ │ ├── app.json
│ │ ├── app.wxss
│ │ ├── package.json
│ │ ├── pages
│ │ │ ├── cjl
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── index
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── jd
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── slot
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── xc
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── xdf
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── ymc
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── yx
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ ├── yyjk
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ └── yyx
│ │ │ │ ├── img.js
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ └── project.config.json
│ └── utils.js
└── tsconfig.json
├── react
├── .babelrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── example
│ ├── react16.8.html
│ └── react18.0.html
├── package.json
├── rollup.config.build.js
├── rollup.config.dev.js
└── src
│ ├── app.js
│ ├── components
│ ├── LuckyGrid.js
│ ├── LuckyWheel.js
│ └── SlotMachine.js
│ ├── demo
│ ├── LuckyGrid.js
│ ├── LuckyWheel.js
│ └── SlotMachine.js
│ └── index.js
├── taro
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── package.json
├── react
│ ├── LuckyGrid.js
│ ├── LuckyWheel.js
│ ├── SlotMachine.js
│ └── index.js
├── utils
│ ├── index.css
│ └── index.js
└── vue
│ ├── LuckyGrid.vue
│ ├── LuckyWheel.vue
│ ├── SlotMachine.vue
│ └── index.js
├── uni
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── lucky-grid.vue
├── lucky-wheel.vue
├── package.json
├── slot-machine.vue
└── utils.js
└── vue
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── composition-api.js
├── index.js
├── package.json
├── rollup.config.build.js
├── rollup.config.dev.js
├── shims-vue.d.ts
├── src
├── components
│ ├── LuckyGrid.ts
│ ├── LuckyWheel.ts
│ └── SlotMachine.ts
├── index.ts
└── utils
│ └── h-demi.ts
├── tsconfig.json
├── types
└── index.d.ts
├── vue-demi.js
├── vue2.html
└── vue3.html
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | - 你当前是什么框架(必填):
11 |
12 |
13 | - 你使用的是哪个包(必填):
14 |
15 |
16 | - 你当前插件的版本(必填):
17 |
18 |
19 | - 当前环境是小程序还是浏览器(选填):
20 |
21 |
22 | - 详细描述你的bug:
23 |
24 |
25 | - 问题代码(重要):
26 |
27 |
28 | ```
29 | // 代码开始, 别再放歪了行吗
30 |
31 | // 代码结束
32 | ```
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | yarn.lock
4 | package-lock.json
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 | lerna-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
25 | packages/demo-taro-vue
26 | packages/demo-taro-react
27 | packages/uni-app-demo
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "version": "0.0.4",
6 | "npmClient": "npm",
7 | "useWorkspaces": true
8 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lucky-canvas",
3 | "devDependencies": {
4 | "lerna": "^4.0.0"
5 | },
6 | "scripts": {
7 | "install": "lerna bootstrap",
8 | "dev": "lerna exec --scope=lucky-canvas -- npm run dev",
9 | "dev:vue": "lerna run --scope=lucky-canvas --scope=@lucky-canvas/vue dev --parallel",
10 | "dev:react": "lerna run --scope=lucky-canvas --scope=@lucky-canvas/react dev --parallel",
11 | "dev:uni": "",
12 | "dev:taro": "",
13 | "dev:mini": "",
14 | "build": "lerna run build --sort --stream",
15 | "update-version": "lerna version --conventional-commits --no-push --no-changelog --no-git-tag-version",
16 | "publish-to-npm": "lerna publish from-package",
17 | "publish-beta": "lerna publish --no-git-tag-version --dist-tag beta"
18 | },
19 | "private": true,
20 | "workspaces": [
21 | "packages/*"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/core/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env", {
5 | "modules": false
6 | }
7 | ]
8 | ]
9 | }
--------------------------------------------------------------------------------
/packages/core/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // module.exports = {
2 | // "env": {
3 | // "browser": true,
4 | // "es6": true,
5 | // "node": true
6 | // },
7 | // "parser": "@typescript-eslint/parser",
8 | // // "plugins": ['prettier'],
9 | // "extends": "eslint:recommended",
10 | // "globals": {
11 | // "Atomics": "readonly",
12 | // "SharedArrayBuffer": "readonly",
13 | // "ENV": true
14 | // },
15 | // "parserOptions": {
16 | // "ecmaVersion": 2018,
17 | // "sourceType": "module"
18 | // },
19 | // "rules": {
20 | // "linebreak-style": 'off'
21 | // }
22 | // }
23 |
--------------------------------------------------------------------------------
/packages/core/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | yarn.lock
4 | package-lock.json
5 | dist
6 |
7 | # local env files
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/packages/core/.npmignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
23 |
24 |
25 | ## 官方文档 & Demo演示
26 |
27 | > **中文**:[https://100px.net/usage/js.html](https://100px.net/usage/js.html)
28 |
29 | > **English**:**If anyone can help translate the document, please contact me** `ldq404@qq.com`
30 |
31 |
32 |
33 |
34 | ## 在 JS / TS 中使用
35 |
36 | - [跳转官网 查看详情](https://100px.net/usage/js.html)
37 |
38 |
39 |
40 | ## 🙏🙏🙏 点个Star
41 |
42 | **如果您觉得这个项目还不错, 可以在 [Github](https://github.com/buuing/lucky-canvas) 上面帮我点个`star`, 支持一下作者 ☜(゚ヮ゚☜)**
43 |
--------------------------------------------------------------------------------
/packages/core/examples/grid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/packages/core/examples/imgs/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/0.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/1.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/10.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/100.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/2.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/20.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/50.png
--------------------------------------------------------------------------------
/packages/core/examples/imgs/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/core/examples/imgs/bg.png
--------------------------------------------------------------------------------
/packages/core/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 | 大转盘示例
11 |
14 | 九宫格示例
15 |
18 | 老虎机示例
19 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/packages/core/examples/slot.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/packages/core/examples/wheel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/packages/core/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/index.umd.js')
2 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lucky-canvas",
3 | "version": "1.7.26",
4 | "description": "一个基于原生 js 的(大转盘 / 九宫格 / 老虎机)抽奖插件",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.esm.js",
7 | "unpkg": "dist/index.umd.js",
8 | "jsdelivr": "dist/index.umd.js",
9 | "types": "types/index.d.ts",
10 | "scripts": {
11 | "dev": "rollup --config rollup.config.dev.js -w",
12 | "build": "rollup --config rollup.config.build.js"
13 | },
14 | "homepage": "https://100px.net",
15 | "bugs": "https://github.com/LuckDraw/lucky-canvas/issues",
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/LuckDraw/lucky-canvas.git",
19 | "directory": "packages/lucky-canvas"
20 | },
21 | "author": "ldq ",
22 | "license": "Apache-2.0",
23 | "files": [
24 | "dist",
25 | "types",
26 | "index.js"
27 | ],
28 | "keywords": [
29 | "大转盘抽奖",
30 | "九宫格抽奖",
31 | "老虎机抽奖",
32 | "抽奖插件",
33 | "js抽奖",
34 | "移动端抽奖",
35 | "canvas抽奖"
36 | ],
37 | "devDependencies": {
38 | "@babel/core": "^7.12.3",
39 | "@babel/preset-env": "^7.12.1",
40 | "@babel/plugin-transform-runtime": "^7.16.4",
41 | "@babel/runtime": "^7.16.3",
42 | "core-js": "^3.19.2",
43 | "@rollup/plugin-commonjs": "^16.0.0",
44 | "@rollup/plugin-eslint": "^8.0.1",
45 | "@rollup/plugin-json": "^4.1.0",
46 | "@rollup/plugin-node-resolve": "^10.0.0",
47 | "@rollup/plugin-typescript": "^6.1.0",
48 | "@typescript-eslint/parser": "^4.14.0",
49 | "babel-plugin-external-helpers": "^6.22.0",
50 | "babel-preset-latest": "^6.24.1",
51 | "eslint": "^7.18.0",
52 | "eslint-plugin-prettier": "^3.3.1",
53 | "prettier": "^2.2.1",
54 | "rollup": "^2.33.1",
55 | "rollup-plugin-babel": "^4.4.0",
56 | "rollup-plugin-livereload": "^2.0.0",
57 | "rollup-plugin-serve": "^1.1.0",
58 | "rollup-plugin-terser": "^7.0.2",
59 | "rollup-plugin-delete": "^2.0.0",
60 | "rollup-plugin-dts": "^3.0.2",
61 | "rollup-plugin-typescript2": "^0.30.0",
62 | "tslib": "^2.3.1",
63 | "typescript": "^4.0.5"
64 | },
65 | "dependencies": {}
66 | }
67 |
--------------------------------------------------------------------------------
/packages/core/postinstall.js:
--------------------------------------------------------------------------------
1 | console.log(` \u001B[93m╭────────────────────────────────────────────────────╮\u001b[0m
2 | \u001B[93m│\u001b[0m \u001B[93m│\u001b[0m
3 | \u001B[93m│\u001b[0m \u001B[97m欢迎使用\u001B[0m \u001B[46mlucky-canvas\u001B[0m \u001B[97m抽奖插件\u001B[0m \u001B[93m│\u001b[0m
4 | \u001B[93m│\u001b[0m \u001B[96m官网文档: https://100px.net\u001B[0m \u001B[93m│\u001b[0m
5 | \u001B[93m│\u001b[0m \u001B[93m│\u001b[0m
6 | \u001B[93m│\u001b[0m \u001B[93m如果您用着顺手可以点个 Star 支持一下\u001B[0m \u001B[93m│\u001b[0m
7 | \u001B[93m│\u001b[0m \u001B[96mGitHub: https://github.com/LuckDraw/lucky-canvas\u001b[0m \u001B[93m│\u001b[0m
8 | \u001B[93m│\u001b[0m \u001B[93m│\u001b[0m
9 | \u001B[93m╰────────────────────────────────────────────────────╯\u001b[0m`)
10 |
--------------------------------------------------------------------------------
/packages/core/rollup.config.build.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import ts from 'rollup-plugin-typescript2'
3 | import dts from 'rollup-plugin-dts'
4 | import json from '@rollup/plugin-json'
5 | import resolve from '@rollup/plugin-node-resolve'
6 | import commonjs from '@rollup/plugin-commonjs'
7 | import babel from 'rollup-plugin-babel'
8 | import { terser } from 'rollup-plugin-terser'
9 | import del from 'rollup-plugin-delete'
10 | import pkg from './package.json'
11 |
12 | export default [
13 | {
14 | input: 'src/index.ts',
15 | output: [
16 | {
17 | file: pkg.main,
18 | format: 'cjs',
19 | sourcemap: true,
20 | },
21 | {
22 | file: pkg.module,
23 | format: 'esm',
24 | sourcemap: true,
25 | },
26 | {
27 | file: pkg.jsdelivr,
28 | format: 'umd',
29 | name: 'LuckyCanvas',
30 | sourcemap: false,
31 | },
32 | ],
33 | plugins: [
34 | ts({
35 | tsconfig: path.resolve(__dirname, './tsconfig.json'),
36 | extensions: ['.js', '.ts'],
37 | "declaration": true,
38 | }),
39 | json(),
40 | resolve(),
41 | commonjs(),
42 | babel({
43 | runtimeHelpers: true,
44 | exclude: 'node_modules/**',
45 | }),
46 | terser()
47 | ]
48 | }, {
49 | input: "dist/src/index.d.ts",
50 | output: [
51 | {
52 | file: "types/index.d.ts",
53 | format: "es"
54 | }
55 | ],
56 | plugins: [
57 | dts(),
58 | del({
59 | targets: ['dist/src'],
60 | hook: 'buildEnd'
61 | })
62 | ],
63 | },
64 | ]
65 |
--------------------------------------------------------------------------------
/packages/core/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import ts from 'rollup-plugin-typescript2'
3 | import json from '@rollup/plugin-json'
4 | import resolve from '@rollup/plugin-node-resolve'
5 | import commonjs from '@rollup/plugin-commonjs'
6 | import babel from 'rollup-plugin-babel'
7 | import livereload from 'rollup-plugin-livereload'
8 | import serve from 'rollup-plugin-serve'
9 | // import eslint from '@rollup/plugin-eslint'
10 | import pkg from './package.json'
11 |
12 | export default {
13 | input: 'src/index.ts',
14 | output: [
15 | {
16 | file: pkg.jsdelivr,
17 | format: 'umd',
18 | name: 'LuckyCanvas',
19 | sourcemap: true,
20 | },
21 | {
22 | file: pkg.module,
23 | format: 'es',
24 | sourcemap: true,
25 | },
26 | ],
27 | plugins: [
28 | resolve(),
29 | commonjs(),
30 | json(),
31 | // eslint({
32 | // throwOnError: true,
33 | // throwOnWarning: true,
34 | // include: ['src/**'],
35 | // exclude: ['node_modules/**']
36 | // }),
37 | ts({
38 | tsconfig: path.resolve(__dirname, './tsconfig.json'),
39 | extensions: ['.js', '.ts']
40 | }),
41 | babel({
42 | runtimeHelpers: true,
43 | exclude: 'node_modules/**',
44 | }),
45 | livereload(),
46 | serve({
47 | open: true,
48 | port: 13000,
49 | contentBase: './',
50 | openPage: '/examples/index.html'
51 | }),
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as LuckyWheel } from './lib/wheel'
2 | export { default as LuckyGrid } from './lib/grid'
3 | export { default as SlotMachine } from './lib/slot'
4 | export { cutRound, opacity } from './utils/image'
5 |
6 | // export type { default as LuckyWheelConfig } from './types/wheel'
7 | // export type { default as LuckyGridConfig } from './types/grid'
8 | // export type { default as SlotMachineConfig } from './types/slot'
9 |
--------------------------------------------------------------------------------
/packages/core/src/observer/array.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 重写数组的原型方法
3 | */
4 | const oldArrayProto = Array.prototype
5 | const newArrayProto = Object.create(oldArrayProto)
6 | const methods = ['push', 'pop', 'shift', 'unshift', 'sort', 'splice', 'reverse']
7 | methods.forEach(method => {
8 | newArrayProto[method] = function (...args: any[]) {
9 | const res = oldArrayProto[method].apply(this, args)
10 | const luckyOb = this['__luckyOb__']
11 | if (['push', 'unshift', 'splice'].includes(method)) luckyOb.walk(this)
12 | luckyOb.dep.notify()
13 | return res
14 | }
15 | })
16 |
17 | export { newArrayProto }
18 |
--------------------------------------------------------------------------------
/packages/core/src/observer/dep.ts:
--------------------------------------------------------------------------------
1 | import Watcher from './watcher'
2 |
3 | export default class Dep {
4 | static target: Watcher | null
5 | private subs: Array
6 |
7 | /**
8 | * 订阅中心构造器
9 | */
10 | constructor () {
11 | this.subs = []
12 | }
13 |
14 | /**
15 | * 收集依赖
16 | * @param {*} sub
17 | */
18 | public addSub (sub: Watcher) {
19 | // 此处临时使用includes防重复添加
20 | if (!this.subs.includes(sub)) {
21 | this.subs.push(sub)
22 | }
23 | }
24 |
25 | /**
26 | * 派发更新
27 | */
28 | public notify () {
29 | this.subs.forEach(sub => {
30 | sub.update()
31 | })
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/core/src/observer/index.ts:
--------------------------------------------------------------------------------
1 | import Dep from './dep'
2 | import { hasProto, def } from './utils'
3 | import { newArrayProto } from './array'
4 |
5 | export default class Observer {
6 | value: any
7 | dep: Dep
8 |
9 | /**
10 | * 观察者构造器
11 | * @param value
12 | */
13 | constructor (value: any) {
14 | // this.value = value
15 | this.dep = new Dep()
16 | // 将响应式对象代理到当前value上面, 并且将当前的enumerable设置为false
17 | def(value, '__luckyOb__', this)
18 | if (Array.isArray(value)) { // 如果是数组, 则重写原型方法
19 | if (hasProto) {
20 | value['__proto__'] = newArrayProto
21 | } else {
22 | Object.getOwnPropertyNames(newArrayProto).forEach(key => {
23 | def(value, key, newArrayProto[key])
24 | })
25 | }
26 | }
27 | this.walk(value)
28 | }
29 |
30 | walk (data: object | any[]) {
31 | Object.keys(data).forEach(key => {
32 | defineReactive(data, key, data[key])
33 | })
34 | }
35 | }
36 |
37 | /**
38 | * 处理响应式
39 | * @param { Object | Array } data
40 | */
41 | export function observe (data: any): Observer | void {
42 | if (!data || typeof data !== 'object') return
43 | let luckyOb: Observer | void
44 | if ('__luckyOb__' in data) {
45 | luckyOb = data['__luckyOb__']
46 | } else {
47 | luckyOb = new Observer(data)
48 | }
49 | return luckyOb
50 | }
51 |
52 | /**
53 | * 重写 setter / getter
54 | * @param {*} data
55 | * @param {*} key
56 | * @param {*} val
57 | */
58 | export function defineReactive (data: any, key: string | number, val: any) {
59 | const dep = new Dep()
60 | const property = Object.getOwnPropertyDescriptor(data, key)
61 | if (property && property.configurable === false) {
62 | return
63 | }
64 | const getter = property && property.get
65 | const setter = property && property.set
66 | if ((!getter || setter) && arguments.length === 2) {
67 | val = data[key]
68 | }
69 | let childOb = observe(val)
70 | Object.defineProperty(data, key, {
71 | get: () => {
72 | const value = getter ? getter.call(data) : val
73 | if (Dep.target) {
74 | dep.addSub(Dep.target)
75 | if (childOb) {
76 | childOb.dep.addSub(Dep.target)
77 | }
78 | }
79 | return value
80 | },
81 | set: (newVal) => {
82 | if (newVal === val) return
83 | val = newVal
84 | if (getter && !setter) return
85 | if (setter) {
86 | setter.call(data, newVal)
87 | } else {
88 | val = newVal
89 | }
90 | childOb = observe(newVal)
91 | dep.notify()
92 | }
93 | })
94 | }
95 |
--------------------------------------------------------------------------------
/packages/core/src/observer/utils.ts:
--------------------------------------------------------------------------------
1 |
2 | import { isExpectType } from '../utils'
3 |
4 | export const hasProto = '__proto__' in {}
5 |
6 | export function def (obj: object, key: string | number, val: any, enumerable?: boolean) {
7 | Object.defineProperty(obj, key, {
8 | value: val,
9 | enumerable: !!enumerable,
10 | writable: true,
11 | configurable: true
12 | })
13 | }
14 |
15 | export function parsePath (path: string) {
16 | path += '.'
17 | let segments: string[] = [], segment = ''
18 | for (let i = 0; i < path.length; i++) {
19 | let curr = path[i]
20 | if (/\[|\./.test(curr)) {
21 | segments.push(segment)
22 | segment = ''
23 | } else if (/\W/.test(curr)) {
24 | continue
25 | } else {
26 | segment += curr
27 | }
28 | }
29 | return function (data: object | any[]) {
30 | return segments.reduce((data, key) => {
31 | return data[key]
32 | }, data)
33 | }
34 | }
35 |
36 | export function traverse (value: any) {
37 | // const seenObjects = new Set()
38 | const dfs = (data: any) => {
39 | if (!isExpectType(data, 'array', 'object')) return
40 | Object.keys(data).forEach(key => {
41 | const value = data[key]
42 | dfs(value)
43 | })
44 | }
45 | dfs(value)
46 | // seenObjects.clear()
47 | }
--------------------------------------------------------------------------------
/packages/core/src/observer/watcher.ts:
--------------------------------------------------------------------------------
1 | import Lucky from '../lib/lucky'
2 | import Dep from './dep'
3 | import { parsePath, traverse } from './utils'
4 |
5 | export interface WatchOptType {
6 | handler?: () => Function
7 | immediate?: boolean
8 | deep?: boolean
9 | }
10 |
11 | let uid = 0
12 | export default class Watcher {
13 | id: number
14 | $lucky: Lucky
15 | expr: string | Function
16 | cb: Function
17 | deep: boolean
18 | getter: Function
19 | value: any
20 |
21 | /**
22 | * 观察者构造器
23 | * @param {*} $lucky
24 | * @param {*} expr
25 | * @param {*} cb
26 | */
27 | constructor ($lucky: Lucky, expr: string | Function, cb: Function, options: WatchOptType = {}) {
28 | this.id = uid++
29 | this.$lucky = $lucky
30 | this.expr = expr
31 | this.deep = !!options.deep
32 | if (typeof expr === 'function') {
33 | this.getter = expr
34 | } else {
35 | this.getter = parsePath(expr)
36 | }
37 | this.cb = cb
38 | this.value = this.get()
39 | }
40 |
41 | /**
42 | * 根据表达式获取新值
43 | */
44 | get () {
45 | Dep.target = this
46 | const value = this.getter.call(this.$lucky, this.$lucky)
47 | // 处理深度监听
48 | if (this.deep) {
49 | traverse(value)
50 | }
51 | Dep.target = null
52 | return value
53 | }
54 |
55 | /**
56 | * 触发 watcher 更新
57 | */
58 | update () {
59 | // get获取新值
60 | const newVal = this.get()
61 | // 读取之前存储的旧值
62 | const oldVal = this.value
63 | this.value = newVal
64 | // 触发 watch 回调
65 | this.cb.call(this.$lucky, newVal, oldVal)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/core/src/types/grid.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FontItemType,
3 | ImgItemType,
4 | BorderRadiusType,
5 | BackgroundType,
6 | ShadowType,
7 | FontExtendType
8 | } from './index'
9 |
10 | export type PrizeFontType = FontItemType & FontExtendType
11 |
12 | export type ButtonFontType = FontItemType & FontExtendType
13 |
14 | export type CellFontType = PrizeFontType | ButtonFontType
15 |
16 | export type BlockImgType = ImgItemType & {}
17 |
18 | export type PrizeImgType = ImgItemType & {
19 | activeSrc?: string
20 | }
21 |
22 | export type ButtonImgType = ImgItemType & {}
23 |
24 | export type CellImgType = PrizeImgType | ButtonImgType
25 |
26 | export type BlockType = {
27 | borderRadius?: BorderRadiusType
28 | background?: BackgroundType
29 | padding?: string
30 | paddingTop?: string | number
31 | paddingRight?: string | number
32 | paddingBottom?: string | number
33 | paddingLeft?: string | number
34 | imgs?: Array
35 | }
36 |
37 | export type CellType = {
38 | x: number
39 | y: number
40 | col?: number
41 | row?: number
42 | borderRadius?: BorderRadiusType
43 | background?: BackgroundType
44 | shadow?: ShadowType
45 | fonts?: Array
46 | imgs?: Array
47 | }
48 |
49 | export type PrizeType = CellType & {
50 | range?: number
51 | disabled?: boolean
52 | }
53 |
54 | export type ButtonType = CellType & {
55 | callback?: Function
56 | }
57 |
58 | export type DefaultConfigType = {
59 | gutter?: number
60 | speed?: number
61 | accelerationTime?: number
62 | decelerationTime?: number
63 | }
64 |
65 | export type DefaultStyleType = {
66 | borderRadius?: BorderRadiusType
67 | background?: BackgroundType
68 | shadow?: ShadowType
69 | fontColor?: PrizeFontType['fontColor']
70 | fontSize?: PrizeFontType['fontSize']
71 | fontStyle?: PrizeFontType['fontStyle']
72 | fontWeight?: PrizeFontType['fontWeight']
73 | lineHeight?: PrizeFontType['lineHeight']
74 | wordWrap?: PrizeFontType['wordWrap']
75 | lengthLimit?: PrizeFontType['lengthLimit']
76 | lineClamp?: PrizeFontType['lineClamp']
77 | }
78 |
79 | export type ActiveStyleType = {
80 | background?: BackgroundType
81 | shadow?: ShadowType
82 | fontColor?: PrizeFontType['fontColor']
83 | fontSize?: PrizeFontType['fontSize']
84 | fontStyle?: PrizeFontType['fontStyle']
85 | fontWeight?: PrizeFontType['fontWeight']
86 | lineHeight?: PrizeFontType['lineHeight']
87 | }
88 |
89 | export type RowsType = number
90 | export type ColsType = number
91 | export type StartCallbackType = (e: MouseEvent, button?: ButtonType) => void
92 | export type EndCallbackType = (prize: object) => void
93 |
94 | export default interface LuckyGridConfig {
95 | width: string | number
96 | height: string | number
97 | rows?: RowsType
98 | cols?: ColsType
99 | blocks?: Array
100 | prizes?: Array
101 | buttons?: Array
102 | button?: ButtonType
103 | defaultConfig?: DefaultConfigType
104 | defaultStyle?: DefaultStyleType
105 | activeStyle?: ActiveStyleType
106 | start?: StartCallbackType
107 | end?: EndCallbackType
108 | }
109 |
--------------------------------------------------------------------------------
/packages/core/src/types/index.ts:
--------------------------------------------------------------------------------
1 | // 字体类型
2 | export type FontItemType = {
3 | text: string
4 | top?: string | number
5 | left?: string | number
6 | fontColor?: string
7 | fontSize?: string
8 | fontStyle?: string
9 | fontWeight?: string
10 | lineHeight?: string
11 | }
12 |
13 | export type FontExtendType = {
14 | wordWrap?: boolean
15 | lengthLimit?: string | number
16 | lineClamp?: number
17 | }
18 |
19 | export type ImgType = HTMLImageElement | HTMLCanvasElement
20 |
21 | // 图片类型
22 | export type ImgItemType = {
23 | src: string
24 | top?: string | number
25 | left?: string | number
26 | width?: string
27 | height?: string
28 | formatter?: (img: ImgType) => ImgType
29 | $resolve?: Function
30 | $reject?: Function
31 | }
32 |
33 | export type BorderRadiusType = string | number
34 | export type BackgroundType = string
35 | export type ShadowType = string
36 |
37 | export type ConfigType = {
38 | // 临时处理元素类型, 当版本升到4.x之后就可以删掉了
39 | nodeType?: number
40 | // 配置
41 | flag: 'WEB' | 'MP-WX' | 'UNI-H5' | 'UNI-MP' | 'TARO-H5' | 'TARO-MP'
42 | el?: string
43 | divElement?: HTMLDivElement
44 | canvasElement?: HTMLCanvasElement
45 | ctx: CanvasRenderingContext2D
46 | dpr: number
47 | handleCssUnit?: (num: number, unit: string) => number
48 | // 覆盖方法
49 | rAF?: Function
50 | setTimeout: Function
51 | setInterval: Function
52 | clearTimeout: Function
53 | clearInterval: Function
54 | // 组件生命周期
55 | beforeCreate?: Function
56 | beforeResize?: Function
57 | afterResize?: Function
58 | beforeInit?: Function
59 | afterInit?: Function
60 | beforeDraw?: Function
61 | afterDraw?: Function
62 | afterStart?: Function
63 | }
64 |
65 | export type UserConfigType = Partial
66 |
67 | export type UniImageType = {
68 | path: string
69 | width: number
70 | height: number
71 | }
72 |
73 | export type Tuple = Res['length'] extends Len ? Res : Tuple
74 |
--------------------------------------------------------------------------------
/packages/core/src/types/slot.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FontItemType,
3 | ImgItemType,
4 | BorderRadiusType,
5 | BackgroundType,
6 | FontExtendType
7 | } from './index'
8 |
9 | export type PrizeFontType = FontItemType & FontExtendType
10 |
11 | export type BlockImgType = ImgItemType & {}
12 |
13 | export type PrizeImgType = ImgItemType
14 |
15 | export type BlockType = {
16 | borderRadius?: BorderRadiusType
17 | background?: BackgroundType
18 | padding?: string
19 | paddingTop?: string | number
20 | paddingRight?: string | number
21 | paddingBottom?: string | number
22 | paddingLeft?: string | number
23 | imgs?: Array
24 | }
25 |
26 | export type PrizeType = {
27 | borderRadius?: BorderRadiusType
28 | background?: BackgroundType
29 | fonts?: Array
30 | imgs?: Array
31 | }
32 |
33 | export type SlotType = {
34 | order?: number[]
35 | speed?: number
36 | direction?: 1 | -1
37 | }
38 |
39 | export type DefaultConfigType = {
40 | /**
41 | * vertical 为纵向旋转
42 | * horizontal 为横向旋转
43 | */
44 | mode?: 'vertical' | 'horizontal'
45 | /**
46 | * 当排列方向 = `vertical`时
47 | * 1 bottom to top
48 | * -1 top to bottom
49 | * 当排列方向 = `horizontal`时
50 | * 1 right to left
51 | * -1 left to right
52 | */
53 | direction?: 1 | -1
54 | // 行间距
55 | rowSpacing?: number
56 | // 列间距
57 | colSpacing?: number
58 | // 速度
59 | speed?: number
60 | accelerationTime?: number
61 | decelerationTime?: number
62 | }
63 |
64 | export type DefaultStyleType = {
65 | borderRadius?: BorderRadiusType
66 | background?: BackgroundType
67 | fontColor?: PrizeFontType['fontColor']
68 | fontSize?: PrizeFontType['fontSize']
69 | fontStyle?: PrizeFontType['fontStyle']
70 | fontWeight?: PrizeFontType['fontWeight']
71 | lineHeight?: PrizeFontType['lineHeight']
72 | wordWrap?: PrizeFontType['wordWrap']
73 | lengthLimit?: PrizeFontType['lengthLimit']
74 | lineClamp?: PrizeFontType['lineClamp']
75 | }
76 |
77 | export type EndCallbackType = (prize: PrizeType | undefined) => void
78 |
79 | export default interface SlotMachineConfig {
80 | width: string | number
81 | height: string | number
82 | blocks?: Array
83 | prizes?: Array
84 | slots?: Array
85 | defaultConfig?: DefaultConfigType
86 | defaultStyle?: DefaultStyleType
87 | end?: EndCallbackType
88 | }
89 |
--------------------------------------------------------------------------------
/packages/core/src/types/wheel.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FontItemType,
3 | ImgItemType,
4 | BackgroundType,
5 | FontExtendType
6 | } from './index'
7 |
8 | export type PrizeFontType = FontItemType & FontExtendType
9 |
10 | export type ButtonFontType = FontItemType & {}
11 |
12 | export type BlockImgType = ImgItemType & {
13 | rotate?: boolean
14 | }
15 |
16 | export type PrizeImgType = ImgItemType & {}
17 |
18 | export type ButtonImgType = ImgItemType & {}
19 |
20 | export type BlockType = {
21 | padding?: string
22 | background?: BackgroundType
23 | imgs?: Array
24 | }
25 |
26 | export type PrizeType = {
27 | range?: number
28 | background?: BackgroundType
29 | fonts?: Array
30 | imgs?: Array
31 | }
32 |
33 | export type ButtonType = {
34 | radius?: string
35 | pointer?: boolean
36 | background?: BackgroundType
37 | fonts?: Array
38 | imgs?: Array
39 | }
40 |
41 | export type DefaultConfigType = {
42 | gutter?: string | number
43 | offsetDegree?: number
44 | speed?: number
45 | speedFunction?: string
46 | accelerationTime?: number
47 | decelerationTime?: number
48 | stopRange?: number
49 | }
50 |
51 | export type DefaultStyleType = {
52 | background?: BackgroundType
53 | fontColor?: PrizeFontType['fontColor']
54 | fontSize?: PrizeFontType['fontSize']
55 | fontStyle?: PrizeFontType['fontStyle']
56 | fontWeight?: PrizeFontType['fontWeight']
57 | lineHeight?: PrizeFontType['lineHeight']
58 | wordWrap?: PrizeFontType['wordWrap']
59 | lengthLimit?: PrizeFontType['lengthLimit']
60 | lineClamp?: PrizeFontType['lineClamp']
61 | }
62 |
63 | export type StartCallbackType = (e: MouseEvent) => void
64 | export type EndCallbackType = (prize: object) => void
65 |
66 | export default interface LuckyWheelConfig {
67 | width: string | number
68 | height: string | number
69 | blocks?: Array
70 | prizes?: Array
71 | buttons?: Array
72 | defaultConfig?: DefaultConfigType
73 | defaultStyle?: DefaultStyleType
74 | start?: StartCallbackType
75 | end?: EndCallbackType
76 | }
77 |
--------------------------------------------------------------------------------
/packages/core/src/utils/image.ts:
--------------------------------------------------------------------------------
1 | import { ImgType } from '../types/index'
2 | import { roundRectByArc } from './math'
3 |
4 | /**
5 | * 根据路径获取图片对象
6 | * @param { string } src 图片路径
7 | * @returns { Promise } 图片标签
8 | */
9 | export const getImage = (src: string): Promise => {
10 | return new Promise((resolve, reject) => {
11 | const img = new Image()
12 | img.onload = () => resolve(img)
13 | img.onerror = err => reject(err)
14 | img.src = src
15 | })
16 | }
17 |
18 | /**
19 | * 切割圆角
20 | * @param img 将要裁剪的图片对象
21 | * @param radius 裁剪的圆角半径
22 | * @returns 返回一个离屏 canvas 用于渲染
23 | */
24 | export const cutRound = (img: ImgType, radius: number): ImgType => {
25 | const canvas = document.createElement('canvas')
26 | const ctx = canvas.getContext('2d')!
27 | const { width, height } = img
28 | canvas.width = width
29 | canvas.height = height
30 | roundRectByArc(ctx, 0, 0, width, height, radius)
31 | ctx.clip()
32 | ctx.drawImage(img, 0, 0, width, height)
33 | return canvas
34 | }
35 |
36 | /**
37 | * 透明度
38 | * @param img 将要处理的图片对象
39 | * @param opacity 透明度
40 | * @returns 返回一个离屏 canvas 用于渲染
41 | */
42 | export const opacity = (
43 | img: ImgType,
44 | opacity: number
45 | ): ImgType => {
46 | const canvas = document.createElement('canvas')
47 | const ctx = canvas.getContext('2d')!
48 | const { width, height } = img
49 | canvas.width = width
50 | canvas.height = height
51 | // 绘制图片, 部分浏览器不支持 filter 属性, 需要处理兼容
52 | if (typeof ctx.filter === 'string') {
53 | ctx.filter = `opacity(${opacity * 100}%)`
54 | ctx.drawImage(img, 0, 0, width, height)
55 | } else {
56 | ctx.drawImage(img, 0, 0, width, height)
57 | const imageData = ctx.getImageData(0, 0, width, height)
58 | const { data } = imageData
59 | const len = data.length
60 | for (let i = 0; i < len; i += 4) {
61 | const alpha = data[i + 3]
62 | if (alpha !== 0) data[i + 3] = alpha * opacity
63 | }
64 | ctx.putImageData(imageData, 0, 0)
65 | }
66 | return canvas
67 | }
68 |
69 | /**
70 | * 权重矩阵
71 | * @param radius 模糊半径
72 | * @param sigma
73 | * @returns 返回一个权重和为1的矩阵
74 | */
75 | const getMatrix = (radius: number, sigma?: number): number[] => {
76 | sigma = sigma || radius / 3
77 | const r = Math.ceil(radius)
78 | const sigma_2 = sigma * sigma
79 | const sigma2_2 = 2 * sigma_2
80 | const denominator = 1 / (2 * Math.PI * sigma_2)
81 | const matrix = []
82 | let total = 0
83 | // 计算权重矩阵
84 | for (let x = -r; x <= r; x++) {
85 | for (let y = -r; y <= r; y++) {
86 | // 套用二维高斯函数得到每个点的权重
87 | const res = denominator * Math.exp(-(x * x + y * y) / sigma2_2)
88 | matrix.push(res)
89 | total += res
90 | }
91 | }
92 | // 让矩阵中所有权重的和等于1
93 | for (let i = 0; i < matrix.length; i++) {
94 | matrix[i] /= total
95 | }
96 | return matrix
97 | }
98 |
99 | /**
100 | * 高斯模糊
101 | * @param img 将要处理的图片对象
102 | * @param radius 模糊半径
103 | * @returns 返回一个离屏 canvas 用于渲染
104 | */
105 | export const blur = (
106 | img: ImgType,
107 | radius: number
108 | ): ImgType => {
109 | const canvas = document.createElement('canvas')
110 | const ctx = canvas.getContext('2d')!
111 | const { width, height } = img
112 | // 设置图片宽高
113 | canvas.width = width
114 | canvas.height = height
115 | ctx.drawImage(img, 0, 0, width, height)
116 | const ImageData = ctx.getImageData(0, 0, width, height)
117 | const { data } = ImageData
118 | const matrix = getMatrix(radius)
119 | const r = Math.ceil(radius)
120 | const w = width * 4
121 | const cols = r * 2 + 1
122 | const len = data.length, matrixLen = matrix.length
123 | for (let i = 0; i < len; i += 4) {
124 | // 处理
125 | }
126 | console.log(ImageData)
127 | ctx.putImageData(ImageData, 0, 0)
128 | return canvas
129 | }
130 |
131 | export const getBase64Image = () => {
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/packages/core/src/utils/polyfill.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 由于部分低版本下的某些 app 可能会缺少某些原型方法, 这里增加兼容
3 | */
4 |
5 | // ie11 不兼容 includes 方法
6 | if (!Array.prototype.includes) {
7 | Object.defineProperty(Array.prototype, 'includes', {
8 | value: function(valueToFind, fromIndex) {
9 |
10 | if (this == null) {
11 | throw new TypeError('"this" is null or not defined');
12 | }
13 |
14 | // 1. Let O be ? ToObject(this value).
15 | var o = Object(this);
16 |
17 | // 2. Let len be ? ToLength(? Get(O, "length")).
18 | var len = o.length >>> 0;
19 |
20 | // 3. If len is 0, return false.
21 | if (len === 0) {
22 | return false;
23 | }
24 |
25 | // 4. Let n be ? ToInteger(fromIndex).
26 | // (If fromIndex is undefined, this step produces the value 0.)
27 | var n = fromIndex | 0;
28 |
29 | // 5. If n ≥ 0, then
30 | // a. Let k be n.
31 | // 6. Else n < 0,
32 | // a. Let k be len + n.
33 | // b. If k < 0, let k be 0.
34 | var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
35 |
36 | function sameValueZero(x, y) {
37 | return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
38 | }
39 |
40 | // 7. Repeat, while k < len
41 | while (k < len) {
42 | // a. Let elementK be the result of ? Get(O, ! ToString(k)).
43 | // b. If SameValueZero(valueToFind, elementK) is true, return true.
44 | if (sameValueZero(o[k], valueToFind)) {
45 | return true;
46 | }
47 | // c. Increase k by 1.
48 | k++;
49 | }
50 |
51 | // 8. Return false
52 | return false;
53 | }
54 | });
55 | }
56 |
57 | // vivo x7 下网易云游戏 app 缺少 includes 方法
58 | if (!String.prototype.includes) {
59 | String.prototype.includes = function(search, start) {
60 | 'use strict';
61 | if (typeof start !== 'number') {
62 | start = 0;
63 | }
64 | if (start + search.length > this.length) {
65 | return false;
66 | } else {
67 | return this.indexOf(search, start) !== -1;
68 | }
69 | };
70 | }
71 |
72 | // vivo x7 下网易云游戏 app 缺少 find 方法
73 | if (!Array.prototype.find) {
74 | Object.defineProperty(Array.prototype, 'find', {
75 | value: function(predicate) {
76 | // 1. Let O be ? ToObject(this value).
77 | if (this == null) {
78 | throw new TypeError('"this" is null or not defined');
79 | }
80 | var o = Object(this);
81 | // 2. Let len be ? ToLength(? Get(O, "length")).
82 | var len = o.length >>> 0;
83 | // 3. If IsCallable(predicate) is false, throw a TypeError exception.
84 | if (typeof predicate !== 'function') {
85 | throw new TypeError('predicate must be a function');
86 | }
87 | // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
88 | var thisArg = arguments[1];
89 | // 5. Let k be 0.
90 | var k = 0;
91 | // 6. Repeat, while k < len
92 | while (k < len) {
93 | // a. Let Pk be ! ToString(k).
94 | // b. Let kValue be ? Get(O, Pk).
95 | // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
96 | // d. If testResult is true, return kValue.
97 | var kValue = o[k];
98 | if (predicate.call(thisArg, kValue, k, o)) {
99 | return kValue;
100 | }
101 | // e. Increase k by 1.
102 | k++;
103 | }
104 | // 7. Return undefined.
105 | return void 0;
106 | }
107 | });
108 | }
109 |
--------------------------------------------------------------------------------
/packages/core/src/utils/tween.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 缓动函数
3 | * t: current time(当前时间)
4 | * b: beginning value(初始值)
5 | * c: change in value(变化量)
6 | * d: duration(持续时间)
7 | *
8 | * 感谢张鑫旭大佬 https://github.com/zhangxinxu/Tween
9 | */
10 |
11 | interface SpeedType {
12 | easeIn: (...arr: number[]) => number
13 | easeOut: (...arr: number[]) => number
14 | }
15 |
16 | // 二次方的缓动
17 | export const quad: SpeedType = {
18 | easeIn: function (t, b, c, d) {
19 | if (t >= d) t = d
20 | return c * (t /= d) * t + b
21 | },
22 | easeOut: function (t, b, c, d) {
23 | if (t >= d) t = d
24 | return -c * (t /= d) * (t - 2) + b
25 | }
26 | }
27 |
28 | // 三次方的缓动
29 | export const cubic: SpeedType = {
30 | easeIn: function (t, b, c, d) {
31 | if (t >= d) t = d
32 | return c * (t /= d) * t * t + b
33 | },
34 | easeOut: function (t, b, c, d) {
35 | if (t >= d) t = d
36 | return c * ((t = t / d - 1) * t * t + 1) + b
37 | }
38 | }
39 |
40 | // 四次方的缓动
41 | export const quart: SpeedType = {
42 | easeIn: function (t, b, c, d) {
43 | if (t >= d) t = d
44 | return c * (t /= d) * t * t * t + b
45 | },
46 | easeOut: function (t, b, c, d) {
47 | if (t >= d) t = d
48 | return -c * ((t = t / d - 1) * t * t * t - 1) + b
49 | }
50 | }
51 |
52 | // 五次方的缓动
53 | export const quint: SpeedType = {
54 | easeIn: function (t, b, c, d) {
55 | if (t >= d) t = d
56 | return c * (t /= d) * t * t * t * t + b
57 | },
58 | easeOut: function (t, b, c, d) {
59 | if (t >= d) t = d
60 | return c * ((t = t / d - 1) * t * t * t * t + 1) + b
61 | }
62 | }
63 |
64 | // 正弦曲线的缓动
65 | export const sine: SpeedType = {
66 | easeIn: function (t, b, c, d) {
67 | if (t >= d) t = d
68 | return -c * Math.cos(t / d * (Math.PI / 2)) + c + b
69 | },
70 | easeOut: function (t, b, c, d) {
71 | if (t >= d) t = d
72 | return c * Math.sin(t / d * (Math.PI / 2)) + b
73 | }
74 | }
75 |
76 | // 指数曲线的缓动
77 | export const expo: SpeedType = {
78 | easeIn: function (t, b, c, d) {
79 | if (t >= d) t = d
80 | return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b
81 | },
82 | easeOut: function (t, b, c, d) {
83 | if (t >= d) t = d
84 | return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b
85 | }
86 | }
87 |
88 | // 圆形曲线的缓动
89 | export const circ: SpeedType = {
90 | easeIn: function (t, b, c, d) {
91 | if (t >= d) t = d
92 | return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b
93 | },
94 | easeOut: function (t, b, c, d) {
95 | if (t >= d) t = d
96 | return c * Math.sqrt(1 - (t = t / d - 1) * t) + b
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5", // 编译后的es版本
4 | "module": "esnext", // 前端模块化规范
5 | "allowJs": true, // 允许引入js文件
6 | "strict": true, // 开启严格模式
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "skipLibCheck": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "suppressImplicitAnyIndexErrors": true,
13 | "resolveJsonModule": true,
14 | "sourceMap": true,
15 | "declaration": true
16 | },
17 | "exclude": [
18 | "node_modules/**"
19 | ],
20 | "include": [
21 | "src/**/*"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/mini/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["module-resolver", {
4 | "root": ["./src"],
5 | "alias": {}
6 | }]
7 | ],
8 | "presets": [
9 | ["env", {"loose": true, "modules": "commonjs"}]
10 | ]
11 | }
--------------------------------------------------------------------------------
/packages/mini/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'extends': [
3 | 'airbnb-base',
4 | 'plugin:promise/recommended'
5 | ],
6 | 'parser': '@typescript-eslint/parser',
7 | 'plugins': ['@typescript-eslint'],
8 | 'parserOptions': {
9 | 'ecmaVersion': 9,
10 | 'ecmaFeatures': {
11 | 'jsx': false
12 | },
13 | 'sourceType': 'module'
14 | },
15 | 'env': {
16 | 'es6': true,
17 | 'node': true,
18 | 'jest': true
19 | },
20 | 'plugins': [
21 | 'import',
22 | 'node',
23 | 'promise'
24 | ],
25 | 'rules': {
26 | "no-console": "off",
27 | 'arrow-parens': 'off',
28 | 'comma-dangle': [
29 | 'error',
30 | 'only-multiline'
31 | ],
32 | 'complexity': ['error', 20],
33 | 'func-names': 'off',
34 | 'global-require': 'off',
35 | 'handle-callback-err': [
36 | 'error',
37 | '^(err|error)$'
38 | ],
39 | 'import/no-unresolved': [
40 | 'error',
41 | {
42 | 'caseSensitive': true,
43 | 'commonjs': true,
44 | 'ignore': ['^[^.]']
45 | }
46 | ],
47 | 'import/prefer-default-export': 'off',
48 | 'linebreak-style': 'off',
49 | 'no-catch-shadow': 'error',
50 | 'no-continue': 'off',
51 | 'no-div-regex': 'warn',
52 | 'no-else-return': 'off',
53 | 'no-param-reassign': 'off',
54 | 'no-plusplus': 'off',
55 | 'no-shadow': 'off',
56 | 'no-multi-assign': 'off',
57 | 'no-underscore-dangle': 'off',
58 | 'node/no-deprecated-api': 'error',
59 | 'node/process-exit-as-throw': 'error',
60 | 'operator-linebreak': [
61 | 'error',
62 | 'after',
63 | {
64 | 'overrides': {
65 | ':': 'before',
66 | '?': 'before'
67 | }
68 | }
69 | ],
70 | 'prefer-arrow-callback': 'off',
71 | 'prefer-destructuring': 'off',
72 | 'prefer-template': 'off',
73 | 'quote-props': [
74 | 1,
75 | 'as-needed',
76 | {
77 | 'unnecessary': true
78 | }
79 | ],
80 | 'semi': [
81 | 'error',
82 | 'never'
83 | ],
84 | 'no-await-in-loop': 'off',
85 | 'no-restricted-syntax': 'off',
86 | 'promise/always-return': 'off',
87 | },
88 | 'globals': {
89 | 'window': true,
90 | 'document': true,
91 | 'App': true,
92 | 'Page': true,
93 | 'Component': true,
94 | 'Behavior': true,
95 | 'wx': true,
96 | 'getCurrentPages': true,
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/packages/mini/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | dist
4 |
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | dev
12 | node_modules
13 | coverage
--------------------------------------------------------------------------------
/packages/mini/.npmignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/mini/README.md:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | ## 文档 - Document
25 |
26 | - **中文**:[https://100px.net](https://100px.net)
27 |
28 |
29 |
30 | ## 使用 - Usage
31 |
32 | - [**在 微信小程序 中使用**](https://100px.net/usage/wx.html)
33 |
34 |
35 |
36 | ## 🙏🙏🙏 点个Star
37 |
38 | **如果您觉得这个项目还不错, 可以在 [Github](https://github.com/buuing/lucky-canvas) 上面帮我点个`star`, 支持一下作者 ☜(゚ヮ゚☜)**
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/mini/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp')
2 | const clean = require('gulp-clean')
3 |
4 | const config = require('./tools/config')
5 | const BuildTask = require('./tools/build')
6 | const id = require('./package.json').name || 'miniprogram-custom-component'
7 |
8 | // 构建任务实例
9 | // eslint-disable-next-line no-new
10 | new BuildTask(id, config.entry)
11 |
12 | // 清空生成目录和文件
13 | gulp.task('clean', gulp.series(() => gulp.src(config.distPath, {read: false, allowEmpty: true}).pipe(clean()), done => {
14 | if (config.isDev) {
15 | return gulp.src(config.demoDist, {read: false, allowEmpty: true})
16 | .pipe(clean())
17 | }
18 |
19 | return done()
20 | }))
21 | // 监听文件变化并进行开发模式构建
22 | gulp.task('watch', gulp.series(`${id}-watch`))
23 | // 开发模式构建
24 | gulp.task('dev', gulp.series(`${id}-dev`))
25 | // 生产模式构建
26 | gulp.task('default', gulp.series(`${id}-default`))
27 |
--------------------------------------------------------------------------------
/packages/mini/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lucky-canvas/mini",
3 | "version": "0.0.8",
4 | "description": "",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "dev": "gulp dev --develop --watch",
8 | "build": "gulp",
9 | "dist": "npm run build",
10 | "clean-dev": "gulp clean --develop",
11 | "clean": "gulp clean",
12 | "test": "jest --bail",
13 | "test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --bail",
14 | "coverage": "jest ./test/* --coverage --bail",
15 | "lint": "eslint \"src/**/*.js\" --fix",
16 | "lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\" --fix"
17 | },
18 | "files": [
19 | "dist"
20 | ],
21 | "miniprogram": "dist",
22 | "jest": {
23 | "testEnvironment": "jsdom",
24 | "testURL": "https://jest.test",
25 | "collectCoverageFrom": [
26 | "dist/**/*.js"
27 | ],
28 | "moduleDirectories": [
29 | "node_modules",
30 | "dist"
31 | ]
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": ""
36 | },
37 | "author": "ldq",
38 | "license": "Apache-2.0",
39 | "devDependencies": {
40 | "@typescript-eslint/eslint-plugin": "^2.28.0",
41 | "@typescript-eslint/parser": "^2.28.0",
42 | "babel-core": "^6.26.3",
43 | "babel-loader": "^7.1.5",
44 | "babel-plugin-module-resolver": "^3.2.0",
45 | "babel-preset-env": "^1.7.0",
46 | "colors": "^1.3.1",
47 | "eslint": "^5.14.1",
48 | "eslint-config-airbnb-base": "13.1.0",
49 | "eslint-loader": "^2.1.2",
50 | "eslint-plugin-import": "^2.16.0",
51 | "eslint-plugin-node": "^7.0.1",
52 | "eslint-plugin-promise": "^3.8.0",
53 | "gulp": "^4.0.0",
54 | "gulp-clean": "^0.4.0",
55 | "gulp-if": "^2.0.2",
56 | "gulp-install": "^1.1.0",
57 | "gulp-less": "^4.0.1",
58 | "gulp-rename": "^1.4.0",
59 | "gulp-sourcemaps": "^2.6.5",
60 | "jest": "^23.5.0",
61 | "miniprogram-api-typings": "^2.10.3-1",
62 | "miniprogram-simulate": "^1.2.5",
63 | "thread-loader": "^2.1.3",
64 | "through2": "^2.0.3",
65 | "ts-loader": "^7.0.0",
66 | "typescript": "^3.8.3",
67 | "vinyl": "^2.2.0",
68 | "webpack": "^4.29.5",
69 | "webpack-node-externals": "^1.7.2"
70 | },
71 | "dependencies": {
72 | "lucky-canvas": "~1.7.24"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/packages/mini/src/lucky-grid/index.js:
--------------------------------------------------------------------------------
1 | import { LuckyGrid } from 'lucky-canvas'
2 | import { changeUnits, resolveImage, getImage } from '../utils'
3 |
4 | Component({
5 | properties: {
6 | width: { type: String, value: '600rpx' },
7 | height: { type: String, value: '600rpx' },
8 | rows: { type: String, optionalTypes: [Number], value: '3' },
9 | cols: { type: String, optionalTypes: [Number], value: '3' },
10 | blocks: { type: Array, value: [] },
11 | prizes: { type: Array, value: [] },
12 | buttons: { type: Array, value: [] },
13 | defaultConfig: { type: Object, value: {} },
14 | defaultStyle: { type: Object, value: {} },
15 | activeStyle: { type: Object, value: {} },
16 | start: { type: null, value: () => {} },
17 | end: { type: null, value: () => {} },
18 | },
19 | data: {
20 | lucky: null,
21 | isShow: false,
22 | luckyImg: '',
23 | showCanvas: true,
24 | },
25 | observers: {
26 | 'prizes.**': function (newData, oldData) {
27 | this.lucky && (this.lucky.prizes = newData)
28 | },
29 | 'buttons.**': function (newData, oldData) {
30 | this.lucky && (this.lucky.buttons = newData)
31 | },
32 | },
33 | ready() {
34 | wx.createSelectorQuery().in(this).select('#lucky-grid').fields({
35 | node: true, size: true
36 | }).exec((res) => {
37 | if (!res[0] || !res[0].node) {
38 | console.error('lucky-canvas 获取不到 canvas 标签')
39 | return
40 | }
41 | const canvas = this.canvas = res[0].node
42 | const dpr = this.dpr = wx.getSystemInfoSync().pixelRatio
43 | const ctx = this.ctx = canvas.getContext('2d')
44 | const data = this.data
45 | canvas.width = res[0].width * dpr
46 | canvas.height = res[0].height * dpr
47 | ctx.scale(dpr, dpr)
48 | this.lucky = new LuckyGrid({
49 | flag: 'MP-WX',
50 | ctx,
51 | dpr,
52 | width: res[0].width,
53 | height: res[0].height,
54 | // rAF: canvas.requestAnimationFrame, // 帧动画真机调试会报错!
55 | setTimeout,
56 | clearTimeout,
57 | setInterval,
58 | clearInterval,
59 | unitFunc: (num, unit) => changeUnits(num + unit),
60 | afterStart: () => {
61 | // 隐藏图片并显示canvas
62 | this.lucky.draw()
63 | this.setData({
64 | luckyImg: '',
65 | showCanvas: true
66 | })
67 | }
68 | }, {
69 | rows: data.rows,
70 | cols: data.cols,
71 | blocks: data.blocks,
72 | prizes: data.prizes,
73 | buttons: data.buttons,
74 | defaultConfig: data.defaultConfig,
75 | defaultStyle: data.defaultStyle,
76 | activeStyle: data.activeStyle,
77 | start: (...rest) => {
78 | this.triggerEvent('start', ...rest)
79 | },
80 | end: (...rest) => {
81 | this.triggerEvent('end', ...rest)
82 | getImage.call(this).then(res => {
83 | this.setData({ luckyImg: res.tempFilePath })
84 | })
85 | },
86 | })
87 | // 为了保证 onload 回调准确
88 | this.setData({ isShow: true })
89 | })
90 | },
91 | methods: {
92 | imgBindload(e) {
93 | const { name, index, i } = e.currentTarget.dataset
94 | const img = this.data[name][index].imgs[i]
95 | resolveImage(e, img, this.canvas)
96 | },
97 | imgBindloadActive(e) {
98 | const { name, index, i } = e.currentTarget.dataset
99 | const img = this.data[name][index].imgs[i]
100 | resolveImage(e, img, this.canvas, 'activeSrc', '$activeResolve')
101 | },
102 | luckyImgLoad() {
103 | this.setData({ showCanvas: false })
104 | this.lucky.clearCanvas()
105 | },
106 | handleClickOfImg(e) {
107 | const { clientX: x, clientY: y } = e.changedTouches[0]
108 | wx.createSelectorQuery().in(this).select('.lucky-img').fields({
109 | rect: true
110 | }).exec((res) => {
111 | const { left, top } = res[0]
112 | this.toPlay(x - left, y - top)
113 | })
114 | },
115 | handleClickOfCanvas(e) {
116 | const { x, y } = e.changedTouches[0]
117 | this.toPlay(x, y)
118 | },
119 | toPlay(x, y) {
120 | const ctx = this.ctx
121 | this.data.buttons.forEach(btn => {
122 | if (!btn) return
123 | ctx.beginPath()
124 | ctx.rect(...this.lucky.getGeometricProperty([
125 | btn.x,
126 | btn.y,
127 | btn.col || 1,
128 | btn.row || 1
129 | ]))
130 | if (!ctx.isPointInPath(x * this.dpr, y * this.dpr)) return
131 | // 触发抽奖逻辑
132 | this.lucky.startCallback()
133 | })
134 | },
135 | init (...rest) {
136 | this.lucky.init(...rest)
137 | },
138 | play(...rest) {
139 | this.lucky.play(...rest)
140 | },
141 | stop(...rest) {
142 | this.lucky.stop(...rest)
143 | },
144 | },
145 | })
146 |
--------------------------------------------------------------------------------
/packages/mini/src/lucky-grid/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/packages/mini/src/lucky-grid/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/packages/mini/src/lucky-grid/index.wxss:
--------------------------------------------------------------------------------
1 | /* @lucky-canvas/mini/lucky-wheel/index.wxss */
2 | .lucky-box {
3 | position: relative;
4 | overflow: hidden;
5 | margin: 0 auto;
6 | }
7 | .lucky-box .lucky-img {
8 | position: absolute;
9 | left: 0;
10 | top: 0;
11 | z-index: 100;
12 | }
13 | .lucky-box .lucky-canvas {
14 | position: absolute;
15 | left: 0;
16 | top: 0;
17 | z-index: 99;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/mini/src/lucky-wheel/index.js:
--------------------------------------------------------------------------------
1 | import { LuckyWheel } from 'lucky-canvas'
2 | import { changeUnits, resolveImage, getImage } from '../utils'
3 |
4 | Component({
5 | properties: {
6 | width: { type: String, value: '600rpx' },
7 | height: { type: String, value: '600rpx' },
8 | blocks: { type: Array, value: [] },
9 | prizes: { type: Array, value: [] },
10 | buttons: { type: Array, value: [] },
11 | defaultConfig: { type: Object, value: {} },
12 | defaultStyle: { type: Object, value: {} },
13 | start: { type: null, value: () => {} },
14 | end: { type: null, value: () => {} },
15 | },
16 | data: {
17 | lucky: null,
18 | isShow: false,
19 | luckyImg: '',
20 | showCanvas: true,
21 | },
22 | observers: {
23 | 'blocks.**': function (newData, oldData) {
24 | this.lucky && (this.lucky.blocks = newData)
25 | },
26 | 'prizes.**': function (newData, oldData) {
27 | this.lucky && (this.lucky.prizes = newData)
28 | },
29 | 'buttons.**': function (newData, oldData) {
30 | this.lucky && (this.lucky.buttons = newData)
31 | },
32 | },
33 | ready() {
34 | wx.createSelectorQuery().in(this).select('#lucky-wheel').fields({
35 | node: true, size: true
36 | }).exec((res) => {
37 | if (!res[0] || !res[0].node) {
38 | return console.error('lucky-canvas 获取不到 canvas 标签')
39 | }
40 | const canvas = this.canvas = res[0].node
41 | const dpr = this.dpr = wx.getSystemInfoSync().pixelRatio
42 | const ctx = this.ctx = canvas.getContext('2d')
43 | const data = this.data
44 | canvas.width = res[0].width * dpr
45 | canvas.height = res[0].height * dpr
46 | ctx.scale(dpr, dpr)
47 | this.lucky = new LuckyWheel({
48 | flag: 'MP-WX',
49 | ctx,
50 | dpr,
51 | width: res[0].width,
52 | height: res[0].height,
53 | // rAF: canvas.requestAnimationFrame, // 帧动画真机调试会报错!
54 | setTimeout,
55 | clearTimeout,
56 | setInterval,
57 | clearInterval,
58 | unitFunc: (num, unit) => changeUnits(num + unit),
59 | beforeCreate() {
60 | const Radius = Math.min(this.config.width, this.config.height) / 2
61 | // 设置坐标轴
62 | ctx.translate(Radius, Radius)
63 | },
64 | beforeInit() {
65 | // 重置坐标轴
66 | ctx.translate(-this.Radius, -this.Radius)
67 | },
68 | afterStart: () => {
69 | // 隐藏图片并显示canvas
70 | this.lucky.draw()
71 | this.setData({
72 | luckyImg: '',
73 | showCanvas: true
74 | })
75 | }
76 | }, {
77 | blocks: data.blocks,
78 | prizes: data.prizes,
79 | buttons: data.buttons,
80 | defaultConfig: data.defaultConfig,
81 | defaultStyle: data.defaultStyle,
82 | start: (...rest) => {
83 | this.triggerEvent('start', ...rest)
84 | },
85 | end: (...rest) => {
86 | this.triggerEvent('end', ...rest)
87 | getImage.call(this).then(res => {
88 | this.setData({ luckyImg: res.tempFilePath })
89 | })
90 | },
91 | })
92 | // 为了保证 onload 回调准确
93 | this.setData({ isShow: true })
94 | })
95 | },
96 | methods: {
97 | imgBindload(e) {
98 | const { name, index, i } = e.currentTarget.dataset
99 | const img = this.data[name][index].imgs[i]
100 | resolveImage(e, img, this.canvas)
101 | },
102 | luckyImgLoad() {
103 | this.setData({ showCanvas: false })
104 | this.lucky.clearCanvas()
105 | },
106 | handleClickOfImg(e) {
107 | const { clientX: x, clientY: y } = e.changedTouches[0]
108 | wx.createSelectorQuery().in(this).select('.lucky-img').fields({
109 | rect: true
110 | }).exec((res) => {
111 | const { left, top } = res[0]
112 | this.toPlay(x - left, y - top)
113 | })
114 | },
115 | handleClickOfCanvas(e) {
116 | const { x, y } = e.changedTouches[0]
117 | this.toPlay(x, y)
118 | },
119 | toPlay(x, y) {
120 | const ctx = this.ctx
121 | ctx.beginPath()
122 | ctx.arc(0, 0, this.lucky.maxBtnRadius, 0, Math.PI * 2, false)
123 | if (!ctx.isPointInPath(x * this.dpr, y * this.dpr)) return
124 | // 触发抽奖逻辑
125 | this.lucky.startCallback()
126 | },
127 | init (...rest) {
128 | this.lucky.init(...rest)
129 | },
130 | play(...rest) {
131 | this.lucky.play(...rest)
132 | },
133 | stop(...rest) {
134 | this.lucky.stop(...rest)
135 | },
136 | },
137 | })
138 |
--------------------------------------------------------------------------------
/packages/mini/src/lucky-wheel/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/packages/mini/src/lucky-wheel/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/mini/src/lucky-wheel/index.wxss:
--------------------------------------------------------------------------------
1 | /* @lucky-canvas/mini/lucky-wheel/index.wxss */
2 | .lucky-box {
3 | position: relative;
4 | overflow: hidden;
5 | margin: 0 auto;
6 | }
7 | .lucky-box .lucky-img {
8 | position: absolute;
9 | left: 0;
10 | top: 0;
11 | z-index: 100;
12 | }
13 | .lucky-box .lucky-canvas {
14 | position: absolute;
15 | left: 0;
16 | top: 0;
17 | z-index: 99;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/mini/src/slot-machine/index.js:
--------------------------------------------------------------------------------
1 | import { SlotMachine } from 'lucky-canvas'
2 | import { changeUnits, resolveImage, getImage } from '../utils'
3 |
4 | Component({
5 | properties: {
6 | width: { type: String, value: '600rpx' },
7 | height: { type: String, value: '600rpx' },
8 | blocks: { type: Array, value: [] },
9 | prizes: { type: Array, value: [] },
10 | slots: { type: Array, value: [] },
11 | defaultConfig: { type: Object, value: {} },
12 | defaultStyle: { type: Object, value: {} },
13 | end: { type: null, value: () => {} },
14 | },
15 | data: {
16 | lucky: null,
17 | isShow: false,
18 | luckyImg: '',
19 | showCanvas: true,
20 | },
21 | observers: {
22 | 'blocks.**': function (newData, oldData) {
23 | this.lucky && (this.lucky.blocks = newData)
24 | },
25 | 'prizes.**': function (newData, oldData) {
26 | this.lucky && (this.lucky.prizes = newData)
27 | },
28 | 'slots.**': function (newData, oldData) {
29 | this.lucky && (this.lucky.slots = newData)
30 | },
31 | },
32 | ready() {
33 | wx.createSelectorQuery().in(this).select('#slot-machine').fields({
34 | node: true, size: true
35 | }).exec((res) => {
36 | if (!res[0] || !res[0].node) {
37 | return console.error('lucky-canvas 获取不到 canvas 标签')
38 | }
39 | const canvas = this.canvas = res[0].node
40 | const dpr = this.dpr = wx.getSystemInfoSync().pixelRatio
41 | const ctx = this.ctx = canvas.getContext('2d')
42 | const data = this.data
43 | canvas.width = res[0].width * dpr
44 | canvas.height = res[0].height * dpr
45 | ctx.scale(dpr, dpr)
46 | this.lucky = new SlotMachine({
47 | flag: 'MP-WX',
48 | ctx,
49 | dpr,
50 | offscreenCanvas: wx.createOffscreenCanvas({ type: '2d', width: 300, height: 150 }),
51 | // rAF: canvas.requestAnimationFrame, // 帧动画真机调试会报错!
52 | setTimeout,
53 | clearTimeout,
54 | setInterval,
55 | clearInterval,
56 | unitFunc: (num, unit) => changeUnits(num + unit),
57 | afterStart: () => {
58 | // 隐藏图片并显示canvas
59 | this.lucky.draw()
60 | this.setData({
61 | luckyImg: '',
62 | showCanvas: true
63 | })
64 | }
65 | }, {
66 | width: res[0].width,
67 | height: res[0].height,
68 | blocks: data.blocks,
69 | prizes: data.prizes,
70 | slots: data.slots,
71 | defaultConfig: data.defaultConfig,
72 | defaultStyle: data.defaultStyle,
73 | end: (...rest) => {
74 | this.triggerEvent('end', ...rest)
75 | getImage.call(this).then(res => {
76 | this.setData({ luckyImg: res.tempFilePath })
77 | })
78 | },
79 | })
80 | // 为了保证 onload 回调准确
81 | this.setData({ isShow: true })
82 | })
83 | },
84 | methods: {
85 | imgBindload(e) {
86 | const { name, index, i } = e.currentTarget.dataset
87 | const img = this.data[name][index].imgs[i]
88 | resolveImage(e, img, this.canvas)
89 | },
90 | luckyImgLoad() {
91 | this.setData({ showCanvas: false })
92 | this.lucky.clearCanvas()
93 | },
94 | init (...rest) {
95 | this.lucky.init(...rest)
96 | },
97 | play(...rest) {
98 | this.lucky.play(...rest)
99 | },
100 | stop(...rest) {
101 | this.lucky.stop(...rest)
102 | },
103 | },
104 | })
105 |
--------------------------------------------------------------------------------
/packages/mini/src/slot-machine/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/packages/mini/src/slot-machine/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/mini/src/slot-machine/index.wxss:
--------------------------------------------------------------------------------
1 | /* @lucky-canvas/mini/slot-machine/index.wxss */
2 | .lucky-box {
3 | position: relative;
4 | overflow: hidden;
5 | margin: 0 auto;
6 | }
7 | .lucky-box .lucky-img {
8 | position: absolute;
9 | left: 0;
10 | top: 0;
11 | z-index: 100;
12 | }
13 | .lucky-box .lucky-canvas {
14 | position: absolute;
15 | left: 0;
16 | top: 0;
17 | z-index: 99;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/mini/src/utils.js:
--------------------------------------------------------------------------------
1 | const windowWidth = wx.getSystemInfoSync().windowWidth
2 |
3 | export const rpx2px = (value) => {
4 | if (typeof value === 'string') value = Number(value.replace(/[a-z]*/g, ''))
5 | return windowWidth / 750 * value
6 | }
7 |
8 | export const changeUnits = (value) => {
9 | value = String(value)
10 | return Number(value.replace(/^(\-*[0-9.]*)([a-z%]*)$/, (value, num, unit) => {
11 | switch (unit) {
12 | case 'px':
13 | num *= 1
14 | break
15 | case 'rpx':
16 | num = rpx2px(num)
17 | break
18 | default:
19 | num *= 1
20 | break
21 | }
22 | return num
23 | }))
24 | }
25 |
26 | export const resolveImage = (e, img, canvas, srcName = 'src', resolveName = '$resolve') => {
27 | const imgObj = canvas.createImage()
28 | imgObj.onload = () => {
29 | img[resolveName](imgObj)
30 | }
31 | imgObj.src = img[srcName]
32 | }
33 |
34 | export function getImage() {
35 | return new Promise((resolve, reject) => {
36 | wx.canvasToTempFilePath({
37 | canvas: this.canvas,
38 | success: res => resolve(res),
39 | fail: err => reject(err)
40 | })
41 | })
42 | }
43 |
--------------------------------------------------------------------------------
/packages/mini/test/index.test.js:
--------------------------------------------------------------------------------
1 | const _ = require('./utils')
2 |
3 | test('render', async () => {
4 | const componentId = _.load('index', 'comp')
5 | const component = _.render(componentId, {prop: 'index.test.properties'})
6 |
7 | const parent = document.createElement('parent-wrapper')
8 | component.attach(parent)
9 |
10 | expect(_.match(component.dom, 'index.test.properties-falseother.properties-other')).toBe(true)
11 |
12 | await _.sleep(10)
13 |
14 | expect(_.match(component.dom, 'index.test.properties-trueother.properties-other')).toBe(true)
15 | })
16 |
--------------------------------------------------------------------------------
/packages/mini/test/utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const simulate = require('miniprogram-simulate')
4 | const config = require('../tools/config')
5 |
6 | // const dir = config.srcPath // 使用源码进行测试,对于 debug 和代码覆盖率检测会比较友好
7 | const dir = config.distPath // 使用构建后代码进行测试,如果使用了 typescript 进行开发,必须选择此目录
8 |
9 | try {
10 | fs.accessSync(dir)
11 | } catch (err) {
12 | console.error('请先执行 npm run build 再进行单元测试!!!')
13 | }
14 |
15 | const oldLoad = simulate.load
16 | simulate.load = function (componentPath, ...args) {
17 | if (typeof componentPath === 'string') componentPath = path.join(dir, componentPath)
18 | return oldLoad(componentPath, ...args)
19 | }
20 |
21 | module.exports = simulate
22 |
23 | // adjust the simulated wx api
24 | const oldGetSystemInfoSync = global.wx.getSystemInfoSync
25 | global.wx.getSystemInfoSync = function() {
26 | const res = oldGetSystemInfoSync()
27 | res.SDKVersion = '2.4.1'
28 |
29 | return res
30 | }
31 |
--------------------------------------------------------------------------------
/packages/mini/test/wx.test.js:
--------------------------------------------------------------------------------
1 | const _ = require('./utils')
2 |
3 | test('wx.getSystemInfo', async () => {
4 | wx.getSystemInfo({
5 | success(res) {
6 | expect(res.errMsg).toBe('getSystemInfo:ok')
7 | },
8 | complete(res) {
9 | expect(res.errMsg).toBe('getSystemInfo:ok')
10 | },
11 | })
12 | })
13 |
14 | test('wx.getSystemInfoSync', async () => {
15 | const info = wx.getSystemInfoSync()
16 | expect(info.SDKVersion).toBe('2.4.1')
17 | expect(info.version).toBe('6.6.3')
18 | })
19 |
--------------------------------------------------------------------------------
/packages/mini/tools/checkcomponents.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const _ = require('./utils')
4 | const config = require('./config')
5 |
6 | const srcPath = config.srcPath
7 | let hasCheckCompoenntMap = {}
8 |
9 | /**
10 | * 获取 json 路径相关信息
11 | */
12 | function getJsonPathInfo(jsonPath) {
13 | const dirPath = path.dirname(jsonPath)
14 | const fileName = path.basename(jsonPath, '.json')
15 | const relative = path.relative(srcPath, dirPath)
16 | const fileBase = path.join(relative, fileName)
17 |
18 | return {
19 | dirPath, fileName, relative, fileBase
20 | }
21 | }
22 |
23 | /**
24 | * 检测是否包含其他自定义组件
25 | */
26 | const checkProps = ['usingComponents', 'componentGenerics']
27 | async function checkIncludedComponents(jsonPath, componentListMap) {
28 | const json = _.readJson(jsonPath)
29 | if (!json) throw new Error(`json is not valid: "${jsonPath}"`)
30 |
31 | const {dirPath, fileName, fileBase} = getJsonPathInfo(jsonPath)
32 | if (hasCheckCompoenntMap[fileBase]) return
33 | hasCheckCompoenntMap[fileBase] = true
34 |
35 | for (let i = 0, len = checkProps.length; i < len; i++) {
36 | const checkProp = checkProps[i]
37 | const checkPropValue = json[checkProp] || {}
38 | const keys = Object.keys(checkPropValue)
39 |
40 | for (let j = 0, jlen = keys.length; j < jlen; j++) {
41 | const key = keys[j]
42 | let value = typeof checkPropValue[key] === 'object' ? checkPropValue[key].default : checkPropValue[key]
43 | if (!value || typeof value === 'boolean') continue
44 |
45 | value = _.transformPath(value, path.sep)
46 |
47 | // 检查相对路径
48 | const componentPath = `${path.join(dirPath, value)}.json`
49 | const isExists = await _.checkFileExists(componentPath)
50 | if (isExists) {
51 | await checkIncludedComponents(componentPath, componentListMap)
52 | }
53 | }
54 | }
55 |
56 | const wholeFileBase = path.join(dirPath, fileName)
57 | let jsExt = '.js'
58 | const isJsFileExists = await _.checkFileExists(wholeFileBase + '.ts')
59 | if (isJsFileExists) {
60 | jsExt = '.ts'
61 | }
62 |
63 | // 进入存储
64 | componentListMap.wxmlFileList.push(`${fileBase}.wxml`)
65 | componentListMap.wxssFileList.push(`${fileBase}.wxss`)
66 | componentListMap.jsonFileList.push(`${fileBase}.json`)
67 | componentListMap.jsFileList.push(`${fileBase}${jsExt}`)
68 |
69 | componentListMap.jsFileMap[fileBase] = `${wholeFileBase}${jsExt}`
70 | }
71 |
72 | module.exports = async function (entry) {
73 | const componentListMap = {
74 | wxmlFileList: [],
75 | wxssFileList: [],
76 | jsonFileList: [],
77 | jsFileList: [],
78 |
79 | jsFileMap: {}, // 为 webpack entry 所用
80 | }
81 |
82 | const isExists = await _.checkFileExists(entry)
83 | if (!isExists) {
84 | const {dirPath, fileName, fileBase} = getJsonPathInfo(entry)
85 |
86 | const wholeFileBase = path.join(dirPath, fileName)
87 | let jsExt = '.js'
88 | const isJsFileExists = await _.checkFileExists(wholeFileBase + '.ts')
89 | if (isJsFileExists) {
90 | jsExt = '.ts'
91 | }
92 | componentListMap.jsFileList.push(`${fileBase}${jsExt}`)
93 | componentListMap.jsFileMap[fileBase] = `${wholeFileBase}${jsExt}`
94 |
95 | return componentListMap
96 | }
97 |
98 | hasCheckCompoenntMap = {}
99 | await checkIncludedComponents(entry, componentListMap)
100 |
101 | return componentListMap
102 | }
103 |
--------------------------------------------------------------------------------
/packages/mini/tools/checkwxss.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const through = require('through2')
3 | const Vinyl = require('vinyl')
4 |
5 | const _ = require('./utils')
6 |
7 | /**
8 | * 获取 import 列表
9 | */
10 | function getImportList(wxss, filePath) {
11 | const reg = /@import\s+(?:(?:"([^"]+)")|(?:'([^']+)'));/ig
12 | const importList = []
13 | let execRes = reg.exec(wxss)
14 |
15 | while (execRes && (execRes[1] || execRes[2])) {
16 | importList.push({
17 | code: execRes[0],
18 | path: path.join(path.dirname(filePath), execRes[1] || execRes[2]),
19 | })
20 | execRes = reg.exec(wxss)
21 | }
22 |
23 | return importList
24 | }
25 |
26 | /**
27 | * 获取 wxss 内容
28 | */
29 | async function getContent(wxss, filePath, cwd) {
30 | let importList = []
31 |
32 | if (wxss) {
33 | const currentImportList = getImportList(wxss, filePath)
34 |
35 | for (const item of currentImportList) {
36 | // 替换掉 import 语句,不让 less 编译
37 | wxss = wxss.replace(item.code, `/* *updated for miniprogram-custom-component* ${item.code} */`)
38 |
39 | // 处理依赖的 wxss
40 | const importWxss = await _.readFile(item.path)
41 | const importInfo = await getContent(importWxss, item.path, cwd)
42 |
43 | // 获取依赖列表
44 | importList.push(new Vinyl({
45 | cwd,
46 | path: item.path,
47 | contents: Buffer.from(importInfo.wxss, 'utf8'),
48 | }))
49 | importList = importList.concat(importInfo.importList)
50 | }
51 | }
52 |
53 | return {
54 | wxss,
55 | importList,
56 | }
57 | }
58 |
59 | module.exports = {
60 | start() {
61 | return through.obj(function (file, enc, cb) {
62 | if (file.isBuffer()) {
63 | getContent(file.contents.toString('utf8'), file.path, file.cwd).then(res => {
64 | const {wxss, importList} = res
65 |
66 | importList.forEach(importFile => this.push(importFile))
67 |
68 | file.contents = Buffer.from(wxss, 'utf8')
69 | this.push(file)
70 | // eslint-disable-next-line promise/no-callback-in-promise
71 | cb()
72 | }).catch(err => {
73 | // eslint-disable-next-line no-console
74 | console.warn(`deal with ${file.path} failed: ${err.stack}`)
75 | this.push(file)
76 | // eslint-disable-next-line promise/no-callback-in-promise
77 | cb()
78 | })
79 | } else {
80 | this.push(file)
81 | cb()
82 | }
83 | })
84 | },
85 |
86 | end() {
87 | return through.obj(function (file, enc, cb) {
88 | if (file.isBuffer) {
89 | const reg = /\/\*\s\*updated for miniprogram-custom-component\*\s(@import\s+(?:(?:"([^"]+)")|(?:'([^"]+)'));)\s\*\//ig
90 | const wxss = file.contents.toString('utf8').replace(reg, (all, $1) => $1)
91 |
92 | file.contents = Buffer.from(wxss, 'utf8')
93 | this.push(file)
94 | cb()
95 | } else {
96 | this.push(file)
97 | cb()
98 | }
99 | })
100 | },
101 | }
102 |
--------------------------------------------------------------------------------
/packages/mini/tools/config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const webpack = require('webpack')
4 | const nodeExternals = require('webpack-node-externals')
5 |
6 | const isDev = process.argv.indexOf('--develop') >= 0
7 | const isWatch = process.argv.indexOf('--watch') >= 0
8 | const demoSrc = path.resolve(__dirname, './demo')
9 | const demoDist = path.resolve(__dirname, '../dev')
10 | const src = path.resolve(__dirname, '../src')
11 | const dev = path.join(demoDist, 'components')
12 | const dist = path.resolve(__dirname, '../dist')
13 |
14 | module.exports = {
15 | entry: ['lucky-wheel/index', 'lucky-grid/index', 'slot-machine/index'],
16 |
17 | isDev,
18 | isWatch,
19 | srcPath: src, // 源目录
20 | distPath: isDev ? dev : dist, // 目标目录
21 |
22 | demoSrc, // demo 源目录
23 | demoDist, // demo 目标目录
24 |
25 | wxss: {
26 | less: false, // 使用 less 来编写 wxss
27 | sourcemap: false, // 生成 less sourcemap
28 | },
29 |
30 | js: {
31 | webpack: true, // 使用 webpack 来构建 js
32 | },
33 |
34 | webpack: {
35 | mode: 'production',
36 | output: {
37 | filename: '[name].js',
38 | libraryTarget: 'commonjs2',
39 | },
40 | target: 'node',
41 | externals: [nodeExternals()], // 忽略 node_modules
42 | module: {
43 | rules: [{
44 | test: /\.js$/i,
45 | use: [{
46 | loader: 'thread-loader',
47 | }, {
48 | loader: 'babel-loader',
49 | options: {
50 | cacheDirectory: true,
51 | },
52 | }],
53 | exclude: /node_modules/
54 | }, {
55 | test: /\.ts$/,
56 | exclude: /node_modules/,
57 | use: [{
58 | loader: 'thread-loader',
59 | }, {
60 | loader: 'babel-loader',
61 | options: {
62 | cacheDirectory: true,
63 | },
64 | }, {
65 | loader: 'ts-loader',
66 | options: {
67 | appendTsSuffixTo: [/\.vue$/],
68 | happyPackMode: true,
69 | },
70 | }],
71 | }],
72 | },
73 | resolve: {
74 | modules: [src, 'node_modules', path.join(__dirname, 'node_modules')],
75 | extensions: ['.js', '.json'],
76 | },
77 | plugins: [
78 | new webpack.DefinePlugin({}),
79 | new webpack.optimize.LimitChunkCountPlugin({maxChunks: 1}),
80 | ],
81 | optimization: {
82 | minimize: false,
83 | },
84 | devtool: 'source-map', // 生成 js sourcemap
85 | performance: {
86 | hints: 'warning',
87 | assetFilter: assetFilename => assetFilename.endsWith('.js')
88 | }
89 | },
90 |
91 | copy: [], // 将会复制到目标目录
92 | }
93 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/app.js:
--------------------------------------------------------------------------------
1 | App({})
2 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index",
4 | "pages/yyjk/index",
5 | "pages/ymc/index",
6 | "pages/cjl/index",
7 | "pages/yx/index",
8 | "pages/yyx/index",
9 | "pages/jd/index",
10 | "pages/xdf/index",
11 | "pages/xc/index"
12 | ],
13 | "window":{
14 | "backgroundTextStyle":"light",
15 | "navigationBarBackgroundColor": "#dc415f",
16 | "navigationBarTitleText": "lucky-canvas 抽奖示例",
17 | "navigationBarTextStyle":"white"
18 | },
19 | "usingComponents": {
20 | "lucky-wheel": "./components/lucky-wheel/index",
21 | "lucky-grid": "./components/lucky-grid/index",
22 | "slot-machine": "./components/slot-machine/index"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/app.wxss:
--------------------------------------------------------------------------------
1 | .box {
2 | width: 100%;
3 | min-height: 100vh;
4 | position: relative;
5 | background-color: #dc415f;
6 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 100 100'%3E%3Crect x='0' y='0' width='57' height='57' fill='%23ce3250'/%3E%3C/svg%3E");
7 | color: #fe95a8;
8 | }
9 | .my-lucky {
10 | margin-top: 100rpx;
11 | width: 600rpx;
12 | position: absolute;
13 | left: 50%;
14 | transform: translate(-50%);
15 | }
16 | .mask {
17 | width: 100%;
18 | height: 100%;
19 | background: rgba(0, 0, 0, 0.5);
20 | position: fixed;
21 | top: 0;
22 | left: 0;
23 | z-index: 999;
24 | }
25 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/package.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/cjl/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | prizes: [],
5 | buttons: [{
6 | radius: '40px',
7 | imgs: [{ src: imgs['button.png'], width: '105%', top: '-180%' }]
8 | }],
9 | blocks: [
10 | { padding: '20px', imgs: [{ src: imgs['bg.png'], width: '100%' }] },
11 | { padding: '1px', background: '#fa3e3f' },
12 | { padding: '10px', background: '#f9d400' },
13 | { padding: '1px', background: '#e76f51' },
14 | ],
15 | defaultStyle: {
16 | fontColor: '#303133',
17 | fontSize: '10px',
18 | },
19 | defaultConfig: {
20 | gutter: '1px',
21 | },
22 | },
23 | onReady () {
24 | this.getPrizesList()
25 | },
26 | getPrizesList () {
27 | const prizes = []
28 | let data = [
29 | { name: '谢谢参与', img: imgs['0.png'], color: '#d7d7d7' },
30 | { name: '礼物', img: imgs['1.png'], color: '#fef43e' },
31 | { name: '抽奖券', img: imgs['2.png'], color: '#ef7683' },
32 | { name: '元宝', img: imgs['3.png'], color: '#d7d7d7' },
33 | { name: '元宝', img: imgs['4.png'], color: '#fef43e' },
34 | { name: '抽奖券', img: imgs['2.png'], color: '#ef7683' },
35 | { name: '元宝', img: imgs['6.png'], color: '#d7d7d7' },
36 | { name: '抽奖券', img: imgs['2.png'], color: '#fef43e' },
37 | ]
38 | data.forEach((item, index) => {
39 | prizes.push({
40 | name: item.name,
41 | background: item.color,
42 | fonts: [{ text: item.name, top: '10%' }],
43 | imgs:[{ src: item.img, width: '30%', top: '35%' }],
44 | })
45 | })
46 | this.setData({
47 | prizes
48 | })
49 | },
50 | wheelStart () {
51 | // 获取抽奖组件实例
52 | const child = this.selectComponent('#lucky-wheel')
53 | // 调用play方法开始旋转
54 | child.play()
55 | // 用定时器模拟请求接口
56 | setTimeout(() => {
57 | // 3s 后得到中奖索引
58 | const index = Math.random() * 8 >> 0
59 | // 调用stop方法然后缓慢停止
60 | child.stop(index)
61 | }, 3000)
62 | },
63 | wheelEnd (event) {
64 | // 中奖奖品详情
65 | wx.showModal({
66 | title: '提示',
67 | content: '恭喜你获得大奖: ' + event.detail.name,
68 | success (res) {}
69 | })
70 | },
71 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/cjl/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/cjl/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/cjl/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/cjl/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/index/index.js:
--------------------------------------------------------------------------------
1 | Page({
2 | data: {
3 | prizes: [],
4 | wheel: {
5 | defaultStyle: {
6 | fontColor: '#ff625b',
7 | fontSize: '16px'
8 | },
9 | blocks: [
10 | { padding: '20rpx', background: '#ffc27a' },
11 | { padding: '10rpx', background: '#ff4a4c' },
12 | ],
13 | buttons: [
14 | { radius: '50px', background: '#ff4a4c' },
15 | { radius: '45px', background: '#fff' },
16 | { radius: '41px', background: '#f6c66f', pointer: true },
17 | {
18 | radius: '35px', background: '#ffdea0',
19 | imgs: [{ src: 'https://100px.net/assets/img/button.2f4ac3e9.png', width: '70%', top: '-55%' }]
20 | }
21 | ],
22 | },
23 | grid: {
24 | rows: 3,
25 | cols: 4,
26 | blocks: [{ padding: '5px', background: '#ff4a4c', borderRadius: 10 }],
27 | prizes: [
28 | { x: 0, y: 0, fonts: [{ text: '0元', top: 20 }], imgs: [{ src: 'https://100px.net/assets/img/4.1349538d.png', activeSrc: 'https://100px.net/assets/img/00.a8cd98c2.png', width: '50%' }] },
29 | { x: 1, y: 0, fonts: [{ text: '1元', top: 20 }] },
30 | { x: 2, y: 0, fonts: [{ text: '2元', top: 20 }] },
31 | { x: 3, y: 0, fonts: [{ text: '3元', top: 20 }] },
32 | { x: 3, y: 1, fonts: [{ text: '4元', top: 20 }] },
33 | { x: 3, y: 2, fonts: [{ text: '5元', top: 20 }] },
34 | { x: 2, y: 2, fonts: [{ text: '6元', top: 20 }] },
35 | { x: 1, y: 2, fonts: [{ text: '7元', top: 20 }] },
36 | { x: 0, y: 2, fonts: [{ text: '8元', top: 20 }] },
37 | { x: 0, y: 1, fonts: [{ text: '9元', top: 20 }] }
38 | ],
39 | buttons: [{ x: 1, y: 1, col: 2, fonts: [{ text: '抽奖按钮', top: 20 }], imgs: [{ src: 'https://100px.net/assets/img/4.1349538d.png', width: '50%' }] }],
40 | }
41 | },
42 | onReady () {
43 | this.setData({
44 | prizes: []
45 | });
46 | setTimeout(() => {
47 | [
48 | { title: '1元红包', background: '#ffd185', fonts: [{ text: '1元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/0.efbe4dff.png', width: '35%', top: '35%' }] },
49 | { title: '100元红包', background: '#f9e3bb', fonts: [{ text: '100元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/1.de299995.png', width: '35%', top: '35%' }] },
50 | { title: '0.5元红包', background: '#ffd185', fonts: [{ text: '0.5元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/2.8f1949c9.png', width: '35%', top: '35%' }] },
51 | { title: '2元红包', background: '#f9e3bb', fonts: [{ text: '2元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/3.9307595d.png', width: '35%', top: '35%' }] },
52 | { title: '10元红包', background: '#ffd185', fonts: [{ text: '10元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/4.1349538d.png', width: '35%', top: '35%' }] },
53 | { title: '50元红包', background: '#f9e3bb', fonts: [{ text: '50元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/5.b92ceb2f.png', width: '35%', top: '35%' }] },
54 | { title: '0.3元红包', background: '#ffd185', fonts: [{ text: '0.3元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/6.02483a09.png', width: '35%', top: '35%' }] },
55 | { title: '5元红包', background: '#f9e3bb', fonts: [{ text: '5元红包', top: '10%' }], imgs: [{ src: 'https://100px.net/assets/img/7.48cda152.png', width: '35%', top: '35%' }] },
56 | ].forEach(_ => {
57 | this.setData({
58 | prizes: [...this.data.prizes, _]
59 | });
60 | })
61 | }, 1000);
62 | },
63 | wheelStart () {
64 | // 获取抽奖组件实例
65 | const child = this.selectComponent('#lucky-wheel')
66 | // 调用play方法开始旋转
67 | child.play()
68 | // 用定时器模拟请求接口
69 | setTimeout(() => {
70 | // 3s 后得到中奖索引
71 | const index = Math.random() * 6 >> 0
72 | // 调用stop方法然后缓慢停止
73 | child.stop(index)
74 | }, 3000)
75 | },
76 | wheelEnd (event) {
77 | // 中奖奖品详情
78 | console.log(event.detail)
79 | },
80 | gridStart () {
81 | // 获取抽奖组件实例
82 | const child = this.selectComponent('#lucky-grid')
83 | // 调用play方法开始旋转
84 | child.play()
85 | // 用定时器模拟请求接口
86 | setTimeout(() => {
87 | // 3s 后得到中奖索引
88 | const index = Math.random() * 6 >> 0
89 | // 调用stop方法然后缓慢停止
90 | child.stop(index)
91 | }, 3000)
92 | },
93 | gridEnd (event) {
94 | // 中奖奖品详情
95 | console.log(event.detail)
96 | }
97 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 该小程序为示例 demo | 官网: 100px.net
4 |
5 |
6 | 大转盘默认 Demo
7 | 九宫格默认 Demo
8 | 仿抽奖乐大转盘
9 | 仿网易严选九宫格
10 | 仿携程转盘抽奖
11 |
12 | 仿网易云游戏九宫格
13 | 仿京东大转盘抽京豆
14 | 仿新东方小程序抽奖
15 |
16 |
17 |
18 | 本插件开源免费, 如果用着顺手可以 github 上面点个 star 支持我一下
19 | Github: 官网右上角有链接
20 |
21 |
22 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .box {
2 | background-color: #dc415f;
3 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 100 100'%3E%3Crect x='0' y='0' width='57' height='57' fill='%23ce3250'/%3E%3C/svg%3E");
4 | color: #fe95a8;
5 | min-height: 100vh;
6 | }
7 | .center {
8 | text-align: center;
9 | }
10 | .ul {
11 | padding: 0 50rpx;
12 | display: flex;
13 | justify-content: space-between;
14 | flex-wrap: wrap;
15 | }
16 | .list {
17 | color: #ff4a4c;
18 | text-align: center;
19 | width: 48%;
20 | padding: 0rpx 0;
21 | margin: 20rpx 0;
22 | background: #f9e3bb;
23 | border-radius: 10rpx;
24 | box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
25 |
26 | }
27 | navigator {
28 | display: block;
29 | width: 100%;
30 | height: 100%;
31 | padding: 20rpx 0;
32 | font-size: 30rpx;
33 | }
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/jd/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | blocks: [
5 | { padding: '30px', imgs: [{ src: imgs['bg.png'], width: '100%' }] },
6 | ],
7 | prizes: [],
8 | buttons: [
9 | { radius: '40px', imgs: [{ src: imgs['button.png'], width: '140%', top: '-140%' }] },
10 | { radius: '15%', imgs: [{ src: imgs['btn.png'], width: '100%', top: '-830%' }] }
11 | ],
12 | defaultStyle: {
13 | fontColor: '#000',
14 | fontSize: '13px',
15 | fontStyle: 'SimHei',
16 | },
17 | defaultConfig: {
18 | offsetDegree: 22.5
19 | },
20 | },
21 | onReady () {
22 | this.getPrizesList()
23 | },
24 | getPrizesList () {
25 | const prizes = []
26 | let data = [
27 | { name: '10个京豆', img: imgs['1.png'], color: '#F8DEF8' },
28 | { name: '5个京豆', img: imgs['1.png'], color: '#FEF3FC' },
29 | { name: '1个京豆', img: imgs['1.png'], color: '#F8DEF8' },
30 | { name: '谢谢参与', img: imgs['0.png'], color: '#FEF3FC' },
31 | { name: '10个京豆', img: imgs['1.png'], color: '#F8DEF8' },
32 | { name: '5个京豆', img: imgs['1.png'], color: '#FEF3FC' },
33 | { name: '1个京豆', img: imgs['1.png'], color: '#F8DEF8' },
34 | { name: '谢谢参与', img: imgs['0.png'], color: '#FEF3FC' },
35 | ]
36 | data.forEach((item, index) => {
37 | prizes.push({
38 | name: item.name,
39 | background: item.color,
40 | fonts: [{ text: item.name, top: '12%' }],
41 | imgs:[{ src: item.img, width: '45%', top: '35%' }],
42 | })
43 | })
44 | this.setData({
45 | prizes
46 | })
47 | },
48 | wheelStart () {
49 | // 获取抽奖组件实例
50 | const child = this.selectComponent('#lucky-wheel')
51 | // 调用play方法开始旋转
52 | child.play()
53 | // 用定时器模拟请求接口
54 | setTimeout(() => {
55 | // 3s 后得到中奖索引
56 | const index = Math.random() * 8 >> 0
57 | // 调用stop方法然后缓慢停止
58 | child.stop(index)
59 | }, 3000)
60 | },
61 | wheelEnd (event) {
62 | // 中奖奖品详情
63 | wx.showModal({
64 | title: '提示',
65 | content: '恭喜你获得大奖: ' + event.detail.name,
66 | success (res) {}
67 | })
68 | },
69 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/jd/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/jd/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/jd/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/jd/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/slot/index.js:
--------------------------------------------------------------------------------
1 | Page({
2 | data: {
3 | maskShow: false,
4 | prizes: [],
5 | slots: [],
6 | defaultStyle: {
7 | fontColor: '#ff625b',
8 | fontSize: '16px'
9 | },
10 | blocks: [
11 | { padding: '20rpx', background: '#ffc27a' },
12 | { padding: '10rpx', background: '#ff4a4c' },
13 | ],
14 | },
15 | onReady () {
16 | this.setData({
17 | prizes: []
18 | });
19 | setTimeout(() => {
20 | [
21 | {
22 | title: '1元红包', background: '#ffd185', fonts: [{ text: '1元红包', top: '10%' }],
23 | },
24 | {
25 | title: '100元红包', background: '#f9e3bb', fonts: [{ text: '100元红包', top: '10%' }],
26 | },
27 | {
28 | title: '0.5元红包', background: '#ffd185', fonts: [{ text: '0.5元红包', top: '10%' }],
29 | },
30 | {
31 | title: '2元红包', background: '#f9e3bb', fonts: [{ text: '2元红包', top: '10%' }],
32 | },
33 | {
34 | title: '10元红包', background: '#ffd185', fonts: [{ text: '10元红包', top: '10%' }],
35 | },
36 | {
37 | title: '50元红包', background: '#f9e3bb', fonts: [{ text: '50元红包', top: '10%' }],
38 | },
39 | {
40 | title: '0.3元红包', background: '#ffd185', fonts: [{ text: '0.3元红包', top: '10%' }],
41 | },
42 | {
43 | title: '5元红包', background: '#f9e3bb', fonts: [{ text: '5元红包', top: '10%' }],
44 | },
45 | ].forEach(_ => {
46 | this.setData({
47 | prizes: [...this.data.prizes, _]
48 | });
49 | })
50 | this.setData({
51 | slots: [
52 | {},
53 | { direction: -1 },
54 | {},
55 | ]
56 | })
57 | }, 50);
58 | },
59 | handleStart () {
60 | // 获取抽奖组件实例
61 | const child = this.selectComponent('#slot-machine')
62 | // 调用play方法开始旋转
63 | child.play()
64 | // 用定时器模拟请求接口
65 | setTimeout(() => {
66 | // 3s 后得到中奖索引
67 | const index = Math.random() * 6 >> 0
68 | // 调用stop方法然后缓慢停止
69 | child.stop(index)
70 | }, 3000)
71 | },
72 | handleEnd (event) {
73 | // 中奖奖品详情
74 | this.setData({ maskShow: true })
75 | wx.showModal({
76 | title: '提示',
77 | content: '恭喜你获得大奖: ' + event.detail.title,
78 | success (res) {}
79 | })
80 | },
81 | maskHidden () {
82 | this.setData({ maskShow: false })
83 | },
84 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/slot/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/slot/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 | 开始111
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/slot/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/slot/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xc/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | prizes: [
5 | { name: '免费住酒店' },
6 | { name: '房型升级' },
7 | { name: '免费取消' },
8 | { name: '延迟退房' },
9 | { name: '免费早餐' },
10 | { name: '明天再来' },
11 | ],
12 | buttons: [{
13 | radius: '25%',
14 | fonts: [{ text: '开始\n抽奖', fontColor: '#fff', top: '-50%', fontSize: '16px' }],
15 | imgs: [{ src: imgs['btn.png'], width: '120%', top: '-175%' }]
16 | }],
17 | blocks: [
18 | { imgs: [{ src: imgs['bg.png'], width: '100%', rotate: true }] },
19 | ],
20 | defaultConfig: {
21 | offsetDegree: 30
22 | },
23 | },
24 | onReady () {
25 | },
26 | wheelStart () {
27 | // 获取抽奖组件实例
28 | const child = this.selectComponent('#lucky-wheel')
29 | // 调用play方法开始旋转
30 | child.play()
31 | // 用定时器模拟请求接口
32 | setTimeout(() => {
33 | // 3s 后得到中奖索引
34 | const index = Math.random() * 8 >> 0
35 | // 调用stop方法然后缓慢停止
36 | child.stop(index)
37 | }, 3000)
38 | },
39 | wheelEnd (event) {
40 | // 中奖奖品详情
41 | wx.showModal({
42 | title: '提示',
43 | content: '恭喜你获得大奖: ' + event.detail.name,
44 | success (res) {}
45 | })
46 | },
47 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xc/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xc/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xc/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/xc/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xdf/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | blocks: [
5 | { padding: '3px', background: '#92c53a', borderRadius: '8px' },
6 | { padding: '2px', background: '#bdd333', borderRadius: '8px' },
7 | { padding: '7px', background: '#0D6630', borderRadius: '8px' },
8 | ],
9 | prizes: [
10 | {
11 | x: 0, y: 0,
12 | fonts: [{ text: '谢谢参与', top: '68%' }],
13 | imgs: [{ src: imgs['0.png'], width: '50%', top: '12%' }],
14 | },
15 | {
16 | x: 1, y: 0,
17 | fonts: [{ text: '1个星球币', top: '68%' }],
18 | imgs: [{ src: imgs['1.png'], width: '50%', top: '12%' }],
19 | },
20 | {
21 | x: 2, y: 0,
22 | fonts: [{ text: '5个星球币', top: '68%' }],
23 | imgs: [{ src: imgs['1.png'], width: '50%', top: '12%' }],
24 | },
25 | {
26 | x: 2, y: 1,
27 | fonts: [{ text: '10个星球币', top: '68%' }],
28 | imgs: [{ src: imgs['1.png'], width: '50%', top: '12%' }],
29 | },
30 | {
31 | x: 2, y: 2,
32 | fonts: [{ text: '谢谢参与', top: '68%' }],
33 | imgs: [{ src: imgs['0.png'], width: '50%', top: '12%' }],
34 | },
35 | {
36 | x: 1, y: 2,
37 | fonts: [{ text: '1个星球币', top: '68%' }],
38 | imgs: [{ src: imgs['1.png'], width: '50%', top: '12%' }],
39 | },
40 | {
41 | x: 0, y: 2,
42 | fonts: [{ text: '5个星球币', top: '68%' }],
43 | imgs: [{ src: imgs['1.png'], width: '50%', top: '12%' }],
44 | },
45 | {
46 | x: 0, y: 1,
47 | fonts: [{ text: '10个星球币', top: '68%' }],
48 | imgs: [{ src: imgs['1.png'], width: '50%', top: '12%' }],
49 | },
50 | ],
51 | buttons: [{
52 | x: 1, y: 1,
53 | imgs: [{ src: imgs['button.png'], width: '100%', height: '100%' }]
54 | }],
55 | defaultConfig: {
56 | gutter: 8
57 | },
58 | defaultStyle: {
59 | background: '#f4f7cc',
60 | borderRadius: '13px',
61 | fontColor: '#d55a07',
62 | fontSize: '11px',
63 | shadow: '0 9 0.1 #E9E967',
64 | },
65 | activeStyle: {
66 | background: '#e9e967',
67 | shadow: '0 9 0.1 #E1E13B',
68 | }
69 | },
70 | onReady () {
71 | },
72 | gridStart () {
73 | // 获取抽奖组件实例
74 | const child = this.selectComponent('#lucky-grid')
75 | // 调用play方法开始旋转
76 | child.play()
77 | // 用定时器模拟请求接口
78 | setTimeout(() => {
79 | // 3s 后得到中奖索引
80 | const index = Math.random() * 6 >> 0
81 | // 调用stop方法然后缓慢停止
82 | child.stop(index)
83 | }, 3000)
84 | },
85 | gridEnd (event) {
86 | // 中奖奖品详情
87 | wx.showModal({
88 | title: '提示',
89 | content: '恭喜你获得大奖: ' + event.detail.fonts[0].text,
90 | success (res) {}
91 | })
92 | }
93 | })
94 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xdf/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xdf/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/xdf/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/xdf/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/ymc/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | maskShow: false,
5 | prizes: [],
6 | defaultStyle: {
7 | fontColor: '#ff625b',
8 | fontSize: '16px'
9 | },
10 | blocks: [
11 | { padding: '20rpx', background: '#ffc27a' },
12 | { padding: '10rpx', background: '#ff4a4c' },
13 | ],
14 | buttons: [
15 | { radius: '50px', background: '#ff4a4c' },
16 | { radius: '45px', background: '#fff' },
17 | { radius: '41px', background: '#f6c66f', pointer: true },
18 | {
19 | radius: '35px', background: '#ffdea0',
20 | imgs: [{ src: imgs['button.png'], width: '70%', top: '-55%' }]
21 | }
22 | ],
23 | },
24 | onReady () {
25 | this.setData({
26 | prizes: []
27 | });
28 | setTimeout(() => {
29 | [
30 | {
31 | title: '1元红包', background: '#ffd185', fonts: [{ text: '1元红包', top: '10%' }],
32 | imgs: [{ src: imgs['0.png'], width: '35%', top: '35%' }]
33 | },
34 | {
35 | title: '100元红包', background: '#f9e3bb', fonts: [{ text: '100元红包', top: '10%' }],
36 | imgs: [{ src: imgs['1.png'], width: '35%', top: '35%' }]
37 | },
38 | {
39 | title: '0.5元红包', background: '#ffd185', fonts: [{ text: '0.5元红包', top: '10%' }],
40 | imgs: [{ src: imgs['2.png'], width: '35%', top: '35%' }]
41 | },
42 | {
43 | title: '2元红包', background: '#f9e3bb', fonts: [{ text: '2元红包', top: '10%' }],
44 | imgs: [{ src: imgs['3.png'], width: '35%', top: '35%' }]
45 | },
46 | {
47 | title: '10元红包', background: '#ffd185', fonts: [{ text: '10元红包', top: '10%' }],
48 | imgs: [{ src: imgs['4.png'], width: '35%', top: '35%' }]
49 | },
50 | {
51 | title: '50元红包', background: '#f9e3bb', fonts: [{ text: '50元红包', top: '10%' }],
52 | imgs: [{ src: imgs['5.png'], width: '35%', top: '35%' }]
53 | },
54 | {
55 | title: '0.3元红包', background: '#ffd185', fonts: [{ text: '0.3元红包', top: '10%' }],
56 | imgs: [{ src: imgs['6.png'], width: '35%', top: '35%' }]
57 | },
58 | {
59 | title: '5元红包', background: '#f9e3bb', fonts: [{ text: '5元红包', top: '10%' }],
60 | imgs: [{ src: imgs['7.png'], width: '35%', top: '35%' }]
61 | },
62 | ].forEach(_ => {
63 | this.setData({
64 | prizes: [...this.data.prizes, _]
65 | });
66 | })
67 | }, 50);
68 | },
69 | wheelStart () {
70 | // 获取抽奖组件实例
71 | const child = this.selectComponent('#lucky-wheel')
72 | // 调用play方法开始旋转
73 | child.play()
74 | // 用定时器模拟请求接口
75 | setTimeout(() => {
76 | // 3s 后得到中奖索引
77 | const index = Math.random() * 6 >> 0
78 | // 调用stop方法然后缓慢停止
79 | child.stop(index)
80 | }, 3000)
81 | },
82 | wheelEnd (event) {
83 | // 中奖奖品详情
84 | this.setData({ maskShow: true })
85 | wx.showModal({
86 | title: '提示',
87 | content: '恭喜你获得大奖: ' + event.detail.title,
88 | success (res) {}
89 | })
90 | },
91 | maskHidden () {
92 | this.setData({ maskShow: false })
93 | },
94 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/ymc/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/ymc/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/ymc/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/ymc/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yx/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | prizes: [],
5 | blocks: [
6 | { padding: '1px', background: '#e2cea3', borderRadius: '13px' },
7 | { padding: '5px 0px', background: '#f3ecdc', borderRadius: '13px' },
8 | { padding: '1px', background: '#e2cea3', borderRadius: '8px' },
9 | { padding: '15px 10px', background: '#fffcf5', borderRadius: '8px' },
10 | ],
11 | buttons: [{
12 | x: 1, y: 1, background: 'rgba(0, 0, 0, 0)',
13 | imgs: [
14 | { src: imgs['btn.png'], width: '90%', top: '5%' }
15 | ]
16 | }],
17 | defaultStyle: {
18 | background: '#ffefd6',
19 | borderRadius: '5px',
20 | fontColor: '#755c28',
21 | fontSize: '10px',
22 | lineHeight: '12px'
23 | },
24 | activeStyle: {
25 | background: '#de7247',
26 | fontColor: '#ffefd6',
27 | }
28 | },
29 | onReady () {
30 | // 模拟接口异步请求奖品列表
31 | this.getPrizesList()
32 | },
33 | getPrizesList () {
34 | const prizes = []
35 | let axis = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 2], [0, 2], [0, 1]]
36 | let data = ['电热烘干毛巾架', '10元满减红包', '2积分', '胖喵焖烧罐', '5元满减红包', '多层置物架', '3元直减红包', '全场满99减10']
37 | axis.forEach((item, index) => {
38 | prizes.push({
39 | x: item[0], y: item[1],
40 | title: data[index],
41 | imgs: [{
42 | width: '100%',
43 | height: '100%',
44 | src: imgs[`default-${index}.png`],
45 | activeSrc: imgs[`active-${index}.png`]
46 | }]
47 | })
48 | })
49 | this.setData({ prizes })
50 | },
51 | gridStart () {
52 | // 获取抽奖组件实例
53 | const child = this.selectComponent('#lucky-grid')
54 | // 调用play方法开始旋转
55 | child.play()
56 | // 用定时器模拟请求接口
57 | setTimeout(() => {
58 | // 3s 后得到中奖索引
59 | const index = Math.random() * 6 >> 0
60 | // 调用stop方法然后缓慢停止
61 | child.stop(index)
62 | }, 3000)
63 | },
64 | gridEnd (event) {
65 | // 中奖奖品详情
66 | wx.showModal({
67 | title: '提示',
68 | content: '恭喜你获得大奖: ' + event.detail.title,
69 | success (res) {}
70 | })
71 | }
72 | })
73 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yx/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yx/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yx/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/yx/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyjk/index.js:
--------------------------------------------------------------------------------
1 | import imgs from '../ymc/img'
2 | Page({
3 | data: {
4 | maskShow: false,
5 | prizes: [],
6 | blocks: [
7 | { padding: '15px', background: '#ffc27a', borderRadius: 28 },
8 | { padding: '4px', background: '#ff4a4c', borderRadius: 23 },
9 | { padding: '4px', background: '#ff625b', borderRadius: 20 },
10 | ],
11 | buttons: [{
12 | x: 1, y: 1,
13 | background: 'linear-gradient(270deg, #FFDCB8, #FDC689)',
14 | shadow: '0 5 1 #e89b4f',
15 | imgs: [
16 | { src: imgs['button.png'], width: '70%', top: '20%' },
17 | ]
18 | }],
19 | activeStyle: {
20 | background: 'linear-gradient(270deg, #FFDCB8, #FDC689)',
21 | shadow: ''
22 | },
23 | defaultConfig: {
24 | gutter: 5,
25 | },
26 | defaultStyle: {
27 | borderRadius: 15,
28 | fontColor: '#DF424B',
29 | fontSize: '14px',
30 | textAlign: 'center',
31 | background: '#fff',
32 | shadow: '0 5 1 #ebf1f4'
33 | },
34 | },
35 | onReady () {
36 | // 模拟接口异步请求奖品列表
37 | const prizes = []
38 | const data = [
39 | { name: '1元红包', img: imgs['0.png'] },
40 | { name: '100元红包', img: imgs['1.png'] },
41 | { name: '0.5元红包', img: imgs['2.png'] },
42 | { name: '2元红包', img: imgs['3.png'] },
43 | { name: '10元红包', img: imgs['4.png'] },
44 | { name: '50元红包', img: imgs['5.png'] },
45 | { name: '0.3元红包', img: imgs['6.png'] },
46 | { name: '5元红包', img: imgs['7.png'] }
47 | ]
48 | let axis = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 2], [0, 2], [0, 1]]
49 | for (let i = 0; i < 8; i++) {
50 | let item = data[i]
51 | prizes.push({
52 | name: item.name,
53 | index: i, x: axis[i][0], y: axis[i][1],
54 | fonts: [{ text: item.name, top: '70%' }],
55 | imgs: [{ src: item.img, width: '53%', top: '8%' }]
56 | })
57 | }
58 | this.setData({
59 | prizes
60 | })
61 | },
62 | gridStart () {
63 | // 获取抽奖组件实例
64 | const child = this.selectComponent('#lucky-grid')
65 | // 调用play方法开始旋转
66 | child.play()
67 | // 用定时器模拟请求接口
68 | setTimeout(() => {
69 | // 3s 后得到中奖索引
70 | const index = Math.random() * 6 >> 0
71 | // 调用stop方法然后缓慢停止
72 | child.stop(index)
73 | }, 3000)
74 | },
75 | gridEnd (event) {
76 | this.setData({ maskShow: true })
77 | // 中奖奖品详情
78 | wx.showModal({
79 | title: '提示',
80 | content: '恭喜你获得大奖: ' + event.detail.name,
81 | success (res) {}
82 | })
83 | },
84 | maskHidden () {
85 | this.setData({ maskShow: false })
86 | },
87 | })
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyjk/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyjk/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyjk/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/yyjk/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyx/index.js:
--------------------------------------------------------------------------------
1 | import imgs from './img'
2 | Page({
3 | data: {
4 | prizes: [],
5 | blocks: [
6 | { padding: '1px', background: '#192b2c', borderRadius: '10px' },
7 | { padding: '2px', background: '#316d70', borderRadius: '10px' },
8 | { padding: '5px', background: '#183233', borderRadius: '10px' },
9 | ],
10 | buttons: [{
11 | x: 1, y: 1, background: 'rgba(0, 0, 0, 0)',
12 | fonts: [
13 | { text: '剩余次数:???次', fontColor: '#ca620d', top: '75%' }
14 | ],
15 | imgs: [
16 | { src: imgs['btn.png'], width: '100%', top: '0' }
17 | ]
18 | }],
19 | defaultStyle: {
20 | fontColor: '#AFFFD7',
21 | fontSize: '10px',
22 | lineHeight: '13px',
23 | wordWrap: false
24 | },
25 | activeStyle: {
26 | fontColor: '#fff',
27 | }
28 | },
29 | onReady () {
30 | // 模拟接口异步请求奖品列表
31 | this.getPrizesList()
32 | },
33 | getPrizesList () {
34 | const prizes = []
35 | let axis = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 2], [0, 2], [0, 1]]
36 | let data = [
37 | { name: 'steam游戏任选\n(限款)', img: imgs['1.png'], top: '60%' },
38 | { name: '百元京东卡', img: imgs['2.png'], top: '69%' },
39 | { name: '暴雪游戏30点数', img: imgs['3.png'], top: '69%' },
40 | { name: '云币2888', img: imgs['4.png'], top: '69%' },
41 | { name: '手游月卡', img: imgs['5.png'], top: '69%' },
42 | { name: '连续3日\n手游时长+1h', img: imgs['6.png'], top: '60%' },
43 | { name: '手游时长+30min', img: imgs['6.png'], top: '69%' },
44 | { name: '端游时长+1h', img: imgs['7.png'], top: '69%' },
45 | ]
46 | data.forEach((item, index) => {
47 | prizes.push({
48 | x: axis[index][0], y: axis[index][1],
49 | title: item.name,
50 | fonts: [{ text: item.name, top: item.top }],
51 | imgs: [
52 | {
53 | src: imgs['cell.png'],
54 | activeSrc: imgs['active.png'],
55 | width: '100%',
56 | height: '100%',
57 | },
58 | {
59 | src: item.img,
60 | width: '70%',
61 | top: '3%'
62 | }
63 | ]
64 | })
65 | })
66 | this.setData({ prizes })
67 | },
68 | gridStart () {
69 | // 获取抽奖组件实例
70 | const child = this.selectComponent('#lucky-grid')
71 | // 调用play方法开始旋转
72 | child.play()
73 | // 用定时器模拟请求接口
74 | setTimeout(() => {
75 | // 3s 后得到中奖索引
76 | const index = Math.random() * 6 >> 0
77 | // 调用stop方法然后缓慢停止
78 | child.stop(index)
79 | }, 3000)
80 | },
81 | gridEnd (event) {
82 | // 中奖奖品详情
83 | wx.showModal({
84 | title: '提示',
85 | content: '恭喜你获得大奖: ' + event.detail.title,
86 | success (res) {}
87 | })
88 | }
89 | })
90 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyx/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyx/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/mini/tools/demo/pages/yyx/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buuing/lucky-canvas/096b39ba753a6a4db51b0e4ecce140ebeff4e59b/packages/mini/tools/demo/pages/yyx/index.wxss
--------------------------------------------------------------------------------
/packages/mini/tools/demo/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件。",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "nodeModules": true
13 | },
14 | "compileType": "miniprogram",
15 | "libVersion": "2.10.4",
16 | "appid": "",
17 | "projectname": "miniprogram-demo",
18 | "isGameTourist": false,
19 | "condition": {
20 | "search": {
21 | "current": -1,
22 | "list": []
23 | },
24 | "conversation": {
25 | "current": -1,
26 | "list": []
27 | },
28 | "game": {
29 | "currentL": -1,
30 | "list": []
31 | },
32 | "miniprogram": {
33 | "current": -1,
34 | "list": []
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/packages/mini/tools/utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 |
4 | // eslint-disable-next-line no-unused-vars
5 | const colors = require('colors')
6 | const through = require('through2')
7 |
8 | /**
9 | * 异步函数封装
10 | */
11 | function wrap(func, scope) {
12 | return function (...args) {
13 | if (args.length) {
14 | const temp = args.pop()
15 | if (typeof temp !== 'function') {
16 | args.push(temp)
17 | }
18 | }
19 |
20 | return new Promise(function (resolve, reject) {
21 | args.push(function (err, data) {
22 | if (err) reject(err)
23 | else resolve(data)
24 | })
25 |
26 | func.apply((scope || null), args)
27 | })
28 | }
29 | }
30 |
31 | const accessSync = wrap(fs.access)
32 | const statSync = wrap(fs.stat)
33 | const renameSync = wrap(fs.rename)
34 | const mkdirSync = wrap(fs.mkdir)
35 | const readFileSync = wrap(fs.readFile)
36 | const writeFileSync = wrap(fs.writeFile)
37 |
38 | /**
39 | * 调整路径分隔符
40 | */
41 | function transformPath(filePath, sep = '/') {
42 | return filePath.replace(/[\\/]/g, sep)
43 | }
44 |
45 | /**
46 | * 检查文件是否存在
47 | */
48 | async function checkFileExists(filePath) {
49 | try {
50 | await accessSync(filePath)
51 | return true
52 | } catch (err) {
53 | return false
54 | }
55 | }
56 |
57 | /**
58 | * 递归创建目录
59 | */
60 | async function recursiveMkdir(dirPath) {
61 | const prevDirPath = path.dirname(dirPath)
62 | try {
63 | await accessSync(prevDirPath)
64 | } catch (err) {
65 | // 上一级目录不存在
66 | await recursiveMkdir(prevDirPath)
67 | }
68 |
69 | try {
70 | await accessSync(dirPath)
71 |
72 | const stat = await statSync(dirPath)
73 | if (stat && !stat.isDirectory()) {
74 | // 目标路径存在,但不是目录
75 | await renameSync(dirPath, `${dirPath}.bak`) // 将此文件重命名为 .bak 后缀
76 | await mkdirSync(dirPath)
77 | }
78 | } catch (err) {
79 | // 目标路径不存在
80 | await mkdirSync(dirPath)
81 | }
82 | }
83 |
84 | /**
85 | * 读取 json
86 | */
87 | function readJson(filePath) {
88 | try {
89 | // eslint-disable-next-line import/no-dynamic-require
90 | const content = require(filePath)
91 | delete require.cache[require.resolve(filePath)]
92 | return content
93 | } catch (err) {
94 | return null
95 | }
96 | }
97 |
98 | /**
99 | * 读取文件
100 | */
101 | async function readFile(filePath) {
102 | try {
103 | return await readFileSync(filePath, 'utf8')
104 | } catch (err) {
105 | // eslint-disable-next-line no-console
106 | return console.error(err)
107 | }
108 | }
109 |
110 | /**
111 | * 写文件
112 | */
113 | async function writeFile(filePath, data) {
114 | try {
115 | await recursiveMkdir(path.dirname(filePath))
116 | return await writeFileSync(filePath, data, 'utf8')
117 | } catch (err) {
118 | // eslint-disable-next-line no-console
119 | return console.error(err)
120 | }
121 | }
122 |
123 | /**
124 | * 时间格式化
125 | */
126 | function format(time, reg) {
127 | const date = typeof time === 'string' ? new Date(time) : time
128 | const map = {}
129 | map.yyyy = date.getFullYear()
130 | map.yy = ('' + map.yyyy).substr(2)
131 | map.M = date.getMonth() + 1
132 | map.MM = (map.M < 10 ? '0' : '') + map.M
133 | map.d = date.getDate()
134 | map.dd = (map.d < 10 ? '0' : '') + map.d
135 | map.H = date.getHours()
136 | map.HH = (map.H < 10 ? '0' : '') + map.H
137 | map.m = date.getMinutes()
138 | map.mm = (map.m < 10 ? '0' : '') + map.m
139 | map.s = date.getSeconds()
140 | map.ss = (map.s < 10 ? '0' : '') + map.s
141 |
142 | return reg.replace(/\byyyy|yy|MM|M|dd|d|HH|H|mm|m|ss|s\b/g, $1 => map[$1])
143 | }
144 |
145 | /**
146 | * 日志插件
147 | */
148 | function logger(action = 'copy') {
149 | return through.obj(function (file, enc, cb) {
150 | const type = path.extname(file.path).slice(1).toLowerCase()
151 |
152 | // eslint-disable-next-line no-console
153 | console.log(`[${format(new Date(), 'yyyy-MM-dd HH:mm:ss').grey}] [${action.green} ${type.green}] ${'=>'.cyan} ${file.path}`)
154 |
155 | this.push(file)
156 | cb()
157 | })
158 | }
159 |
160 | /**
161 | * 比较数组是否相等
162 | */
163 | function compareArray(arr1, arr2) {
164 | if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false
165 | if (arr1.length !== arr2.length) return false
166 |
167 | for (let i = 0, len = arr1.length; i < len; i++) {
168 | if (arr1[i] !== arr2[i]) return false
169 | }
170 |
171 | return true
172 | }
173 |
174 | /**
175 | * 合并两个对象
176 | */
177 | function merge(obj1, obj2) {
178 | Object.keys(obj2).forEach(key => {
179 | if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) {
180 | obj1[key] = obj1[key].concat(obj2[key])
181 | } else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
182 | obj1[key] = Object.assign(obj1[key], obj2[key])
183 | } else {
184 | obj1[key] = obj2[key]
185 | }
186 | })
187 |
188 | return obj1
189 | }
190 |
191 | /**
192 | * 获取 id
193 | */
194 | let seed = +new Date()
195 | function getId() {
196 | return ++seed
197 | }
198 |
199 | module.exports = {
200 | wrap,
201 | transformPath,
202 |
203 | checkFileExists,
204 | readJson,
205 | readFile,
206 | writeFile,
207 |
208 | logger,
209 | format,
210 | compareArray,
211 | merge,
212 | getId,
213 | }
214 |
--------------------------------------------------------------------------------
/packages/mini/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esnext",
4 | "target": "es2015",
5 | "lib": ["es2015", "es2017", "dom"],
6 | "noImplicitAny": false,
7 | "moduleResolution": "node",
8 | "sourceMap": true,
9 | "baseUrl": ".",
10 | "allowSyntheticDefaultImports": true,
11 | "experimentalDecorators": true,
12 | "emitDecoratorMetadata":true,
13 | "esModuleInterop": true,
14 | "resolveJsonModule": true
15 | },
16 | "files": [
17 | "node_modules/miniprogram-api-typings/index.d.ts"
18 | ],
19 | "include": [
20 | "src/**/*.ts"
21 | ],
22 | "exclude": [
23 | "node_modules"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ]
6 | }
--------------------------------------------------------------------------------
/packages/react/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | yarn.lock
4 | package-lock.json
5 | dist
6 |
7 | # local env files
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/packages/react/.npmignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/react/README.md:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | ## 官方文档
25 |
26 | > **中文**:[https://100px.net](https://100px.net)
27 |
28 |
29 |
30 | ## 在 React 中使用
31 |
32 | - [跳转官网 查看详情](https://100px.net/usage/react.html)
33 |
34 |
35 |
36 | ## 🙏🙏🙏 点个Star
37 |
38 | **如果您觉得这个项目还不错, 可以在 [Github](https://github.com/buuing/lucky-canvas) 上面帮我点个`star`, 支持一下作者 ☜(゚ヮ゚☜)**
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/react/example/react16.8.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Lucky-canva-react Demo
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
73 |
74 |
--------------------------------------------------------------------------------
/packages/react/example/react18.0.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Lucky-canva-react Demo
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
74 |
75 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lucky-canvas/react",
3 | "version": "0.1.13",
4 | "description": "react ( 大转盘 / 九宫格 / 老虎机 ) 抽奖插件",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.esm.js",
7 | "unpkg": "dist/index.umd.js",
8 | "jsdelivr": "dist/index.umd.js",
9 | "types": "types/index.d.ts",
10 | "scripts": {
11 | "dev": "rollup --config rollup.config.dev.js -w",
12 | "build": "rollup --config rollup.config.build.js"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/LuckDraw/lucky-canvas.git"
17 | },
18 | "keywords": [
19 | "react抽奖插件",
20 | "大转盘抽奖",
21 | "九宫格抽奖",
22 | "老虎机抽奖"
23 | ],
24 | "author": "ldq ",
25 | "license": "Apache-2.0",
26 | "bugs": {
27 | "url": "https://github.com/LuckDraw/lucky-canvas/issues"
28 | },
29 | "homepage": "https://100px.net",
30 | "files": [
31 | "dist",
32 | "types"
33 | ],
34 | "devDependencies": {
35 | "@babel/core": "^7.12.10",
36 | "@babel/preset-env": "^7.12.11",
37 | "@babel/preset-react": "^7.12.10",
38 | "@rollup/plugin-commonjs": "^17.0.0",
39 | "@rollup/plugin-json": "^4.1.0",
40 | "@rollup/plugin-node-resolve": "^11.1.0",
41 | "rollup": "^2.38.0",
42 | "rollup-plugin-babel": "^4.4.0",
43 | "rollup-plugin-livereload": "^2.0.5",
44 | "rollup-plugin-serve": "^1.1.0",
45 | "rollup-plugin-terser": "^7.0.2"
46 | },
47 | "dependencies": {
48 | "lucky-canvas": "^1.7.27"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/react/rollup.config.build.js:
--------------------------------------------------------------------------------
1 | import pkg from './package.json'
2 | import json from '@rollup/plugin-json'
3 | import resolve from '@rollup/plugin-node-resolve'
4 | import commonjs from '@rollup/plugin-commonjs'
5 | import babel from 'rollup-plugin-babel'
6 | import { terser } from 'rollup-plugin-terser'
7 |
8 | export default {
9 | input: './src/index.js',
10 | output: [
11 | {
12 | file: pkg.main,
13 | format: 'cjs',
14 | sourcemap: true,
15 | },
16 | {
17 | file: pkg.module,
18 | format: 'esm',
19 | sourcemap: true,
20 | },
21 | {
22 | file: pkg.jsdelivr,
23 | format: 'umd',
24 | name: 'ReactLuckyCanvas',
25 | sourcemap: true,
26 | globals: {
27 | 'lucky-canvas': 'LuckyCanvas',
28 | },
29 | },
30 | ],
31 | plugins: [
32 | babel(),
33 | resolve(),
34 | commonjs(),
35 | json(),
36 | terser(),
37 | ],
38 | external: ['react'],
39 | }
40 |
--------------------------------------------------------------------------------
/packages/react/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import pkg from './package.json'
2 | import json from '@rollup/plugin-json'
3 | import resolve from '@rollup/plugin-node-resolve'
4 | import commonjs from '@rollup/plugin-commonjs'
5 | import babel from 'rollup-plugin-babel'
6 | import livereload from 'rollup-plugin-livereload'
7 | import serve from 'rollup-plugin-serve'
8 |
9 | export default {
10 | input: './src/app.js',
11 | output: [
12 | {
13 | file: pkg.module,
14 | format: 'esm',
15 | sourcemap: true,
16 | },
17 | {
18 | file: pkg.jsdelivr,
19 | format: 'umd',
20 | name: 'ReactLuckyCanvas',
21 | sourcemap: true,
22 | globals: {
23 | 'lucky-canvas': 'LuckyCanvas',
24 | },
25 | },
26 | ],
27 | plugins: [
28 | babel({
29 | exclude: "node_modules/**"
30 | }),
31 | resolve(),
32 | commonjs(),
33 | json(),
34 | livereload(),
35 | serve({
36 | open: true,
37 | port: 8000,
38 | contentBase: './',
39 | openPage: '/example/react16.8.html',
40 | }),
41 | ],
42 | external: ['react'],
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import LuckyWheel from './demo/LuckyWheel';
4 | import LuckyGrid from './demo/LuckyGrid';
5 | import SlotMachine from './demo/SlotMachine';
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------
/packages/react/src/components/LuckyGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { LuckyGrid as Grid } from 'lucky-canvas'
3 | import { name, version } from '../../package.json'
4 |
5 | export default class LuckyGrid extends React.Component {
6 | constructor (props) {
7 | super(props)
8 | this.myLucky = React.createRef()
9 | this.lucky = undefined
10 | }
11 | componentDidMount () {
12 | // 当前已经有实例时,不再进行,解决在`react18 + strictMode`下组件componentDidMount会被触发两次的问题
13 | if (this.lucky) {
14 | return
15 | }
16 | this.myLucky.current.setAttribute('package', `${name}@${version}`)
17 | try {
18 | this.initLucky()
19 | this.props.onSuccess && this.props.onSuccess()
20 | } catch (err) {
21 | this.props.onError && this.props.onError(err)
22 | } finally {
23 | this.props.onFinally && this.props.onFinally(err)
24 | }
25 | }
26 | componentDidUpdate (prevProps) {
27 | if (!this.lucky) return
28 | if (this.props.width !== prevProps.width) {
29 | this.lucky.width = this.props.width
30 | }
31 | if (this.props.height !== prevProps.height) {
32 | this.lucky.height = this.props.height
33 | }
34 | if (this.props.cols !== prevProps.cols) {
35 | this.lucky.cols = this.props.cols
36 | }
37 | if (this.props.rows !== prevProps.rows) {
38 | this.lucky.rows = this.props.rows
39 | }
40 | if (this.props.blocks !== prevProps.blocks) {
41 | this.lucky.blocks = this.props.blocks
42 | }
43 | if (this.props.prizes !== prevProps.prizes) {
44 | this.lucky.prizes = this.props.prizes
45 | }
46 | if (this.props.buttons !== prevProps.buttons) {
47 | this.lucky.buttons = this.props.buttons
48 | }
49 | }
50 | initLucky () {
51 | this.lucky = new Grid({
52 | flag: 'WEB',
53 | divElement: this.myLucky.current
54 | }, {
55 | ...this.props,
56 | start: (...rest) => {
57 | this.props.onStart && this.props.onStart(...rest)
58 | },
59 | end: (...rest) => {
60 | this.props.onEnd && this.props.onEnd(...rest)
61 | }
62 | })
63 | }
64 | init (...rest) {
65 | this.lucky.init(...rest)
66 | }
67 | play (...rest) {
68 | this.lucky.play(...rest)
69 | }
70 | stop (...rest) {
71 | this.lucky.stop(...rest)
72 | }
73 | render () {
74 | return
75 | }
76 | }
77 |
78 | LuckyGrid.defaultProps = {
79 | width: '',
80 | height: '',
81 | cols: 3,
82 | rows: 3,
83 | blocks: [],
84 | prizes: [],
85 | buttons: [],
86 | defaultStyle: {},
87 | activeStyle: {},
88 | defaultConfig: {},
89 | }
90 |
--------------------------------------------------------------------------------
/packages/react/src/components/LuckyWheel.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { LuckyWheel as Wheel } from 'lucky-canvas'
3 | import { name, version } from '../../package.json'
4 |
5 | export default class LuckyWheel extends React.Component {
6 | constructor (props) {
7 | super(props)
8 | this.myLucky = React.createRef()
9 | this.lucky = undefined
10 | }
11 | componentDidMount () {
12 | // 当前已经有实例时,不再进行,解决在`react18 + strictMode`下组件componentDidMount会被触发两次的问题
13 | if (this.lucky) {
14 | return
15 | }
16 | this.myLucky.current.setAttribute('package', `${name}@${version}`)
17 | try {
18 | this.initLucky()
19 | this.props.onSuccess && this.props.onSuccess()
20 | } catch (err) {
21 | this.props.onError && this.props.onError(err)
22 | } finally {
23 | this.props.onFinally && this.props.onFinally()
24 | }
25 | }
26 | componentDidUpdate (prevProps) {
27 | if (!this.lucky) return
28 | if (this.props.width !== prevProps.width) {
29 | this.lucky.width = this.props.width
30 | }
31 | if (this.props.height !== prevProps.height) {
32 | this.lucky.height = this.props.height
33 | }
34 | if (this.props.blocks !== prevProps.blocks) {
35 | this.lucky.blocks = this.props.blocks
36 | }
37 | if (this.props.prizes !== prevProps.prizes) {
38 | this.lucky.prizes = this.props.prizes
39 | }
40 | if (this.props.buttons !== prevProps.buttons) {
41 | this.lucky.buttons = this.props.buttons
42 | }
43 | }
44 | initLucky () {
45 | this.lucky = new Wheel({
46 | flag: 'WEB',
47 | divElement: this.myLucky.current
48 | }, {
49 | ...this.props,
50 | start: (...rest) => {
51 | this.props.onStart && this.props.onStart(...rest)
52 | },
53 | end: (...rest) => {
54 | this.props.onEnd && this.props.onEnd(...rest)
55 | }
56 | })
57 | }
58 | init (...rest) {
59 | this.lucky.init(...rest)
60 | }
61 | play (...rest) {
62 | this.lucky.play(...rest)
63 | }
64 | stop (...rest) {
65 | this.lucky.stop(...rest)
66 | }
67 | render () {
68 | return
69 | }
70 | }
71 |
72 | LuckyWheel.defaultProps = {
73 | width: '',
74 | height: '',
75 | prizes: [],
76 | blocks: [],
77 | buttons: [],
78 | defaultStyle: {},
79 | defaultConfig: {},
80 | }
81 |
--------------------------------------------------------------------------------
/packages/react/src/components/SlotMachine.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SlotMachine as Slot } from 'lucky-canvas'
3 | import { name, version } from '../../package.json'
4 |
5 | export default class SlotMachine extends React.Component {
6 | constructor (props) {
7 | super(props)
8 | this.myLucky = React.createRef()
9 | this.lucky = undefined
10 | }
11 | componentDidMount () {
12 | // 当前已经有实例时,不再进行,解决在`react18 + strictMode`下组件componentDidMount会被触发两次的问题
13 | if (this.lucky) {
14 | return
15 | }
16 | this.myLucky.current.setAttribute('package', `${name}@${version}`)
17 | try {
18 | this.initLucky()
19 | this.props.onSuccess && this.props.onSuccess()
20 | } catch (err) {
21 | this.props.onError && this.props.onError(err)
22 | } finally {
23 | this.props.onFinally && this.props.onFinally(err)
24 | }
25 | }
26 | componentDidUpdate (prevProps) {
27 | if (!this.lucky) return
28 | if (this.props.width !== prevProps.width) {
29 | this.lucky.width = this.props.width
30 | }
31 | if (this.props.height !== prevProps.height) {
32 | this.lucky.height = this.props.height
33 | }
34 | if (this.props.blocks !== prevProps.blocks) {
35 | this.lucky.blocks = this.props.blocks
36 | }
37 | if (this.props.prizes !== prevProps.prizes) {
38 | this.lucky.prizes = this.props.prizes
39 | }
40 | if (this.props.slots !== prevProps.slots) {
41 | this.lucky.slots = this.props.slots
42 | }
43 | }
44 | initLucky () {
45 | this.lucky = new Slot({
46 | flag: 'WEB',
47 | divElement: this.myLucky.current
48 | }, {
49 | ...this.props,
50 | start: (...rest) => {
51 | this.props.onStart && this.props.onStart(...rest)
52 | },
53 | end: (...rest) => {
54 | this.props.onEnd && this.props.onEnd(...rest)
55 | }
56 | })
57 | }
58 | init (...rest) {
59 | this.lucky.init(...rest)
60 | }
61 | play (...rest) {
62 | this.lucky.play(...rest)
63 | }
64 | stop (...rest) {
65 | this.lucky.stop(...rest)
66 | }
67 | render () {
68 | return
69 | }
70 | }
71 |
72 | SlotMachine.defaultProps = {
73 | width: '',
74 | height: '',
75 | blocks: [],
76 | prizes: [],
77 | slots: [],
78 | defaultStyle: {},
79 | defaultConfig: {},
80 | }
81 |
--------------------------------------------------------------------------------
/packages/react/src/demo/LuckyGrid.js:
--------------------------------------------------------------------------------
1 | import LuckyGrid from '../components/LuckyGrid.js'
2 | import React from 'react'
3 |
4 | export default class GridDemo extends React.Component {
5 | constructor () {
6 | super()
7 | this.myLucky = React.createRef()
8 |
9 | const data = [
10 | { name: '1元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
11 | { name: '100元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
12 | { name: '0.5元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
13 | { name: '2元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
14 | { name: '10元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
15 | { name: '50元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
16 | { name: '0.3元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
17 | { name: '5元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' }
18 | ]
19 | let axis = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 2], [0, 2], [0, 1]]
20 | const prizes = []
21 | for (let i = 0; i < 8; i++) {
22 | let item = data[i]
23 | prizes.push({
24 | name: item.name,
25 | index: i, x: axis[i][0], y: axis[i][1],
26 | fonts: [{ text: item.name, top: '70%' }],
27 | imgs: [{ src: item.img, width: '53%', top: '8%' }]
28 | })
29 | }
30 | this.state = {
31 | blocks: [
32 | { padding: '10px', background: '#ffc27a' },
33 | { padding: '10px', paddingRight: '90px', background: '#ff4a4c' },
34 | { padding: '0px', background: '#fff' }
35 | ],
36 | prizes,
37 | activeStyle: {
38 | background: 'linear-gradient(270deg, #FFDCB8, #FDC689)',
39 | shadow: ''
40 | },
41 | buttons: [{
42 | x: 1, y: 1,
43 | background: 'linear-gradient(270deg, #FFDCB8, #FDC689)',
44 | shadow: '0 5 1 #e89b4f',
45 | fonts: [
46 | { text: `1 次`, fontColor: '#fff', top: '73%', fontSize: '11px' },
47 | ],
48 | imgs: [
49 | // { src: 'https://100px.net/assets/img/button.2f4ac3e9.png', width: '65%', top: '12%' },
50 | // { src: './img/btn.png', width: '50%', top: '73%' }
51 | ]
52 | }
53 | ],
54 | defaultStyle: {
55 | borderRadius: 15,
56 | fontColor: '#DF424B',
57 | fontSize: '14px',
58 | textAlign: 'center',
59 | background: '#fff',
60 | shadow: '0 5 1 #ebf1f4'
61 | },
62 | }
63 | }
64 | render () {
65 | return {
74 | this.myLucky.current.play()
75 | setTimeout(() => {
76 | const index = Math.random() * 6 >> 0
77 | this.myLucky.current.stop(index)
78 | }, 2500)
79 | }}
80 | onEnd={prize => {
81 | alert('恭喜获得大奖:' + prize.name)
82 | }}
83 | >
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/packages/react/src/demo/LuckyWheel.js:
--------------------------------------------------------------------------------
1 | import LuckyWheel from '../components/LuckyWheel.js'
2 | import React from 'react'
3 |
4 | export default class Wheel extends React.Component {
5 | constructor () {
6 | super()
7 | this.myLucky = React.createRef()
8 | this.state = {
9 | currPrize: '哈哈哈',
10 | blocks: [
11 | ],
12 | prizes: [
13 | { title: '1元红包', background: '#f9e3bb', fonts: [{ text: '1元红包', top: '18%' }] },
14 | { title: '100元红包', background: '#f8d384', fonts: [{ text: '100元红包', top: '18%' }] },
15 | { title: '0.5元红包', background: '#f9e3bb', fonts: [{ text: '0.5元红包', top: '18%' }] },
16 | { title: '2元红包', background: '#f8d384', fonts: [{ text: '2元红包', top: '18%' }] },
17 | { title: '10元红包', background: '#f9e3bb', fonts: [{ text: '10元红包', top: '18%' }] },
18 | { title: '50元红包', background: '#f8d384', fonts: [{ text: '50元红包', top: '18%' }] },
19 | ],
20 | buttons: [
21 | {
22 | radius: '35px', background: '#ffdea0',
23 | fonts: [{ text: '开始\n抽奖', fontSize: '18px', top: -18 }]
24 | }
25 | ],
26 | }
27 | }
28 | render () {
29 | return
30 | 当前中奖: {this.state.currPrize}
31 | {
40 | console.log(this.state.currPrize)
41 | this.myLucky.current.play()
42 | setTimeout(() => {
43 | const index = Math.random() * 6 >> 0
44 | this.myLucky.current.stop(index)
45 | }, 2500)
46 | }}
47 | onEnd={prize => {
48 | this.setState({
49 | currPrize: prize.title
50 | })
51 | }}
52 | >
53 |
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/react/src/demo/SlotMachine.js:
--------------------------------------------------------------------------------
1 | import SlotMachine from '../components/SlotMachine.js'
2 | import React from 'react'
3 |
4 | export default class SlotDemo extends React.Component {
5 | constructor () {
6 | super()
7 | this.myLucky = React.createRef()
8 |
9 | const data = [
10 | { name: '1元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
11 | { name: '100元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
12 | { name: '0.5元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
13 | { name: '2元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
14 | { name: '10元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
15 | { name: '50元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
16 | { name: '0.3元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' },
17 | { name: '5元红包', img: 'https://unpkg.com/buuing@0.0.1/imgs/lucky-canvas.png' }
18 | ]
19 | let axis = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 2], [0, 2], [0, 1]]
20 | const prizes = []
21 | for (let i = 0; i < 8; i++) {
22 | let item = data[i]
23 | prizes.push({
24 | name: item.name,
25 | index: i, x: axis[i][0], y: axis[i][1],
26 | fonts: [{ text: item.name, top: '70%' }],
27 | imgs: [{ src: item.img, width: '100%', top: '0%' }]
28 | })
29 | }
30 | this.state = {
31 | blocks: [
32 | { padding: '10px', background: '#ffc27a' },
33 | { padding: '10px', paddingRight: '90px', background: '#ff4a4c' },
34 | { padding: '0px', background: '#fff' }
35 | ],
36 | prizes,
37 | slots: [
38 | {},
39 | {},
40 | {},
41 | ],
42 | defaultStyle: {
43 | borderRadius: 15,
44 | fontColor: '#DF424B',
45 | fontSize: '14px',
46 | textAlign: 'center',
47 | background: '#fff',
48 | shadow: '0 5 1 #ebf1f4'
49 | },
50 | }
51 | }
52 | render () {
53 | return
54 | {
63 | this.myLucky.current.play()
64 | setTimeout(() => {
65 | const index = Math.random() * 6 >> 0
66 | this.myLucky.current.stop(index)
67 | }, 2500)
68 | }}
69 | onEnd={prize => {
70 | alert('恭喜获得大奖:' + prize.name)
71 | }}
72 | >
73 |
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/packages/react/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as LuckyWheel } from './components/LuckyWheel.js'
2 | export { default as LuckyGrid } from './components/LuckyGrid.js'
3 | export { default as SlotMachine } from './components/SlotMachine.js'
4 |
--------------------------------------------------------------------------------
/packages/taro/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | yarn.lock
4 | package-lock.json
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 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/packages/taro/.npmignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/taro/README.md:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | ## 官方文档
25 |
26 | > **中文**:[https://100px.net/usage/taro.html](https://100px.net/usage/taro.html)
27 |
28 |
29 |
30 | ## 在 Taro 中使用
31 |
32 | ### 安装
33 |
34 | > 为了确保相关依赖安装正确, 你必须通过 npm / yarn 来安装
35 |
36 | ```shell
37 | # npm 安装:
38 | npm install @lucky-canvas/taro@latest
39 |
40 | # yarn 安装:
41 | yarn add @lucky-canvas/taro@latest
42 | ```
43 |
44 |
45 |
46 |
47 | ### 使用
48 |
49 | #### taro-vue 简单示例
50 |
51 | - [点击查看 taro-vue 完整示例](https://100px.net/usage/taro.html)
52 |
53 | ```html
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
75 | ```
76 |
77 |
78 |
79 | #### taro-react 简单示例
80 |
81 | - [点击查看 taro-react 完整示例](https://100px.net/usage/taro.html)
82 |
83 | ```js
84 | import React from 'react'
85 | import { View } from '@tarojs/components'
86 | import { LuckyWheel, LuckyGrid, SlotMachine } from '@lucky-canvas/taro/react'
87 |
88 | export default class Index extends React.Component {
89 | render () {
90 | return
91 |
92 | {/* 大转盘抽奖 */}
93 |
94 |
95 | {/* 大转盘抽奖 */}
96 |
97 |
98 | {/* 老虎机抽奖 */}
99 |
100 |
101 |
102 | }
103 | }
104 | ```
105 |
106 |
107 |
108 | ## 完整文档: https://100px.net
109 |
110 |
111 |
112 | ## 🙏🙏🙏 点个Star
113 |
114 | ### **如果您觉得这个项目还不错, 可以在 [Github](https://github.com/buuing/lucky-canvas) 上面帮我点个`star`, 支持一下作者 ☜(゚ヮ゚☜) ☜(゚ヮ゚☜)**
115 |
116 |
117 |
--------------------------------------------------------------------------------
/packages/taro/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lucky-canvas/taro",
3 | "version": "0.0.13",
4 | "description": "基于 taro 实现的大转盘 / 九宫格 / 老虎机抽奖插件",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/LuckDraw/lucky-canvas.git"
12 | },
13 | "keywords": [
14 | "taro抽奖插件",
15 | "taro大转盘抽奖",
16 | "taro九宫格抽奖"
17 | ],
18 | "author": "ldq ",
19 | "license": "Apache-2.0",
20 | "files": [
21 | "vue",
22 | "react",
23 | "utils",
24 | "taro2"
25 | ],
26 | "bugs": {
27 | "url": "https://github.com/LuckDraw/lucky-canvas/issues"
28 | },
29 | "homepage": "https://100px.net",
30 | "dependencies": {
31 | "lucky-canvas": "~1.7.19"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/taro/react/index.js:
--------------------------------------------------------------------------------
1 | export { default as LuckyWheel } from './LuckyWheel.js'
2 | export { default as LuckyGrid } from './LuckyGrid.js'
3 | export { default as SlotMachine } from './SlotMachine.js'
4 |
--------------------------------------------------------------------------------
/packages/taro/utils/index.css:
--------------------------------------------------------------------------------
1 | .lucky-box {
2 | position: relative;
3 | overflow: hidden;
4 | margin: 0 auto;
5 | }
6 | .lucky-box .lucky-canvas {
7 | position: absolute;
8 | pointer-events: none;
9 | left: 0;
10 | top: 0;
11 | }
12 | .lucky-box .lucky-wheel-btn {
13 | position: absolute;
14 | left: 50%;
15 | top: 50%;
16 | transform: translate(-50%, -50%);
17 | background: rgba(0, 0, 0, 0);
18 | border-radius: 50%;
19 | cursor: pointer;
20 | }
21 | .lucky-box .lucky-grid-btn {
22 | position: absolute;
23 | background: rgba(0, 0, 0, 0);
24 | border-radius: 0;
25 | cursor: pointer;
26 | }
27 | .lucky-box .lucky-imgs {
28 | width: 0;
29 | height: 0;
30 | visibility: hidden;
31 | }
--------------------------------------------------------------------------------
/packages/taro/utils/index.js:
--------------------------------------------------------------------------------
1 | import Taro from '@tarojs/taro'
2 |
3 | const windowWidth = Taro.getSystemInfoSync().windowWidth
4 |
5 | export const getFlag = () => {
6 | let flag
7 | switch (process.env.TARO_ENV) {
8 | case 'h5':
9 | flag = 'WEB'
10 | break
11 | case 'weapp':
12 | flag = 'MP-WX'
13 | break
14 | case 'qq':
15 | flag = 'MP-WX'
16 | break
17 | case 'rn':
18 | flag = 'TARO-RN'
19 | break
20 | default:
21 | flag = 'MP-WX'
22 | break
23 | }
24 | return flag
25 | }
26 |
27 | export const rpx2px = (value) => {
28 | if (typeof value === 'string') value = Number(value.replace(/[a-z]*/g, ''))
29 | return windowWidth / 750 * value
30 | }
31 |
32 | export const changeUnits = (value) => {
33 | value = String(value)
34 | return Number(value.replace(/^(\-*[0-9.]*)([a-z%]*)$/, (value, num, unit) => {
35 | switch (unit) {
36 | case 'px':
37 | num *= 1
38 | break
39 | case 'rpx':
40 | num = rpx2px(num)
41 | break
42 | default:
43 | num *= 1
44 | break
45 | }
46 | return num
47 | }))
48 | }
49 |
50 | export const resolveImage = async (img, canvas, srcName = 'src', resolveName = '$resolve', cb) => {
51 | let imgObj = canvas.createImage()
52 | // 成功回调
53 | imgObj.onload = () => {
54 | if (typeof cb === 'function') cb()
55 | img[resolveName](imgObj)
56 | }
57 | // 失败回调
58 | imgObj.onerror = (err) => {
59 | console.error(err)
60 | // img['$reject']()
61 | }
62 | // 设置src
63 | imgObj.src = img[srcName]
64 | }
65 |
66 | // export const resolveImage = async (res, img, imgName = 'src', resolveName = '$resolve') => {
67 | // const src = img[imgName]
68 | // // 如果是base64就调用base64src()方法把图片写入本地, 然后渲染临时路径
69 | // if (/^data:image\/([a-z]+);base64,/.test(src)) {
70 | // const path = await base64src(src)
71 | // img[resolveName]({ ...res.detail, path })
72 | // return
73 | // }
74 | // // 如果是网络图片, 则通过getImageInfo()方法获取图片宽高
75 | // Taro.getImageInfo({
76 | // src: src,
77 | // success: (imgObj) => img[resolveName](imgObj),
78 | // fail: () => console.error('API `Taro.getImageInfo` 加载图片失败', src)
79 | // })
80 | // }
81 |
82 | export function getImage (canvasId, canvas) {
83 | return new Promise((resolve, reject) => {
84 | Taro.canvasToTempFilePath({
85 | canvas: canvas,
86 | canvasId: canvasId,
87 | success: (res) => resolve(res),
88 | fail: (err) => reject(err),
89 | }, this)
90 | })
91 | }
92 |
--------------------------------------------------------------------------------
/packages/taro/vue/SlotMachine.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
15 |
16 |
17 |
18 |
19 |
20 | imgBindload(e, 'blocks', index, i)">
21 |
22 |
23 |
24 |
25 |
26 |
27 | imgBindload(e, 'prizes', index, i)">
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
194 |
195 |
198 |
--------------------------------------------------------------------------------
/packages/taro/vue/index.js:
--------------------------------------------------------------------------------
1 | export { default as LuckyWheel } from './LuckyWheel.vue'
2 | export { default as LuckyGrid } from './LuckyGrid.vue'
3 | export { default as SlotMachine } from './SlotMachine.vue'
4 |
--------------------------------------------------------------------------------
/packages/uni/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | yarn.lock
4 | package-lock.json
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 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 | components.zip
24 | components/
25 |
--------------------------------------------------------------------------------
/packages/uni/.npmignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/uni/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
23 |
24 | |适配框架|npm下载量|CDN使用量|
25 | | :-: | :-: | :-: |
26 | |[`JS` / `JQ` 中使用](https://100px.net/usage/js.html)|
|
|
27 | |[`Vue` 中使用](https://100px.net/usage/vue.html)|
|
|
28 | |[`React` 中使用](https://100px.net/usage/react.html)|
|-|
29 | |[`UniApp` 中使用](https://100px.net/usage/uni.html)|
|-|
30 | |[`Taro3.x` 中使用](https://100px.net/usage/taro.html)|
|-|
31 | |[`微信小程序` 中使用](https://100px.net/usage/wx.html)|
|-|
32 |
33 |
34 |
35 | ## 官方文档 & Demo演示
36 |
37 | > **中文**:[https://100px.net](https://100px.net)
38 |
39 | > **English**:**If anyone can help translate the document, please contact me** `ldq404@qq.com`
40 |
41 |
42 |
43 | ## 在 uni-app 中使用
44 |
45 | ### 1. 安装插件
46 |
47 | - 你可以选择通过 `HBuilderX` 导入插件: [https://ext.dcloud.net.cn/plugin?id=3499](https://ext.dcloud.net.cn/plugin?id=3499)
48 |
49 | - 也可以选择通过 `npm` / `yarn` 安装
50 |
51 | ```shell
52 | # npm 安装:
53 | npm install @lucky-canvas/uni
54 |
55 | # yarn 安装:
56 | yarn add @lucky-canvas/uni
57 | ```
58 |
59 |
60 |
61 | ### 2. 引入并使用
62 |
63 | ```html
64 |
65 |
66 |
71 |
72 |
77 |
78 | ```
79 |
80 | ```js
81 | // npm 下载会默认到 node_modules 里面,直接引入包名即可
82 | import LuckyWheel from '@lucky-canvas/uni/lucky-wheel' // 大转盘
83 | import LuckyGrid from '@lucky-canvas/uni/lucky-grid' // 九宫格
84 |
85 | // 如果你是通过 HBuilderX 导入插件,那你需要指定一下路径
86 | // import LuckyWheel from '@/components/@lucky-canvas/uni/lucky-wheel' // 大转盘
87 | // import LuckyGrid from '@/components/@lucky-canvas/uni/lucky-grid' // 九宫格
88 |
89 | export default {
90 | // 注册组件
91 | components: { LuckyWheel, LuckyGrid },
92 | }
93 | ```
94 |
95 |
96 |
97 | ### 3. 我提供了一个最基本的 demo 供你用于尝试
98 |
99 | 由于 uni-app 渲染 md 的时候会出问题,所以我把 demo 代码放到了文档里
100 |
101 | - [https://100px.net/usage/uni-app.html](https://100px.net/usage/uni-app.html)
102 |
103 |
104 |
105 | ### **4. 补充说明**
106 |
107 | - [**如果用着顺手, 可以在 Github 上面点个
支持一下(●'◡'●)**](https://github.com/buuing/lucky-canvas)
108 |
109 | - 另外: 如果你修复了某些bug或兼容, 欢迎提给我, 我会把你展示到官网的贡献者列表当中
110 |
111 |
112 |
113 |
114 | ### 5. 常见问题
115 |
116 | 1. 转盘层级太高了, 我的弹窗盖不住怎么办?
117 |
118 | > 答: 因为小程序里canvas是原生组件顶层渲染, 我无法控制canvas的层级, 如果你想盖住它也肯简单, 你可以百度搜索``组件
119 |
120 | 2. 你这些素材, 图片组件从哪下载?
121 |
122 | > 答: 官网里的任何图片素材, 所使用到的图片资源均为学习交流使用, 请勿将其用于商业用途, 由此产生的任何商业纠纷我这边概不负责
123 |
124 | 3. xxx属性怎么使用? xxx方法怎么调用?
125 |
126 | > 答: 自己去看文档, 不然难道要我把代码给你写好吗?
127 |
128 | 4. 这个属性的效果与官网的描述不一致?
129 |
130 | > 答: 可能有bug, 你可以去github上的issues去提问 (请认真填写模板)
131 |
132 | 5. 为什么这个插件不支持app和其他小程序
133 |
134 | > 答: 没时间, 但是希望志同道合的同学来一起参与uniapp的兼容开发
135 |
136 | ---
137 |
138 | 作者留言: 为了使我自己保持心情愉悦, 低于5星的提问我用浏览器插件都屏蔽了
139 |
140 | 不要通过插件市场赞赏!!!
141 |
--------------------------------------------------------------------------------
/packages/uni/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lucky-canvas/uni",
3 | "version": "0.0.13",
4 | "description": "uni-app【大转盘 / 九宫格 / 老虎机】抽奖插件",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "keywords": [
9 | "uni-app抽奖"
10 | ],
11 | "files": [
12 | "lucky-wheel.vue",
13 | "lucky-grid.vue",
14 | "slot-machine.vue",
15 | "utils.js",
16 | "demo.vue"
17 | ],
18 | "author": "ldq ",
19 | "license": "Apache-2.0",
20 | "dependencies": {
21 | "lucky-canvas": "~1.7.19"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/uni/utils.js:
--------------------------------------------------------------------------------
1 | let windowWidth = uni.getSystemInfoSync().windowWidth
2 | // uni-app@2.9起, 屏幕最多适配到960, 超出则按375计算
3 | if (windowWidth > 960) windowWidth = 375
4 |
5 | export const rpx2px = (value) => {
6 | if (typeof value === 'string') value = Number(value.replace(/[a-z]*/g, ''))
7 | return windowWidth / 750 * value
8 | }
9 |
10 | export const changeUnits = (value) => {
11 | value = String(value)
12 | return Number(value.replace(/^(\-*[0-9.]*)([a-z%]*)$/, (value, num, unit) => {
13 | switch (unit) {
14 | case 'px':
15 | num *= 1
16 | break
17 | case 'rpx':
18 | num = rpx2px(num)
19 | break
20 | default:
21 | num *= 1
22 | break
23 | }
24 | return num
25 | }))
26 | }
27 |
28 | export const resolveImage = async (img, canvas, srcName = 'src', resolveName = '$resolve') => {
29 | const imgObj = canvas.createImage()
30 | // 成功回调
31 | imgObj.onload = () => {
32 | img[resolveName](imgObj)
33 | }
34 | // 失败回调
35 | imgObj.onerror = (err) => {
36 | console.error(err)
37 | // img['$reject']()
38 | }
39 | // 设置src
40 | imgObj.src = img[srcName]
41 | }
42 |
43 | // 旧版canvas引入图片的方法
44 | // export const resolveImage = async (res, img, imgName = 'src', resolveName = '$resolve') => {
45 | // const src = img[imgName]
46 | // const $resolve = img[resolveName]
47 | // // #ifdef MP
48 | // // 如果是base64就调用base64src()方法把图片写入本地, 然后渲染临时路径
49 | // if (/^data:image\/([a-z]+);base64,/.test(src)) {
50 | // const path = await base64src(src)
51 | // $resolve({ ...res.detail, path })
52 | // return
53 | // }
54 | // // #endif
55 | // // 如果是本地图片, 直接返回
56 | // if (src.indexOf('http') !== 0) {
57 | // $resolve({ ...res.detail, path:src })
58 | // return
59 | // }
60 | // // 如果是网络图片, 则通过getImageInfo()方法获取图片宽高
61 | // uni.getImageInfo({
62 | // src: src,
63 | // success: (imgObj) => $resolve(imgObj),
64 | // fail: () => console.error('API `uni.getImageInfo` 加载图片失败', src)
65 | // })
66 | // }
67 |
68 | export function getImage(canvasId, canvas) {
69 | return new Promise((resolve, reject) => {
70 | uni.canvasToTempFilePath({
71 | canvas,
72 | canvasId,
73 | success: res => resolve(res),
74 | fail: err => reject(err)
75 | }, this)
76 | })
77 | }
78 |
--------------------------------------------------------------------------------
/packages/vue/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | *.zip
4 | *.tgz
5 | node_modules
6 | dist
7 | yarn.lock
8 | package-lock.json
9 |
10 | # local env files
11 | .env.local
12 | .env.*.local
13 |
14 | # Log files
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
19 | # Editor directories and files
20 | .idea
21 | .vscode
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
--------------------------------------------------------------------------------
/packages/vue/.npmignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/vue/README.md:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | |适配框架|npm包|最新版本|npm下载量|CDN使用量|
25 | | :-: | :-: | :-: | :-: | :-: |
26 | |`JS` / `JQ`|[lucky-canvas](https://100px.net/usage/js.html)|

|

|

|
27 | |`Vue2.x` / `Vue3.x`|[@lucky-canvas/vue](https://100px.net/usage/vue.html)|

|

|

|
28 | |`React`|[@lucky-canvas/react](https://100px.net/usage/react.html)|

|

|-|
29 | |`UniApp`|[@lucky-canvas/uni](https://100px.net/usage/uni.html)|

|

|-|
30 | |`Taro3.x`|[@lucky-canvas/taro](https://100px.net/usage/taro.html)|

|

|-|
31 | |`微信小程序`|[@lucky-canvas/mini](https://100px.net/usage/wx.html)|

|

|-|
32 |
33 |
34 |
35 |
36 |
37 | ## 在 vue2.x / vue3.x 中使用
38 |
39 | - [跳转官网 查看详情](https://100px.net/usage/vue.html)
40 |
41 |
42 |
43 | ## 🙏🙏🙏 点个Star
44 |
45 | **如果您觉得这个项目还不错, 可以在 [Github](https://github.com/LuckDraw/lucky-canvas) 上面帮我点个`star`, 支持一下作者 ☜(゚ヮ゚☜)**
46 |
47 |
--------------------------------------------------------------------------------
/packages/vue/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/index.esm.js')
--------------------------------------------------------------------------------
/packages/vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lucky-canvas/vue",
3 | "version": "0.1.11",
4 | "description": "一个支持 vue2 / vue3 的(大转盘 / 九宫格 / 老虎机)luckydraw 抽奖插件",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.esm.js",
7 | "unpkg": "dist/index.umd.js",
8 | "jsdelivr": "dist/index.umd.js",
9 | "types": "types/index.d.ts",
10 | "keywords": [
11 | "vue3抽奖",
12 | "大转盘抽奖",
13 | "九宫格抽奖",
14 | "抽奖插件",
15 | "web抽奖",
16 | "移动端抽奖",
17 | "canvas抽奖"
18 | ],
19 | "author": "ldq ",
20 | "license": "Apache-2.0",
21 | "files": [
22 | "dist",
23 | "types",
24 | "index.js"
25 | ],
26 | "scripts": {
27 | "dev": "rollup --config rollup.config.dev.js -w",
28 | "build": "rollup --config rollup.config.build.js",
29 | "switch:2": "vue-demi-switch 2 vue2",
30 | "switch:3": "vue-demi-switch 3"
31 | },
32 | "homepage": "https://100px.net",
33 | "dependencies": {
34 | "@vue/composition-api": "^1.0.0",
35 | "lucky-canvas": "^1.7.23",
36 | "vue-demi": "^0.7.4"
37 | },
38 | "peerDependencies": {
39 | "vue": "^2.0.0 || >=3.0.0-rc.0"
40 | },
41 | "peerDependenciesMeta": {
42 | "@vue/composition-api": {
43 | "optional": true
44 | }
45 | },
46 | "devDependencies": {
47 | "@rollup/plugin-commonjs": "^20.0.0",
48 | "@rollup/plugin-json": "^4.1.0",
49 | "@rollup/plugin-node-resolve": "^13.0.5",
50 | "@rollup/plugin-typescript": "^8.2.1",
51 | "@vue/compiler-sfc": "^3.0.11",
52 | "acorn-jsx": "^5.3.1",
53 | "postcss": "^8.2.8",
54 | "rollup": "^2.43.0",
55 | "rollup-plugin-delete": "^2.0.0",
56 | "rollup-plugin-dts": "^4.0.0",
57 | "rollup-plugin-postcss": "^4.0.0",
58 | "rollup-plugin-terser": "^7.0.2",
59 | "rollup-plugin-typescript2": "0.30.0",
60 | "typescript": "^4.2.3",
61 | "vue": "^3.0.11"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/vue/rollup.config.build.js:
--------------------------------------------------------------------------------
1 | import ts from 'rollup-plugin-typescript2'
2 | import dts from 'rollup-plugin-dts'
3 | import commonjs from '@rollup/plugin-commonjs'
4 | import json from '@rollup/plugin-json'
5 | import resolve from '@rollup/plugin-node-resolve'
6 | import PostCSS from 'rollup-plugin-postcss'
7 | import del from 'rollup-plugin-delete'
8 | import { terser } from 'rollup-plugin-terser'
9 | import pkg from './package.json'
10 |
11 | export default [
12 | {
13 | input: 'src/index.ts',
14 | output: [
15 | {
16 | file: pkg.main,
17 | format: 'cjs',
18 | sourcemap: true,
19 | },
20 | {
21 | file: pkg.module,
22 | format: 'esm',
23 | sourcemap: true,
24 | },
25 | {
26 | file: pkg.jsdelivr,
27 | format: 'umd',
28 | name: 'VueLuckyCanvas',
29 | sourcemap: false,
30 | globals: {
31 | 'vue-demi': 'VueDemi',
32 | 'lucky-canvas': 'LuckyCanvas',
33 | },
34 | },
35 | ],
36 | plugins: [
37 | ts(),
38 | json(),
39 | resolve(),
40 | commonjs(),
41 | PostCSS(),
42 | terser(),
43 | ],
44 | external: ['vue-demi'],
45 | }, {
46 | input: "dist/src/index.d.ts",
47 | output: [
48 | {
49 | file: "types/index.d.ts",
50 | format: "es"
51 | }
52 | ],
53 | plugins: [
54 | dts(),
55 | del({
56 | targets: 'dist/src',
57 | hook: 'buildEnd'
58 | })
59 | ],
60 | },
61 | ]
62 |
--------------------------------------------------------------------------------
/packages/vue/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import ts from 'rollup-plugin-typescript2'
2 | import commonjs from '@rollup/plugin-commonjs'
3 | import json from '@rollup/plugin-json'
4 | import resolve from '@rollup/plugin-node-resolve'
5 | import PostCSS from 'rollup-plugin-postcss'
6 | import pkg from './package.json'
7 |
8 | export default [
9 | {
10 | input: 'src/index.ts',
11 | output: [
12 | {
13 | file: pkg.module,
14 | format: 'esm',
15 | sourcemap: true,
16 | },
17 | {
18 | file: pkg.jsdelivr,
19 | format: 'umd',
20 | name: 'VueLuckyCanvas',
21 | sourcemap: true,
22 | globals: {
23 | 'vue-demi': 'VueDemi',
24 | 'lucky-canvas': 'LuckyCanvas',
25 | },
26 | },
27 | ],
28 | plugins: [
29 | ts(),
30 | json(),
31 | resolve(),
32 | commonjs(),
33 | PostCSS(),
34 | ],
35 | external: ['vue-demi'],
36 | }
37 | ]
38 |
--------------------------------------------------------------------------------
/packages/vue/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue'
3 | const component: DefineComponent<{}, {}, any>
4 | export default component
5 | }
6 |
7 | declare module '*.json' {
8 | const value: {
9 | name: string
10 | version: string
11 | }
12 | export default value
13 | }
14 |
--------------------------------------------------------------------------------
/packages/vue/src/components/LuckyGrid.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue-demi'
2 | import { LuckyGrid } from 'lucky-canvas'
3 | import h from "../utils/h-demi"
4 | // @ts-ignore
5 | import { name, version } from '../../package.json'
6 |
7 | export default defineComponent({
8 | name: 'LuckyGrid',
9 | props: {
10 | width: {
11 | type: [String, Number],
12 | },
13 | height: {
14 | type: [String, Number],
15 | },
16 | cols: {
17 | type: [String, Number],
18 | default: 3
19 | },
20 | rows: {
21 | type: [String, Number],
22 | default: 3
23 | },
24 | blocks: {
25 | type: Array,
26 | default: () => []
27 | },
28 | prizes: {
29 | type: Array,
30 | default: () => []
31 | },
32 | buttons: {
33 | type: Array,
34 | default: () => []
35 | },
36 | button: { // 老版本要兼容这个属性
37 | type: Object
38 | },
39 | defaultStyle: {
40 | type: Object,
41 | default: () => ({})
42 | },
43 | activeStyle: {
44 | type: Object,
45 | default: () => ({})
46 | },
47 | defaultConfig: {
48 | type: Object,
49 | default: () => ({})
50 | },
51 | },
52 | emits: [
53 | 'start',
54 | 'end',
55 | 'success',
56 | 'error',
57 | 'finally',
58 | ],
59 | watch: {
60 | cols (newData, oldData) {
61 | this.lucky && ((this.lucky as any).cols = newData)
62 | },
63 | rows (newData, oldData) {
64 | this.lucky && ((this.lucky as any).rows = newData)
65 | },
66 | blocks (newData, oldData) {
67 | this.lucky && ((this.lucky as any).blocks = newData)
68 | },
69 | prizes (newData, oldData) {
70 | this.lucky && ((this.lucky as any).prizes = newData)
71 | },
72 | buttons (newData, oldData) {
73 | this.lucky && ((this.lucky as any).buttons = newData)
74 | },
75 | button (newData, oldData) {
76 | this.lucky && ((this.lucky as any).button = newData)
77 | },
78 | },
79 | data() {
80 | return {
81 | lucky: null as LuckyGrid | null,
82 | };
83 | },
84 | mounted () {
85 | // 添加版本信息到标签上, 方便定位版本问题
86 | if (this.$refs.myLucky) {
87 | const dom = this.$refs.myLucky as HTMLDivElement
88 | dom.setAttribute('package', `${name}@${version}`)
89 | }
90 | // 开始创建组件
91 | try {
92 | this.initLucky()
93 | this.$emit('success')
94 | } catch (err) {
95 | this.$emit('error', err)
96 | } finally {
97 | this.$emit('finally')
98 | }
99 | },
100 | methods: {
101 | initLucky () {
102 | this.lucky = new LuckyGrid({
103 | flag: 'WEB',
104 | width: String(this.width),
105 | height: String(this.height),
106 | divElement: this.$refs.myLucky as HTMLDivElement,
107 | rAF: window.requestAnimationFrame,
108 | setTimeout: window.setTimeout,
109 | setInterval: window.setInterval,
110 | clearTimeout: window.clearTimeout,
111 | clearInterval: window.clearInterval,
112 | }, {
113 | ...this.$props as any,
114 | start: (e, btn) => {
115 | this.$emit('start', e, btn)
116 | },
117 | end: (prize) => {
118 | this.$emit('end', prize)
119 | },
120 | })
121 | },
122 | init () {
123 | this.lucky && this.lucky.init()
124 | },
125 | /**
126 | * play方法可以让抽奖开始旋转
127 | */
128 | play () {
129 | this.lucky?.play()
130 | },
131 | /**
132 | * stop方法可以传递一个中奖索引, 来停止游戏
133 | * @param index 中奖索引
134 | */
135 | stop (index?: number) {
136 | this.lucky?.stop(index)
137 | },
138 | },
139 | render() {
140 | return h('div', { ref: 'myLucky' })
141 | }
142 | })
143 |
--------------------------------------------------------------------------------
/packages/vue/src/components/LuckyWheel.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue-demi'
2 | import { LuckyWheel } from 'lucky-canvas'
3 | import h from "../utils/h-demi"
4 | // @ts-ignore
5 | import { name, version } from '../../package.json'
6 |
7 | export default defineComponent({
8 | name: 'LuckyWheel',
9 | props: {
10 | width: {
11 | type: [String, Number],
12 | },
13 | height: {
14 | type: [String, Number],
15 | },
16 | blocks: {
17 | type: Array,
18 | default: () => []
19 | },
20 | prizes: {
21 | type: Array,
22 | default: () => []
23 | },
24 | buttons: {
25 | type: Array,
26 | default: () => []
27 | },
28 | defaultStyle: {
29 | type: Object,
30 | default: () => ({})
31 | },
32 | defaultConfig: {
33 | type: Object,
34 | default: () => ({})
35 | }
36 | },
37 | emits: [
38 | 'start',
39 | 'end',
40 | 'success',
41 | 'error',
42 | 'finally',
43 | ],
44 | watch: {
45 | blocks (newData, oldData) {
46 | this.lucky && ((this.lucky as any).blocks = newData)
47 | },
48 | prizes (newData, oldData) {
49 | this.lucky && ((this.lucky as any).prizes = newData)
50 | },
51 | buttons (newData, oldData) {
52 | this.lucky && ((this.lucky as any).buttons = newData)
53 | },
54 | },
55 | data() {
56 | return {
57 | lucky: null as LuckyWheel | null,
58 | };
59 | },
60 | mounted () {
61 | // 添加版本信息到标签上, 方便定位版本问题
62 | if (this.$refs.myLucky) {
63 | const dom = this.$refs.myLucky as HTMLDivElement
64 | dom.setAttribute('package', `${name}@${version}`)
65 | }
66 | // 开始创建组件
67 | try {
68 | this.initLucky()
69 | this.$emit('success')
70 | } catch (err) {
71 | this.$emit('error', err)
72 | } finally {
73 | this.$emit('finally')
74 | }
75 | },
76 | methods: {
77 | initLucky () {
78 | this.lucky = new LuckyWheel({
79 | flag: 'WEB',
80 | width: String(this.width),
81 | height: String(this.height),
82 | divElement: this.$refs.myLucky as HTMLDivElement,
83 | rAF: window.requestAnimationFrame,
84 | setTimeout: window.setTimeout,
85 | setInterval: window.setInterval,
86 | clearTimeout: window.clearTimeout,
87 | clearInterval: window.clearInterval,
88 | }, {
89 | ...this.$props as any,
90 | start: (e) => {
91 | this.$emit('start', e)
92 | },
93 | end: (prize) => {
94 | this.$emit('end', prize)
95 | },
96 | })
97 | },
98 | init () {
99 | this.lucky && this.lucky.init()
100 | },
101 | /**
102 | * play方法可以让抽奖开始旋转
103 | */
104 | play () {
105 | this.lucky?.play()
106 | },
107 | /**
108 | * stop方法可以传递一个中奖索引, 来停止游戏
109 | * @param index 中奖索引
110 | */
111 | stop (index?: number) {
112 | this.lucky?.stop(index)
113 | },
114 | },
115 | render() {
116 | return h('div', { ref: 'myLucky' })
117 | }
118 | })
119 |
--------------------------------------------------------------------------------
/packages/vue/src/components/SlotMachine.ts:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue-demi'
2 | import { SlotMachine } from 'lucky-canvas'
3 | import h from "../utils/h-demi"
4 | // @ts-ignore
5 | import { name, version } from '../../package.json'
6 |
7 | export default defineComponent({
8 | name: 'SlotMachine',
9 | props: {
10 | width: {
11 | type: [String, Number],
12 | },
13 | height: {
14 | type: [String, Number],
15 | },
16 | blocks: {
17 | type: Array,
18 | default: () => []
19 | },
20 | prizes: {
21 | type: Array,
22 | default: () => []
23 | },
24 | slots: {
25 | type: Array,
26 | default: () => []
27 | },
28 | defaultStyle: {
29 | type: Object,
30 | default: () => ({})
31 | },
32 | defaultConfig: {
33 | type: Object,
34 | default: () => ({})
35 | },
36 | },
37 | watch: {
38 | blocks (newData, oldData) {
39 | this.lucky && ((this.lucky as any).blocks = newData)
40 | },
41 | slots (newData, oldData) {
42 | this.lucky && ((this.lucky as any).slots = newData)
43 | },
44 | prizes (newData, oldData) {
45 | this.lucky && ((this.lucky as any).prizes = newData)
46 | },
47 | },
48 | data() {
49 | return {
50 | lucky: null as SlotMachine | null,
51 | };
52 | },
53 | mounted () {
54 | // 添加版本信息到标签上, 方便定位版本问题
55 | if (this.$refs.myLucky) {
56 | const dom = this.$refs.myLucky as HTMLDivElement
57 | dom.setAttribute('package', `${name}@${version}`)
58 | }
59 | // 开始创建组件
60 | try {
61 | this.initLucky()
62 | this.$emit('success')
63 | } catch (err) {
64 | this.$emit('error', err)
65 | } finally {
66 | this.$emit('finally')
67 | }
68 | },
69 | methods: {
70 | initLucky () {
71 | this.lucky = new SlotMachine({
72 | flag: 'WEB',
73 | width: String(this.width),
74 | height: String(this.height),
75 | divElement: this.$refs.myLucky as HTMLDivElement,
76 | rAF: window.requestAnimationFrame,
77 | setTimeout: window.setTimeout,
78 | setInterval: window.setInterval,
79 | clearTimeout: window.clearTimeout,
80 | clearInterval: window.clearInterval,
81 | }, {
82 | ...this.$props as any,
83 | start: (e: Event) => {
84 | this.$emit('start', e)
85 | },
86 | end: (btn) => {
87 | this.$emit('end', btn)
88 | },
89 | })
90 | },
91 | init () {
92 | this.lucky && this.lucky.init()
93 | },
94 | /**
95 | * play方法可以让抽奖开始旋转
96 | */
97 | play () {
98 | this.lucky?.play()
99 | },
100 | /**
101 | * stop方法可以传递一个中奖索引, 来停止游戏
102 | * @param index 中奖索引
103 | */
104 | stop (index: number) {
105 | this.lucky?.stop(index)
106 | },
107 | },
108 | render() {
109 | return h('div', { ref: 'myLucky' })
110 | }
111 | })
112 |
--------------------------------------------------------------------------------
/packages/vue/src/index.ts:
--------------------------------------------------------------------------------
1 | import { isVue2 } from "vue-demi"
2 | import LuckyWheel from "./components/LuckyWheel"
3 | import LuckyGrid from "./components/LuckyGrid"
4 | import SlotMachine from "./components/SlotMachine"
5 |
6 |
7 | const install = (app: { component: Function }) => {
8 | app.component('LuckyWheel', LuckyWheel)
9 | app.component('LuckyGrid', LuckyGrid)
10 | app.component('SlotMachine', SlotMachine)
11 | }
12 |
13 | if (typeof window !== 'undefined' && (window as any).Vue && isVue2) {
14 | install((window as any).Vue)
15 | }
16 |
17 | export { install, LuckyWheel, LuckyGrid, SlotMachine }
18 | export default { install }
19 |
--------------------------------------------------------------------------------
/packages/vue/src/utils/h-demi.ts:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | import { h as hDemi, isVue2 } from 'vue-demi'
3 |
4 | interface Options {
5 | props?: Object,
6 | domProps?: Object
7 | on?: Object
8 | }
9 |
10 | const adaptOnsV3 = (ons: Object) => {
11 | if (!ons) return null
12 | return Object.entries(ons).reduce((ret, [key, handler]) => {
13 | key = key.charAt(0).toUpperCase() + key.slice(1)
14 | key = `on${key}`
15 | return { ...ret, [key]: handler }
16 | }, {})
17 | }
18 |
19 | const h = (type: String | Object, options: Options & any = {}, chidren?: any) => {
20 | if (isVue2) return hDemi(type, options, chidren)
21 | const { props, domProps, on, ...extraOptions } = options
22 | let ons = adaptOnsV3(on)
23 | const params = { ...extraOptions, ...props, ...domProps, ...ons }
24 | return hDemi(type, params, chidren)
25 | }
26 |
27 | const slot = (s, attrs?) => {
28 | if (typeof s == 'function') return s(attrs)
29 | return s
30 | }
31 |
32 | export { slot }
33 |
34 | export default h
--------------------------------------------------------------------------------
/packages/vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES5",
4 | "module": "ESNext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "removeComments": true,
10 | "skipLibCheck": false,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "resolveJsonModule": true,
14 | "sourceMap": true,
15 | "baseUrl": ".",
16 | "declaration": true,
17 | "declarationDir": "./types",
18 | "outDir": "./dist",
19 | },
20 | "include": [
21 | "src/**/*.ts",
22 | "src/**/*.tsx",
23 | "src/**/*.vue",
24 | "shims-vue.d.ts",
25 | ],
26 | "exclude": [
27 | "node_modules"
28 | ]
29 | }
--------------------------------------------------------------------------------
/packages/vue/vue-demi.js:
--------------------------------------------------------------------------------
1 | ;(function (window) {
2 | if (window.VueDemi) {
3 | return
4 | }
5 | var VueDemi = {}
6 | var Vue = window.Vue
7 | if (Vue) {
8 | if (Vue.version.slice(0, 2) === '2.') {
9 | var VueCompositionAPI = window.VueCompositionAPI
10 | if (VueCompositionAPI) {
11 | for (var key in VueCompositionAPI) {
12 | VueDemi[key] = VueCompositionAPI[key]
13 | }
14 | VueDemi.isVue2 = true
15 | VueDemi.isVue3 = false
16 | VueDemi.install = function (){}
17 | VueDemi.Vue = Vue
18 | VueDemi.Vue2 = Vue
19 | VueDemi.version = Vue.version
20 | } else {
21 | console.error(
22 | '[vue-demi] no VueCompositionAPI instance found, please be sure to import `@vue/composition-api` before `vue-demi`.'
23 | )
24 | }
25 | } else if (Vue.version.slice(0, 2) === '3.') {
26 | for (var key in Vue) {
27 | VueDemi[key] = Vue[key]
28 | }
29 | VueDemi.isVue2 = false
30 | VueDemi.isVue3 = true
31 | VueDemi.install = function (){}
32 | VueDemi.Vue = Vue
33 | VueDemi.Vue2 = undefined
34 | VueDemi.version = Vue.version
35 | VueDemi.set = function(target, key, val) {
36 | if (Array.isArray(target)) {
37 | target.length = Math.max(target.length, key)
38 | target.splice(key, 1, val)
39 | return val
40 | }
41 | target[key] = val
42 | return val
43 | }
44 | VueDemi.del = function(target, key) {
45 | if (Array.isArray(target)) {
46 | target.splice(key, 1)
47 | return
48 | }
49 | delete target[key]
50 | }
51 | } else {
52 | console.error('[vue-demi] Vue version ' + Vue.version + ' is unsupported.')
53 | }
54 | } else {
55 | console.error(
56 | '[vue-demi] no Vue instance found, please be sure to import `vue` before `vue-demi`.'
57 | )
58 | }
59 | window.VueDemi = VueDemi
60 | })(window);
--------------------------------------------------------------------------------
/packages/vue/vue2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | vue2.x
8 |
9 |
10 |
21 |
22 |
33 |
34 |
35 |
36 |
37 |
38 |
76 |
77 |
--------------------------------------------------------------------------------
/packages/vue/vue3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | vue3.x
8 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
64 |
65 |
--------------------------------------------------------------------------------