├── utils
├── runtime.js
└── util.js
├── app.wxss
├── components
├── divider
│ ├── index.json
│ ├── index.wxml
│ ├── index.js
│ └── index.wxss
├── grid
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── grids
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── canvasdrawer
│ ├── canvasdrawer.json
│ ├── canvasdrawer.wxss
│ ├── canvasdrawer.wxml
│ └── canvasdrawer.js
├── brushPoint
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── colorBox
│ ├── index.json
│ ├── index.wxml
│ ├── index.js
│ └── index.wxss
├── customButton
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── customToggle
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── helpers
│ ├── mergeOptionsToData.js
│ ├── isEmpty.js
│ ├── colors.js
│ ├── checkIPhoneX.js
│ ├── compareVersion.js
│ ├── arrayTreeFilter.js
│ ├── classNames.js
│ ├── safeAreaBehavior.js
│ ├── debounce.js
│ ├── gestures.js
│ ├── safeSetDataBehavior.js
│ ├── eventsMixin.js
│ ├── shallowEqual.js
│ ├── computedBehavior.js
│ ├── createFieldsStore.js
│ ├── relationsBehavior.js
│ ├── baseComponent.js
│ ├── funcBehavior.js
│ ├── styleToCssString.js
│ └── popupMixin.js
└── aiModels
│ ├── autoPainter
│ └── autoPainter.js
│ └── classifier
│ └── classifier.js
├── images
├── mini-code.jpg
├── gif
│ ├── ai-painter.gif
│ └── ai-classifier.gif
├── models
│ └── flower.png
└── games
│ └── common
│ ├── idea.png
│ ├── think.png
│ ├── btn_back.png
│ ├── btn_back1.png
│ ├── btn_erase.png
│ ├── btn_refresh.png
│ ├── btn_tranCan.png
│ ├── btn_pageview.png
│ └── btn_tranCan1.png
├── sitemap.json
├── pages
├── index
│ ├── index.wxss
│ ├── index.js
│ ├── index.json
│ └── index.wxml
├── share
│ ├── share.wxml
│ ├── share.wxss
│ ├── share.json
│ └── share.js
├── ai-classifier
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
└── ai-painter
│ ├── index.json
│ ├── index.wxml
│ ├── index.wxss
│ └── index.js
├── .gitignore
├── project.private.config.json
├── app.json
├── package.json
├── project.config.json
├── app.js
├── README.md
└── miniprogram_npm
├── fetch-wechat
├── index.js.map
└── index.js
└── regenerator-runtime
├── index.js.map
└── index.js
/utils/runtime.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 |
3 |
--------------------------------------------------------------------------------
/components/divider/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true
3 | }
--------------------------------------------------------------------------------
/components/grid/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true
3 | }
--------------------------------------------------------------------------------
/components/grids/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true
3 | }
--------------------------------------------------------------------------------
/components/canvasdrawer/canvasdrawer.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true
3 | }
--------------------------------------------------------------------------------
/components/brushPoint/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component":true,
3 | "usingComponents":{}
4 | }
--------------------------------------------------------------------------------
/components/colorBox/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/components/canvasdrawer/canvasdrawer.wxss:
--------------------------------------------------------------------------------
1 | .board {
2 | position: fixed;
3 | top: 2000rpx;
4 | }
--------------------------------------------------------------------------------
/components/customButton/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/components/customToggle/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/components/grids/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/images/mini-code.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/mini-code.jpg
--------------------------------------------------------------------------------
/images/gif/ai-painter.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/gif/ai-painter.gif
--------------------------------------------------------------------------------
/images/models/flower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/models/flower.png
--------------------------------------------------------------------------------
/images/games/common/idea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/idea.png
--------------------------------------------------------------------------------
/images/gif/ai-classifier.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/gif/ai-classifier.gif
--------------------------------------------------------------------------------
/images/games/common/think.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/think.png
--------------------------------------------------------------------------------
/images/games/common/btn_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_back.png
--------------------------------------------------------------------------------
/images/games/common/btn_back1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_back1.png
--------------------------------------------------------------------------------
/images/games/common/btn_erase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_erase.png
--------------------------------------------------------------------------------
/images/games/common/btn_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_refresh.png
--------------------------------------------------------------------------------
/images/games/common/btn_tranCan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_tranCan.png
--------------------------------------------------------------------------------
/components/canvasdrawer/canvasdrawer.wxml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/games/common/btn_pageview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_pageview.png
--------------------------------------------------------------------------------
/images/games/common/btn_tranCan1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaocc1106/ai-painter-wx-miniprogram/HEAD/images/games/common/btn_tranCan1.png
--------------------------------------------------------------------------------
/components/divider/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ text }}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | page{
2 | height: 100%;
3 | width:100%;
4 | }
5 |
6 | .container #intro {
7 | margin: 30rpx;
8 | }
9 |
10 | .container #intro text {
11 | font-size: 30rpx;
12 | color: gray;
13 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows
2 | [Dd]esktop.ini
3 | Thumbs.db
4 | $RECYCLE.BIN/
5 |
6 | # macOS
7 | .DS_Store
8 | .fseventsd
9 | .Spotlight-V100
10 | .TemporaryItems
11 | .Trashes
12 |
13 | # Node.js
14 | node_modules/
15 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | // pages/index/index.js
2 |
3 | Page({
4 |
5 | jumpTo: function (event) {
6 | let page = event.target.dataset.dest;
7 | wx.navigateTo({
8 | url: '/pages/' + page + '/index',
9 | })
10 | },
11 | })
--------------------------------------------------------------------------------
/components/colorBox/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/components/customButton/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{text}}
6 |
--------------------------------------------------------------------------------
/project.private.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
3 | "projectname": "ai-painter-wx-miniprogram",
4 | "setting": {
5 | "compileHotReLoad": true
6 | }
7 | }
--------------------------------------------------------------------------------
/components/customToggle/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{text}}
6 |
--------------------------------------------------------------------------------
/pages/share/share.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/pages/share/share.wxss:
--------------------------------------------------------------------------------
1 | .share-image {
2 | width: 94%;
3 | height: 1000rpx;
4 | margin: 0 auto;
5 | margin-left: 3%;
6 | margin-top: 20rpx;
7 | border-radius: 10rpx;
8 | box-shadow:0 1px 3px rgba(0,0,0,.1)
9 | }
10 |
11 | button {
12 | width: 44%;
13 | float: left;
14 | margin-left: 4%;
15 | margin-top: 20rpx;
16 | color: #333;
17 | background: #ffcc01;
18 | }
--------------------------------------------------------------------------------
/pages/share/share.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarBackgroundColor": "#ffcc01",
3 | "navigationBarTextStyle": "black",
4 | "navigationBarTitleText": "分享保存",
5 | "backgroundColor": "#ffffff",
6 | "backgroundTextStyle": "dark",
7 | "enablePullDownRefresh": false,
8 | "disableScroll": true,
9 | "usingComponents": {
10 | "canvasdrawer": "/components/canvasdrawer/canvasdrawer"
11 | }
12 | }
--------------------------------------------------------------------------------
/components/helpers/mergeOptionsToData.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 过滤对象的函数属性
3 | * @param {Object} opts
4 | */
5 | const mergeOptionsToData = (opts = {}) => {
6 | const options = Object.assign({}, opts)
7 |
8 | for (const key in options) {
9 | if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
10 | delete options[key]
11 | }
12 | }
13 |
14 | return options
15 | }
16 |
17 | export default mergeOptionsToData
18 |
--------------------------------------------------------------------------------
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "AI 涂鸦",
3 | "navigationBarBackgroundColor": "#ffcc01",
4 | "navigationBarTextStyle": "black",
5 | "backgroundColor": "#ffffff",
6 | "backgroundTextStyle": "dark",
7 | "enablePullDownRefresh": false,
8 | "disableScroll": true,
9 | "usingComponents": {
10 | "my-divider": "/components/divider/index",
11 | "my-grids": "/components/grids/index",
12 | "my-grid": "/components/grid/index"
13 | }
14 | }
--------------------------------------------------------------------------------
/components/helpers/isEmpty.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Checks if a value is empty.
3 | */
4 | function isEmpty(value) {
5 | if (Array.isArray(value)) {
6 | return value.length === 0
7 | } else if (typeof value === 'object') {
8 | if (value) {
9 | for (const _ in value) {
10 | return false
11 | }
12 | }
13 | return true
14 | } else {
15 | return !value
16 | }
17 | }
18 |
19 | export default isEmpty
--------------------------------------------------------------------------------
/components/brushPoint/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/pages/ai-classifier/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarBackgroundColor": "#ffcc01",
3 | "navigationBarTextStyle": "black",
4 | "navigationBarTitleText": "AI猜图",
5 | "backgroundColor": "#ffffff",
6 | "backgroundTextStyle": "dark",
7 | "enablePullDownRefresh": false,
8 | "disableScroll": true,
9 | "usingComponents": {
10 | "brush-point": "/components/brushPoint/index",
11 | "color-box": "/components/colorBox/index",
12 | "custom-toggle": "/components/customToggle/index",
13 | "custom-button": "/components/customButton/index"
14 | }
15 | }
--------------------------------------------------------------------------------
/pages/ai-painter/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarBackgroundColor": "#ffcc01",
3 | "navigationBarTextStyle": "black",
4 | "navigationBarTitleText": "AI涂鸦",
5 | "backgroundColor": "#ffffff",
6 | "backgroundTextStyle": "dark",
7 | "enablePullDownRefresh": false,
8 | "disableScroll": true,
9 | "usingComponents": {
10 | "brush-point": "/components/brushPoint/index",
11 | "color-box": "/components/colorBox/index",
12 | "custom-toggle": "/components/customToggle/index",
13 | "custom-button": "/components/customButton/index"
14 | }
15 | }
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index",
4 | "pages/ai-painter/index",
5 | "pages/ai-classifier/index",
6 | "pages/share/share"
7 | ],
8 | "window": {
9 | "backgroundTextStyle": "light",
10 | "navigationBarBackgroundColor": "#f0efef",
11 | "navigationBarTitleText": "AI涂鸦",
12 | "navigationBarTextStyle": "black"
13 | },
14 | "sitemapLocation": "sitemap.json",
15 | "plugins": {
16 | "tfjsPlugin": {
17 | "version": "0.1.0",
18 | "provider": "wx6afed118d9e81df9"
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 基于quickdraw数据集训练的涂鸦分类以及自动涂鸦小模型,代码仓库:https://github.com/zhaocc1106/ai-painter-wx-miniprogram
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/components/helpers/colors.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Don't modify this file!
3 | * Colors generated by themes!
4 | */
5 |
6 | /* eslint-disable */
7 |
8 | export const colors = {
9 | 'light': '#ddd',
10 | 'stable': '#b2b2b2',
11 | 'positive': '#387ef5',
12 | 'calm': '#11c1f3',
13 | 'balanced': '#33cd5f',
14 | 'energized': '#ffc900',
15 | 'assertive': '#ef473a',
16 | 'royal': '#886aea',
17 | 'dark': '#444',
18 | }
19 |
20 | export const isPresetColor = (color) => {
21 | if (!color) {
22 | return false
23 | }
24 | return colors[color] ? colors[color] : color
25 | }
26 |
27 | /* eslint-enable */
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ai-painter",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@tensorflow/tfjs-backend-cpu": "2.0.1",
13 | "@tensorflow/tfjs-backend-webgl": "2.0.1",
14 | "@tensorflow/tfjs-backend-wasm": "2.0.1",
15 | "@tensorflow/tfjs-core": "2.0.1",
16 | "@tensorflow/tfjs-layers": "2.0.1",
17 | "@tensorflow/tfjs-converter": "2.0.1",
18 | "fetch-wechat": "0.0.3",
19 | "regenerator-runtime": "0.13.5"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/components/customToggle/index.wxss:
--------------------------------------------------------------------------------
1 | /* components/customToggle/index.wxss */
2 |
3 | .toggle-container {
4 | width: 100%;
5 | position: relative;
6 | display: flex;
7 | flex-direction: row;
8 | justify-content: center;
9 | }
10 |
11 | .toggle-container .icon{
12 | width: 50rpx;
13 | height: 50rpx;
14 | z-index: 3;
15 | }
16 |
17 | .toggle-container .text{
18 | height: 50rpx;
19 | line-height: 50rpx;
20 | text-align: left;
21 | margin-left: 5rpx;
22 | z-index: 3;
23 | font-size: 30rpx;
24 | }
25 |
26 | .toggle-container .toggle-bg{
27 | z-index: 1;
28 | position: absolute;
29 | height: 100%;
30 | background: white;
31 | border-radius: 20rpx;
32 | }
33 |
--------------------------------------------------------------------------------
/components/helpers/checkIPhoneX.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取系统信息
3 | */
4 |
5 | let systemInfo = null
6 |
7 | export const getSystemInfo = (isForce) => {
8 | if (!systemInfo || isForce) {
9 | try {
10 | systemInfo = wx.getSystemInfoSync()
11 | } catch(e) { /* Ignore */ }
12 | }
13 |
14 | return systemInfo
15 | }
16 |
17 | // iPhoneX 竖屏安全区域
18 | export const safeAreaInset = {
19 | top: 88, // StatusBar & NavBar
20 | left: 0,
21 | right: 0,
22 | bottom: 34, // Home Indicator
23 | }
24 |
25 | const isIPhoneX = ({ model, platform }) => {
26 | return /iPhone X/.test(model) && platform === 'ios'
27 | }
28 |
29 | export const checkIPhoneX = (isForce) => isIPhoneX(getSystemInfo(isForce))
30 |
--------------------------------------------------------------------------------
/components/grids/index.wxss:
--------------------------------------------------------------------------------
1 | .wux-grids {
2 | position: relative;
3 | box-sizing: border-box;
4 | overflow: hidden;
5 | background-color: #fff
6 | }
7 | .wux-grids--bordered:before {
8 | content: " ";
9 | position: absolute;
10 | left: 0;
11 | top: 0;
12 | right: 0;
13 | height: 1PX;
14 | border-top: 1PX solid #d9d9d9;
15 | color: #d9d9d9;
16 | transform-origin: 0 0;
17 | transform: scaleY(.5)
18 | }
19 | .wux-grids--bordered:after {
20 | content: " ";
21 | position: absolute;
22 | left: 0;
23 | top: 0;
24 | width: 1PX;
25 | bottom: 0;
26 | border-left: 1PX solid #d9d9d9;
27 | color: #d9d9d9;
28 | transform-origin: 0 0;
29 | transform: scaleX(.5)
30 | }
--------------------------------------------------------------------------------
/components/helpers/compareVersion.js:
--------------------------------------------------------------------------------
1 | function compareVersion(v1, v2) {
2 | const $v1 = v1.split('.')
3 | const $v2 = v2.split('.')
4 | const len = Math.max($v1.length, $v2.length)
5 |
6 | while ($v1.length < len) {
7 | $v1.push('0')
8 | }
9 | while ($v2.length < len) {
10 | $v2.push('0')
11 | }
12 |
13 | for (let i = 0; i < len; i++) {
14 | const num1 = parseInt($v1[i])
15 | const num2 = parseInt($v2[i])
16 |
17 | if (num1 > num2) {
18 | return 1
19 | } else if (num1 < num2) {
20 | return -1
21 | }
22 | }
23 |
24 | return 0
25 | }
26 |
27 | export default compareVersion
28 |
--------------------------------------------------------------------------------
/components/grid/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{ label }}
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/components/helpers/arrayTreeFilter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * https://github.com/afc163/array-tree-filter
3 | */
4 | function arrayTreeFilter(data, filterFn, options) {
5 | options = options || {}
6 | options.childrenKeyName = options.childrenKeyName || 'children'
7 | let children = data || []
8 | const result = []
9 | let level = 0
10 | do {
11 | const foundItem = children.filter(function(item) {
12 | return filterFn(item, level)
13 | })[0]
14 | if (!foundItem) {
15 | break
16 | }
17 | result.push(foundItem)
18 | children = foundItem[options.childrenKeyName] || []
19 | level += 1
20 | } while (children.length > 0)
21 | return result
22 | }
23 |
24 | export default arrayTreeFilter
--------------------------------------------------------------------------------
/components/colorBox/index.js:
--------------------------------------------------------------------------------
1 | // components/brushPoint/index.js
2 | Component({
3 | options: {
4 | multipleSlots: true // 在组件定义时的选项中启用多slot支持
5 | },
6 | /**
7 | * 组件的属性列表
8 | * 用于组件自定义设置
9 | */
10 | properties: {
11 | selected: {
12 | type: Boolean,
13 | value: false
14 | },
15 | },
16 |
17 | /**
18 | * 私有数据,组件的初始数据
19 | * 可用于模版渲染
20 | */
21 | data: {
22 | },
23 |
24 | attached: function () {
25 | this.setData({
26 | color: this.dataset.color
27 | });
28 | },
29 |
30 |
31 | /**
32 | * 组件的方法列表
33 | * 更新属性和数据的方法与更新页面数据的方法类似
34 | */
35 | methods: {
36 |
37 | /*
38 | * 内部私有方法建议以下划线开头
39 | * triggerEvent 用于触发事件
40 | */
41 |
42 |
43 | // 选中笔刷
44 | _select(e) {
45 | this.triggerEvent("select");
46 | }
47 | }
48 | })
--------------------------------------------------------------------------------
/components/customButton/index.wxss:
--------------------------------------------------------------------------------
1 | /* components/customToggle/index.wxss */
2 |
3 | .btn-container {
4 | width: 100%;
5 | position: relative;
6 | display: flex;
7 | flex-direction: row;
8 | justify-content: center;
9 | }
10 |
11 | .btn-container .icon{
12 | margin-top: 5rpx;
13 | width: 45rpx;
14 | height: 40rpx;
15 | z-index: 3;
16 | }
17 |
18 | .btn-container .text{
19 | height: 50rpx;
20 | line-height: 50rpx;
21 | text-align: left;
22 | margin-left: 5rpx;
23 | font-size: 0.75em;
24 | color: gray;
25 | z-index: 3;
26 | }
27 |
28 | .btn-container .btn-bg{
29 | z-index: 1;
30 | opacity: 0;
31 | position: absolute;
32 | height: 100%;
33 | background: gray;
34 | border-radius: 20rpx;
35 | animation: bgShow 0.2s linear 1;
36 | }
37 |
38 | /* animation */
39 | @keyframes bgShow{
40 | from, to{
41 | opacity: 0
42 | }
43 |
44 | 50%{
45 | opacity: 0.5
46 | }
47 | }
--------------------------------------------------------------------------------
/components/customToggle/index.js:
--------------------------------------------------------------------------------
1 | // components/brushPoint/index.js
2 | Component({
3 | options: {
4 | multipleSlots: true // 在组件定义时的选项中启用多slot支持
5 | },
6 | /**
7 | * 组件的属性列表
8 | * 用于组件自定义设置
9 | */
10 | properties: {
11 | imgUrl: {
12 | type: String,
13 | value: ""
14 | },
15 |
16 | width:{
17 | type: String,
18 | value: "10rpx"
19 | },
20 |
21 | text:{
22 | type: String,
23 | value: ""
24 | },
25 |
26 | selected: {
27 | type: Boolean,
28 | value: false
29 | },
30 | },
31 |
32 | /**
33 | * 私有数据,组件的初始数据
34 | * 可用于模版渲染
35 | */
36 | data: {
37 | },
38 |
39 |
40 | /**
41 | * 组件的方法列表
42 | * 更新属性和数据的方法与更新页面数据的方法类似
43 | */
44 | methods: {
45 |
46 | /*
47 | * 内部私有方法建议以下划线开头
48 | * triggerEvent 用于触发事件
49 | */
50 |
51 | _click(){
52 | this.triggerEvent("clickEvent");
53 | }
54 | }
55 | })
--------------------------------------------------------------------------------
/components/brushPoint/index.wxss:
--------------------------------------------------------------------------------
1 | /* components/brushPoint/index.wxss */
2 |
3 | .brush-point-container {
4 | position: relative;
5 | height: 100%;
6 | width: 100%;
7 | display: flex;
8 | flex-direction: row;
9 | align-items: center;
10 | justify-content: center;
11 | }
12 |
13 | .brush-point-front {
14 | z-index: 3;
15 | position: absolute;
16 | left: 32.5;
17 | top: 32.5;
18 | }
19 |
20 | .brush-point-bg{
21 | z-index: 2;
22 | }
23 |
24 | .click-effect {
25 | background: gray;
26 | opacity: 0;
27 | width: 64rpx;
28 | height: 64rpx;
29 | border-radius: 64rpx;
30 | z-index: 1;
31 | position: absolute;
32 | left: 50%;
33 | margin-left: -32rpx;
34 | animation-delay: 0.1s;
35 | animation: click-effect-show 0.1s linear 1;
36 | }
37 |
38 |
39 | /* ---------------------------------动画-------------------------------------------*/
40 | @keyframes click-effect-show {
41 | from, to {
42 | opacity: 0;
43 | }
44 |
45 | 50%{
46 | opacity: 0.5;
47 | }
48 | }
--------------------------------------------------------------------------------
/components/colorBox/index.wxss:
--------------------------------------------------------------------------------
1 | /* components/colorBox/index.wxss */
2 |
3 | /* 显示颜色的盒子 */
4 |
5 | .color-box {
6 | width: 100%;
7 | height: 60rpx;
8 | margin-top: 27rpx;
9 | }
10 |
11 | .color-box-selected {
12 | width: 100%;
13 | height: 60rpx;
14 | margin-top: 27rpx;
15 | animation: color-box-up 0.1s linear 1;
16 | animation-fill-mode: forwards;
17 | }
18 |
19 | .bottom-line {
20 | width: 0rpx;
21 | height: 5rpx;
22 | z-index: 3;
23 | position: absolute;
24 | top: 87rpx;
25 | animation-delay: 0.05s;
26 | animation: bottom-line-show 0.2s ease-in 1;
27 | animation-fill-mode: forwards;
28 | }
29 |
30 | /* ---------------------------------动画-------------------------------------------*/
31 |
32 | @keyframes color-box-up {
33 | to {
34 | transform: translateY(-10rpx);
35 | }
36 | }
37 |
38 | @keyframes bottom-line-show {
39 | from {
40 | widh: 0rpx;
41 | transform: translateX(30rpx);
42 | }
43 |
44 | to {
45 | width: 60rpx;
46 | transform: translateX(0rpx);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/utils/util.js:
--------------------------------------------------------------------------------
1 | const formatTime = date => {
2 | const year = date.getFullYear()
3 | const month = date.getMonth() + 1
4 | const day = date.getDate()
5 | const hour = date.getHours()
6 | const minute = date.getMinutes()
7 | const second = date.getSeconds()
8 |
9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
10 | }
11 |
12 | const formatNumber = n => {
13 | n = n.toString()
14 | return n[1] ? n : '0' + n
15 | }
16 |
17 | // 只响应最后一次promise
18 | class promiseContainer {
19 | constructor() {
20 | this.promise = null;
21 | this.then = null;
22 | }
23 | addPromise (promise, then) {
24 | let that = this;
25 | that.promise = promise;
26 | that.then = then;
27 | promise.then((data) => {
28 | if (promise === that.promise) {
29 | that.then(data);
30 | }
31 | else {
32 | console.log("Cancel current promise.");
33 | }
34 | });
35 | }
36 | }
37 |
38 | module.exports = {
39 | formatTime: formatTime,
40 | promiseContainer: promiseContainer,
41 | }
42 |
--------------------------------------------------------------------------------
/components/helpers/classNames.js:
--------------------------------------------------------------------------------
1 | /*!
2 | Copyright (c) 2018 Jed Watson.
3 | Licensed under the MIT License (MIT), see
4 | http://jedwatson.github.io/classnames
5 | */
6 | /* global define */
7 | 'use strict';
8 |
9 | var hasOwn = {}.hasOwnProperty;
10 |
11 | function classNames() {
12 | var classes = [];
13 |
14 | for (var i = 0; i < arguments.length; i++) {
15 | var arg = arguments[i];
16 | if (!arg) continue;
17 |
18 | var argType = typeof arg;
19 |
20 | if (argType === 'string' || argType === 'number') {
21 | classes.push(arg);
22 | } else if (Array.isArray(arg) && arg.length) {
23 | var inner = classNames.apply(null, arg);
24 | if (inner) {
25 | classes.push(inner);
26 | }
27 | } else if (argType === 'object') {
28 | for (var key in arg) {
29 | if (hasOwn.call(arg, key) && arg[key]) {
30 | classes.push(key);
31 | }
32 | }
33 | }
34 | }
35 |
36 | return classes.join(' ');
37 | }
38 |
39 | export default classNames
--------------------------------------------------------------------------------
/components/customButton/index.js:
--------------------------------------------------------------------------------
1 | // components/brushPoint/index.js
2 | Component({
3 | options: {
4 | multipleSlots: true // 在组件定义时的选项中启用多slot支持
5 | },
6 | /**
7 | * 组件的属性列表
8 | * 用于组件自定义设置
9 | */
10 | properties: {
11 | imgUrl: {
12 | type: String,
13 | value: ""
14 | },
15 |
16 | width: {
17 | type: String,
18 | value: "10rpx"
19 | },
20 |
21 | text: {
22 | type: String,
23 | value: ""
24 | },
25 |
26 | selected: {
27 | type: Boolean,
28 | value: false
29 | },
30 | },
31 |
32 | /**
33 | * 私有数据,组件的初始数据
34 | * 可用于模版渲染
35 | */
36 | data: {
37 | clicked: false
38 | },
39 |
40 |
41 | /**
42 | * 组件的方法列表
43 | * 更新属性和数据的方法与更新页面数据的方法类似
44 | */
45 | methods: {
46 |
47 | /*
48 | * 内部私有方法建议以下划线开头
49 | * triggerEvent 用于触发事件
50 | */
51 |
52 | _click() {
53 | this.setData({
54 | clicked: true
55 | });
56 |
57 | setTimeout(()=>{
58 | this.setData({
59 | clicked: false
60 | });
61 | }, 300);
62 |
63 | this.triggerEvent("clickEvent");
64 | }
65 | }
66 | })
--------------------------------------------------------------------------------
/components/divider/index.js:
--------------------------------------------------------------------------------
1 | import baseComponent from '../helpers/baseComponent'
2 | import classNames from '../helpers/classNames'
3 |
4 | baseComponent({
5 | properties: {
6 | prefixCls: {
7 | type: String,
8 | value: 'wux-divider',
9 | },
10 | position: {
11 | type: String,
12 | value: 'center',
13 | },
14 | dashed: {
15 | type: Boolean,
16 | value: false,
17 | },
18 | text: {
19 | type: String,
20 | value: '',
21 | },
22 | showText: {
23 | type: Boolean,
24 | value: true,
25 | },
26 | },
27 | computed: {
28 | classes: ['prefixCls, dashed, showText, position', function(prefixCls, dashed, showText, position) {
29 | const wrap = classNames(prefixCls, {
30 | [`${prefixCls}--dashed`]: dashed,
31 | [`${prefixCls}--text`]: showText,
32 | [`${prefixCls}--text-${position}`]: showText && position,
33 | })
34 | const text = `${prefixCls}__text`
35 |
36 | return {
37 | wrap,
38 | text,
39 | }
40 | }],
41 | },
42 | })
43 |
--------------------------------------------------------------------------------
/components/divider/index.wxss:
--------------------------------------------------------------------------------
1 | .wux-divider {
2 | display: block;
3 | height: 2rpx;
4 | width: 100%;
5 | margin: 30rpx 0;
6 | clear: both;
7 | border-top: 2rpx solid #e8e8e8
8 | }
9 | .wux-divider--text {
10 | display: table;
11 | white-space: nowrap;
12 | text-align: center;
13 | background: 0 0;
14 | font-weight: 500;
15 | color: rgba(0,0,0,.85);
16 | font-size: 32rpx;
17 | border-top: none!important
18 | }
19 | .wux-divider--text:after,
20 | .wux-divider--text:before {
21 | content: '';
22 | display: table-cell;
23 | position: relative;
24 | top: 50%;
25 | width: 50%;
26 | border-top-width: 2rpx;
27 | border-top-style: solid;
28 | border-top-color: #e8e8e8;
29 | transform: translateY(50%)
30 | }
31 | .wux-divider--dashed {
32 | border-top: 2rpx dashed #e8e8e8
33 | }
34 | .wux-divider--dashed.wux-divider--text:after,
35 | .wux-divider--dashed.wux-divider--text:before {
36 | border-top-style: dashed
37 | }
38 | .wux-divider--text-left:before {
39 | width: 5%
40 | }
41 | .wux-divider--text-left:after {
42 | width: 95%
43 | }
44 | .wux-divider--text-right:before {
45 | width: 95%
46 | }
47 | .wux-divider--text-right:after {
48 | width: 5%
49 | }
50 | .wux-divider__text {
51 | display: inline-block;
52 | padding: 0 30rpx
53 | }
--------------------------------------------------------------------------------
/components/helpers/safeAreaBehavior.js:
--------------------------------------------------------------------------------
1 | import { getSystemInfo, checkIPhoneX } from './checkIPhoneX'
2 |
3 | const defaultSafeArea = {
4 | top: false,
5 | bottom: false,
6 | }
7 |
8 | const setSafeArea = (params) => {
9 | if (typeof params === 'boolean') {
10 | return Object.assign({}, defaultSafeArea, {
11 | top: params,
12 | bottom: params,
13 | })
14 | } else if (params !== null && typeof params === 'object') {
15 | return Object.assign({}, defaultSafeArea)
16 | } else if (typeof params === 'string') {
17 | return Object.assign({}, defaultSafeArea, {
18 | [params]: true,
19 | })
20 | }
21 | return defaultSafeArea
22 | }
23 |
24 | export default Behavior({
25 | properties: {
26 | safeArea: {
27 | type: [Boolean, String, Object],
28 | value: false,
29 | },
30 | },
31 | observers: {
32 | safeArea(newVal) {
33 | this.setData({ safeAreaConfig: setSafeArea(newVal) })
34 | },
35 | },
36 | definitionFilter(defFields) {
37 | const { statusBarHeight } = getSystemInfo() || {}
38 | const isIPhoneX = checkIPhoneX()
39 |
40 | Object.assign(defFields.data = (defFields.data || {}), {
41 | safeAreaConfig: defaultSafeArea,
42 | statusBarHeight,
43 | isIPhoneX,
44 | })
45 | },
46 | })
47 |
--------------------------------------------------------------------------------
/pages/ai-classifier/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{status}}
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 | 笔触大小
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {{classesName[index]}}
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/components/helpers/debounce.js:
--------------------------------------------------------------------------------
1 | export default function debounce(func, wait, immediate) {
2 | let timeout,
3 | args,
4 | context,
5 | timestamp,
6 | result
7 |
8 | function later() {
9 | const last = +(new Date()) - timestamp
10 | if (last < wait && last >= 0) {
11 | timeout = setTimeout(later, wait - last)
12 | } else {
13 | timeout = undefined
14 | if (!immediate) {
15 | result = func.apply(context, args)
16 | if (!timeout) {
17 | context = undefined
18 | args = undefined
19 | }
20 | }
21 | }
22 | }
23 |
24 | function debounced() {
25 | context = this
26 | args = arguments
27 | timestamp = +(new Date())
28 |
29 | const callNow = immediate && !timeout
30 | if (!timeout) {
31 | timeout = setTimeout(later, wait)
32 | }
33 |
34 | if (callNow) {
35 | result = func.apply(context, args)
36 | context = undefined
37 | args = undefined
38 | }
39 |
40 | return result
41 | }
42 |
43 | function cancel() {
44 | if (timeout !== undefined) {
45 | clearTimeout(timeout)
46 | timeout = undefined
47 | }
48 |
49 | context = undefined
50 | args = undefined
51 | }
52 |
53 | debounced.cancel = cancel
54 |
55 | return debounced
56 | }
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件。",
3 | "packOptions": {
4 | "ignore": [
5 | {
6 | "value": "images/gif/",
7 | "type": "folder"
8 | },
9 | {
10 | "value": "images/mini-code.jpg",
11 | "type": "file"
12 | }
13 | ],
14 | "include": []
15 | },
16 | "setting": {
17 | "urlCheck": false,
18 | "es6": true,
19 | "enhance": true,
20 | "postcss": true,
21 | "preloadBackgroundData": false,
22 | "minified": true,
23 | "newFeature": true,
24 | "coverView": true,
25 | "nodeModules": true,
26 | "autoAudits": false,
27 | "showShadowRootInWxmlPanel": true,
28 | "scopeDataCheck": false,
29 | "uglifyFileName": false,
30 | "checkInvalidKey": true,
31 | "checkSiteMap": true,
32 | "uploadWithSourceMap": true,
33 | "compileHotReLoad": false,
34 | "babelSetting": {
35 | "ignore": [],
36 | "disablePlugins": [],
37 | "outputPath": ""
38 | },
39 | "useIsolateContext": true,
40 | "useCompilerModule": true,
41 | "userConfirmedUseCompilerModuleSwitch": false,
42 | "condition": false
43 | },
44 | "compileType": "miniprogram",
45 | "libVersion": "2.11.2",
46 | "appid": "wxce0a464ad38acd34",
47 | "projectname": "ai-painter",
48 | "simulatorType": "wechat",
49 | "simulatorPluginLibVersion": {},
50 | "cloudfunctionTemplateRoot": "cloudfunctionTemplate/",
51 | "condition": {},
52 | "editorSetting": {
53 | "tabIndent": "insertSpaces",
54 | "tabSize": 2
55 | }
56 | }
--------------------------------------------------------------------------------
/components/helpers/gestures.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取触摸点位置信息
3 | */
4 | export const getTouchPoints = (nativeEvent, index = 0) => {
5 | const touches = nativeEvent.touches
6 | const changedTouches = nativeEvent.changedTouches
7 | const hasTouches = touches && touches.length > 0
8 | const hasChangedTouches = changedTouches && changedTouches.length > 0
9 | const points = !hasTouches && hasChangedTouches ? changedTouches[index] : hasTouches ? touches[index] : nativeEvent
10 |
11 | return {
12 | x: points.pageX,
13 | y: points.pageY,
14 | }
15 | }
16 |
17 | /**
18 | * 获取触摸点个数
19 | */
20 | export const getPointsNumber = (e) => e.touches && e.touches.length || e.changedTouches && e.changedTouches.length
21 |
22 | /**
23 | * 判断是否为同一点
24 | */
25 | export const isEqualPoints = (p1, p2) => p1.x === p2.x && p1.y === p2.y
26 |
27 | /**
28 | * 判断是否为相近的两点
29 | */
30 | export const isNearbyPoints = (p1, p2, DOUBLE_TAP_RADIUS = 25) => {
31 | const xMove = Math.abs(p1.x - p2.x)
32 | const yMove = Math.abs(p1.y - p2.y)
33 | return xMove < DOUBLE_TAP_RADIUS & yMove < DOUBLE_TAP_RADIUS
34 | }
35 |
36 | /**
37 | * 获取两点之间的距离
38 | */
39 | export const getPointsDistance = (p1, p2) => {
40 | const xMove = Math.abs(p1.x - p2.x)
41 | const yMove = Math.abs(p1.y - p2.y)
42 | return Math.sqrt(xMove * xMove + yMove * yMove)
43 | }
44 |
45 | /**
46 | * 获取触摸移动方向
47 | */
48 | export const getSwipeDirection = (x1, x2, y1, y2) => {
49 | return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
50 | }
51 |
--------------------------------------------------------------------------------
/components/brushPoint/index.js:
--------------------------------------------------------------------------------
1 | // components/brushPoint/index.js
2 | Component({
3 | options: {
4 | multipleSlots: true // 在组件定义时的选项中启用多slot支持
5 | },
6 | /**
7 | * 组件的属性列表
8 | * 用于组件自定义设置
9 | */
10 | properties: {
11 | color: {
12 | type: String,
13 | value: "red"
14 | // observer: function (newVal, oldVal) { }
15 | },
16 |
17 | width: {
18 | type: String,
19 | value: "60rpx"
20 | },
21 |
22 | height: {
23 | type: String,
24 | value: "60rpx"
25 | },
26 |
27 | bgColor: {
28 | type: String,
29 | value: "white"
30 | },
31 |
32 | radius: {
33 | type: Number,
34 | value: 20
35 | },
36 |
37 | selected: {
38 | type: Boolean,
39 | value: false
40 | },
41 | },
42 |
43 | /**
44 | * 私有数据,组件的初始数据
45 | * 可用于模版渲染
46 | */
47 | data: {
48 | hideEffect: true
49 | },
50 |
51 | attached: function () {
52 | this.setData({
53 | innerRadius: this.properties.radius * 1.7 + 10,
54 | outterRadius: this.properties.radius * 1.7 + 20,
55 | });
56 | },
57 |
58 | /**
59 | * 组件的方法列表
60 | * 更新属性和数据的方法与更新页面数据的方法类似
61 | */
62 | methods: {
63 |
64 | /*
65 | * 内部私有方法建议以下划线开头
66 | * triggerEvent 用于触发事件
67 | */
68 |
69 | // 选中笔刷
70 | _select(e) {
71 | this.setData({
72 | hideEffect: false
73 | });
74 |
75 | setTimeout(() => {
76 | this.setData({
77 | hideEffect: true
78 | });
79 | }, 500);
80 | this.triggerEvent("select");
81 | }
82 | }
83 | })
--------------------------------------------------------------------------------
/pages/share/share.js:
--------------------------------------------------------------------------------
1 | //index.js
2 |
3 | Page({
4 | data: {
5 | painting: {},
6 | shareImage: ''
7 | },
8 | onLoad: function (options) {
9 | wx.showLoading({
10 | title: '绘制中...',
11 | mask: true
12 | });
13 | this.eventDraw(options.imgUrl)
14 | },
15 | eventDraw: function (imgUrl) {
16 | this.setData({
17 | painting: {
18 | width: 375,
19 | height: 555,
20 | clear: true,
21 | views: [
22 | {
23 | type: 'image',
24 | url: imgUrl,
25 | top: 136,
26 | left: 42.5,
27 | width: 290,
28 | height: 240
29 | },
30 | ]
31 | }
32 | })
33 | },
34 | eventSave: function () {
35 | wx.saveImageToPhotosAlbum({
36 | filePath: this.data.shareImage,
37 | success (res) {
38 | wx.showToast({
39 | title: '保存图片成功',
40 | icon: 'success',
41 | duration: 2000
42 | })
43 | }
44 | })
45 | },
46 | eventGetImage: function (event) {
47 | wx.hideLoading()
48 | const { tempFilePath } = event.detail
49 | this.setData({
50 | shareImage: tempFilePath
51 | })
52 | },
53 | /**
54 | * 用户点击右上角分享
55 | */
56 | onShareAppMessage: function () {
57 | return {
58 | title: '我创作了一副名画,你也来试一下吧!',
59 | path: `/pages/ai-painter/index`,
60 | imageUrl: this.data.imgUrl,
61 | success: function (res) {
62 | // 转发成功
63 | },
64 | fail: function (res) {
65 | // 转发失败
66 | }
67 | }
68 | },
69 | })
70 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 |
3 | App({
4 | onLaunch: function () {
5 | // 展示本地存储能力
6 | var logs = wx.getStorageSync('logs') || []
7 | logs.unshift(Date.now())
8 | wx.setStorageSync('logs', logs)
9 |
10 | // 登录
11 | wx.login({
12 | success: res => {
13 | // 发送 res.code 到后台换取 openId, sessionKey, unionId
14 | }
15 | })
16 | // 获取用户信息
17 | wx.getSetting({
18 | success: res => {
19 | if (res.authSetting['scope.userInfo']) {
20 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
21 | wx.getUserInfo({
22 | success: res => {
23 | // 可以将 res 发送给后台解码出 unionId
24 | this.globalData.userInfo = res.userInfo
25 |
26 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
27 | // 所以此处加入 callback 以防止这种情况
28 | if (this.userInfoReadyCallback) {
29 | this.userInfoReadyCallback(res)
30 | }
31 | }
32 | })
33 | }
34 | }
35 | })
36 | },
37 |
38 | /** 获取用户信息 */
39 | getUserInfo: function (cb) {
40 | var that = this
41 | if (this.globalData.userInfo) {
42 | typeof cb == "function" && cb(this.globalData.userInfo)
43 | } else {
44 | //调用登陆接口
45 | wx.login({
46 | success: function () {
47 | wx.getUserInfo({
48 | success: function (res) {
49 | that.globalData.userInfo = res.userInfo
50 | typeof cb == "function" && cb(that.globalData.userInfo)
51 | }
52 | })
53 | }
54 | })
55 | }
56 | },
57 |
58 | globalData: {
59 | userInfo: null
60 | }
61 | })
--------------------------------------------------------------------------------
/components/helpers/safeSetDataBehavior.js:
--------------------------------------------------------------------------------
1 | export default Behavior({
2 | lifetimes: {
3 | created() {
4 | this.nextCallback = null
5 | },
6 | detached() {
7 | this.cancelNextCallback()
8 | },
9 | },
10 | methods: {
11 | /**
12 | * safeSetData
13 | * @param {Object} nextData 数据对象
14 | * @param {Function} callback 回调函数
15 | */
16 | safeSetData(nextData, callback) {
17 | this.pendingData = Object.assign({}, this.data, nextData)
18 | callback = this.setNextCallback(callback)
19 |
20 | this.setData(nextData, () => {
21 | this.pendingData = null
22 | callback()
23 | })
24 | },
25 | /**
26 | * 设置下一回调函数
27 | * @param {Function} callback 回调函数
28 | */
29 | setNextCallback(callback) {
30 | let active = true
31 |
32 | this.nextCallback = (event) => {
33 | if (active) {
34 | active = false
35 | this.nextCallback = null
36 |
37 | callback.call(this, event)
38 | }
39 | }
40 |
41 | this.nextCallback.cancel = () => {
42 | active = false
43 | }
44 |
45 | return this.nextCallback
46 | },
47 | /**
48 | * 取消下一回调函数
49 | */
50 | cancelNextCallback() {
51 | if (this.nextCallback !== null) {
52 | this.nextCallback.cancel()
53 | this.nextCallback = null
54 | }
55 | },
56 | },
57 | })
--------------------------------------------------------------------------------
/components/grid/index.wxss:
--------------------------------------------------------------------------------
1 | .wux-grid {
2 | position: relative;
3 | float: left;
4 | box-sizing: border-box
5 | }
6 | .wux-grid--bordered:before {
7 | content: " ";
8 | position: absolute;
9 | right: 0;
10 | top: 0;
11 | width: 1PX;
12 | bottom: 0;
13 | border-right: 1PX solid #d9d9d9;
14 | color: #d9d9d9;
15 | transform-origin: 100% 0;
16 | transform: scaleX(.5)
17 | }
18 | .wux-grid--bordered:after {
19 | content: " ";
20 | position: absolute;
21 | left: 0;
22 | bottom: 0;
23 | right: 0;
24 | height: 1PX;
25 | border-bottom: 1PX solid #d9d9d9;
26 | color: #d9d9d9;
27 | transform-origin: 0 100%;
28 | transform: scaleY(.5)
29 | }
30 | .wux-grid--hover {
31 | background-color: #ececec
32 | }
33 | .wux-grid__thumb {
34 | width: 56rpx;
35 | height: 56rpx;
36 | display: block;
37 | margin: 0 auto
38 | }
39 | .wux-grid__label {
40 | text-align: center;
41 | color: #000;
42 | font-size: 28rpx;
43 | margin-top: 10rpx;
44 | width: auto;
45 | overflow: hidden;
46 | text-overflow: ellipsis;
47 | white-space: nowrap;
48 | word-wrap: normal
49 | }
50 | .wux-grid__inner {
51 | height: 100%;
52 | width: 100%;
53 | text-align: center;
54 | padding: 40rpx 20rpx;
55 | box-sizing: border-box;
56 | display: -ms-flexbox;
57 | display: flex;
58 | -ms-flex-direction: column;
59 | flex-direction: column;
60 | -ms-flex-pack: center;
61 | justify-content: center;
62 | -ms-flex-align: center;
63 | align-items: center
64 | }
65 | .wux-grid--square .wux-grid__content {
66 | position: relative;
67 | display: block;
68 | content: ' ';
69 | padding-bottom: 100%
70 | }
71 | .wux-grid--square .wux-grid__inner {
72 | position: absolute;
73 | top: 50%;
74 | transform: translate3d(0,-50%,0)
75 | }
--------------------------------------------------------------------------------
/components/grids/index.js:
--------------------------------------------------------------------------------
1 | import baseComponent from '../helpers/baseComponent'
2 | import classNames from '../helpers/classNames'
3 |
4 | baseComponent({
5 | relations: {
6 | '../grid/index': {
7 | type: 'child',
8 | observer() {
9 | this.debounce(this.changeCurrent)
10 | },
11 | },
12 | },
13 | properties: {
14 | prefixCls: {
15 | type: String,
16 | value: 'wux-grids',
17 | },
18 | col: {
19 | type: Number,
20 | value: 3,
21 | observer: 'changeCurrent',
22 | },
23 | bordered: {
24 | type: Boolean,
25 | value: true,
26 | observer: 'changeCurrent',
27 | },
28 | square: {
29 | type: Boolean,
30 | value: false,
31 | observer: 'changeCurrent',
32 | },
33 | },
34 | computed: {
35 | classes: ['prefixCls, bordered', function(prefixCls, bordered) {
36 | const wrap = classNames(prefixCls, {
37 | [`${prefixCls}--bordered`]: bordered,
38 | })
39 |
40 | return {
41 | wrap,
42 | }
43 | }],
44 | },
45 | methods: {
46 | changeCurrent() {
47 | const elements = this.getRelationNodes('../grid/index')
48 | const { col, bordered, square } = this.data
49 | const colNum = parseInt(col) > 0 ? parseInt(col) : 1
50 | const width = `${100 / colNum}%`
51 |
52 | if (elements.length > 0) {
53 | elements.forEach((element, index) => {
54 | element.changeCurrent(width, bordered, square, index)
55 | })
56 | }
57 | },
58 | },
59 | })
60 |
--------------------------------------------------------------------------------
/components/helpers/eventsMixin.js:
--------------------------------------------------------------------------------
1 | const defaultEvents = {
2 | onChange() {},
3 | }
4 |
5 | export default function eventsMixin(params = { defaultEvents }) {
6 | return Behavior({
7 | lifetimes: {
8 | created () {
9 | this._oriTriggerEvent = this.triggerEvent
10 | this.triggerEvent = this._triggerEvent
11 | },
12 | },
13 | properties: {
14 | events: {
15 | type: Object,
16 | value: defaultEvents,
17 | },
18 | },
19 | data: {
20 | inputEvents: defaultEvents,
21 | },
22 | definitionFilter(defFields) {
23 | // set default data
24 | Object.assign(defFields.data = (defFields.data || {}), {
25 | inputEvents: Object.assign({}, defaultEvents, defFields.inputEvents),
26 | })
27 |
28 | // set default methods
29 | Object.assign(defFields.methods = (defFields.methods || {}), {
30 | _triggerEvent(name, params, runCallbacks = true, option) {
31 | const { inputEvents } = this.data
32 | const method = `on${name[0].toUpperCase()}${name.slice(1)}`
33 | const func = inputEvents[method]
34 |
35 | if (runCallbacks && typeof func === 'function') {
36 | func.call(this, params)
37 | }
38 |
39 | this._oriTriggerEvent(name, params, option)
40 | },
41 | })
42 |
43 | // set default observers
44 | Object.assign(defFields.observers = (defFields.observers || {}), {
45 | events(newVal) {
46 | this.setData({
47 | inputEvents: Object.assign({}, defaultEvents, this.data.inputEvents, newVal),
48 | })
49 | },
50 | })
51 | },
52 | })
53 | }
54 |
--------------------------------------------------------------------------------
/components/helpers/shallowEqual.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-present, Facebook, Inc.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | *
7 | * @typechecks
8 | *
9 | */
10 |
11 | /*eslint-disable no-self-compare */
12 |
13 | 'use strict';
14 |
15 | var hasOwnProperty = Object.prototype.hasOwnProperty;
16 |
17 | /**
18 | * inlined Object.is polyfill to avoid requiring consumers ship their own
19 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
20 | */
21 | function is(x, y) {
22 | // SameValue algorithm
23 | if (x === y) {
24 | // Steps 1-5, 7-10
25 | // Steps 6.b-6.e: +0 != -0
26 | // Added the nonzero y check to make Flow happy, but it is redundant
27 | return x !== 0 || y !== 0 || 1 / x === 1 / y;
28 | } else {
29 | // Step 6.a: NaN == NaN
30 | return x !== x && y !== y;
31 | }
32 | }
33 |
34 | /**
35 | * Performs equality by iterating through keys on an object and returning false
36 | * when any key has values which are not strictly equal between the arguments.
37 | * Returns true when the values of all keys are strictly equal.
38 | */
39 | function shallowEqual(objA, objB) {
40 | if (is(objA, objB)) {
41 | return true;
42 | }
43 |
44 | if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
45 | return false;
46 | }
47 |
48 | var keysA = Object.keys(objA);
49 | var keysB = Object.keys(objB);
50 |
51 | if (keysA.length !== keysB.length) {
52 | return false;
53 | }
54 |
55 | // Test for A's keys different from B.
56 | for (var i = 0; i < keysA.length; i++) {
57 | if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
58 | return false;
59 | }
60 | }
61 |
62 | return true;
63 | }
64 |
65 | export default shallowEqual
--------------------------------------------------------------------------------
/components/helpers/computedBehavior.js:
--------------------------------------------------------------------------------
1 | import isEmpty from './isEmpty'
2 | import shallowEqual from './shallowEqual'
3 |
4 | const ALL_DATA_KEY = '**'
5 |
6 | const trim = (str = '') => str.replace(/\s/g, '')
7 |
8 | export default Behavior({
9 | lifetimes: {
10 | attached() {
11 | this.initComputed()
12 | },
13 | },
14 | definitionFilter(defFields) {
15 | const { computed = {} } = defFields
16 | const observers = Object.keys(computed).reduce((acc, name) => {
17 | const [field, getter] = Array.isArray(computed[name]) ? computed[name] : [ALL_DATA_KEY, computed[name]]
18 | return {
19 | ...acc,
20 | [field]: function(...args) {
21 | if (typeof getter === 'function') {
22 | const newValue = getter.apply(this, args)
23 | const oldValue = this.data[name]
24 | if (!isEmpty(newValue) && !shallowEqual(newValue, oldValue)) {
25 | this.setData({ [name]: newValue })
26 | }
27 | }
28 | },
29 | }
30 | }, {})
31 |
32 | Object.assign(defFields.observers = (defFields.observers || {}), observers)
33 | Object.assign(defFields.methods = (defFields.methods || {}), {
34 | initComputed: function(data = {}, isForce = false) {
35 | if (!this.runInitComputed || isForce) {
36 | this.runInitComputed = false
37 | const context = this
38 | const result = { ...this.data, ...data }
39 | Object.keys(observers).forEach((key) => {
40 | const values = trim(key).split(',').reduce((acc, name) => ([...acc, result[name]]), [])
41 | observers[key].apply(context, values)
42 | })
43 | this.runInitComputed = true
44 | }
45 | },
46 | })
47 | },
48 | })
49 |
--------------------------------------------------------------------------------
/components/helpers/createFieldsStore.js:
--------------------------------------------------------------------------------
1 | class FieldsStore {
2 | constructor(fields = {}) {
3 | this.fields = fields
4 | }
5 |
6 | setFields(fields) {
7 | Object.assign(this.fields, fields)
8 | }
9 |
10 | updateFields(fields) {
11 | this.fields = fields
12 | }
13 |
14 | clearField(name) {
15 | delete this.fields[name]
16 | }
17 |
18 | getValueFromFields(name, fields) {
19 | const field = fields[name]
20 | if (field && 'value' in field) {
21 | return field.value
22 | }
23 | return field.initialValue
24 | }
25 |
26 | getAllFieldsName() {
27 | const { fields } = this
28 | return fields ? Object.keys(fields) : []
29 | }
30 |
31 | getField(name) {
32 | return {
33 | ...this.fields[name],
34 | name,
35 | }
36 | }
37 |
38 | getFieldValuePropValue(fieldOption) {
39 | const { name, valuePropName } = fieldOption
40 | const field = this.getField(name)
41 | const fieldValue = 'value' in field ? field.value : field.initialValue
42 |
43 | return {
44 | [valuePropName]: fieldValue,
45 | }
46 | }
47 |
48 | getFieldValue(name) {
49 | return this.getValueFromFields(name, this.fields)
50 | }
51 |
52 | getFieldsValue(names) {
53 | const fields = names || this.getAllFieldsName()
54 | return fields.reduce((acc, name) => {
55 | acc[name] = this.getFieldValue(name)
56 | return acc
57 | }, {})
58 | }
59 |
60 | resetFields(ns) {
61 | const { fields } = this
62 | const names = ns || this.getAllFieldsName()
63 | return names.reduce((acc, name) => {
64 | const field = fields[name]
65 | if (field) {
66 | acc[name] = field.initialValue
67 | }
68 | return acc
69 | }, {})
70 | }
71 | }
72 |
73 | export default function createFieldsStore(fields) {
74 | return new FieldsStore(fields)
75 | }
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ai-painter-wx-miniprogram
2 | tensorflow跑在微信小程序上,实现AI识别简笔画,AI自动画画等。
3 |
4 |
5 |
6 | ## quick_draw_classifier模型
7 | #### 说明
8 | 对用户画的简笔画进行分类。
9 | #### 数据集
10 | 使用[quick_draw dataset](https://github.com/googlecreativelab/quickdraw-dataset).
11 | #### 模型训练
12 | https://github.com/zhaocc1106/machine_learn/tree/master/NeuralNetworks-tensorflow/RNN/quick_draw
13 | https://github.com/zhaocc1106/machine_learn/tree/master/NeuralNetworks-tensorflow/RNN/quick_draw/quick_draw_classify
14 | 训练完之后通过如下命令将模型转换为tensorflow.js模型。
15 | ```
16 | tensorflowjs_converter --input_format keras model.h5 ./
17 | ```
18 | 转换后会生成model.json文件以及group bin文件,分别记录网络结构以及参数值。
19 | 实际应用模型时可能需要修改一些地方,比如修改model.json的input layer的batch size为1;修改dropout training参数为false。当然也可以在保存HDF5模型文件前做修改。
20 | #### 演示
21 |
22 |
23 |
24 | ## quick_draw_autopainter模型
25 | #### 说明
26 | 根据用户的起始笔画,来完成后续的简笔画.
27 | #### 数据集
28 | 使用[quick_draw dataset](https://github.com/googlecreativelab/quickdraw-dataset).
29 | #### 模型训练
30 | https://github.com/zhaocc1106/machine_learn/tree/master/NeuralNetworks-tensorflow/RNN/quick_draw
31 | https://github.com/zhaocc1106/machine_learn/tree/master/NeuralNetworks-tensorflow/RNN/quick_draw/auto_draw
32 | 训练完之后通过如下命令将模型转换为tensorflow.js模型。
33 | ```
34 | tensorflowjs_converter --input_format keras model.h5 ./
35 | ```
36 | 转换后会生成model.json文件以及group bin文件,分别记录网络结构以及参数值。
37 | 实际应用模型时可能需要修改一些地方,比如修改model.json的input layer的batch size为1;修改dropout training参数为false。当然也可以在保存HDF5模型文件前做修改。
38 | #### 遗留问题
39 | 对起始笔画要求比较高,可能是如下原因:
40 | 1. 笔画采集和预处理不太满足模型输入
41 | 2. 模型本身问题
42 | 3. 训练数据不够随机性
43 |
44 | 后续再继续优化
45 | #### 演示
46 |
47 |
48 |
49 | ## 小程序中部署tensorflow模型
50 | 参考官方说明https://github.com/tensorflow/tfjs-wechat
51 |
--------------------------------------------------------------------------------
/components/grid/index.js:
--------------------------------------------------------------------------------
1 | import baseComponent from '../helpers/baseComponent'
2 | import classNames from '../helpers/classNames'
3 |
4 | baseComponent({
5 | relations: {
6 | '../grids/index': {
7 | type: 'parent',
8 | },
9 | },
10 | properties: {
11 | prefixCls: {
12 | type: String,
13 | value: 'wux-grid',
14 | },
15 | hoverClass: {
16 | type: String,
17 | value: 'default',
18 | },
19 | thumb: {
20 | type: String,
21 | value: '',
22 | },
23 | label: {
24 | type: String,
25 | value: '',
26 | },
27 | },
28 | data: {
29 | width: '100%',
30 | bordered: true,
31 | square: true,
32 | index: 0,
33 | },
34 | computed: {
35 | classes: ['prefixCls, hoverClass, bordered, square', function(prefixCls, hoverClass, bordered, square) {
36 | const wrap = classNames(prefixCls, {
37 | [`${prefixCls}--bordered`]: bordered,
38 | [`${prefixCls}--square`]: square,
39 | })
40 | const content = `${prefixCls}__content`
41 | const inner = `${prefixCls}__inner`
42 | const hd = `${prefixCls}__hd`
43 | const thumb = `${prefixCls}__thumb`
44 | const bd = `${prefixCls}__bd`
45 | const label = `${prefixCls}__label`
46 | const hover = hoverClass && hoverClass !== 'default' ? hoverClass : `${prefixCls}--hover`
47 |
48 | return {
49 | wrap,
50 | content,
51 | inner,
52 | hd,
53 | thumb,
54 | bd,
55 | label,
56 | hover,
57 | }
58 | }],
59 | },
60 | methods: {
61 | changeCurrent(width, bordered, square, index) {
62 | this.setData({
63 | width,
64 | bordered,
65 | square,
66 | index,
67 | })
68 | },
69 | onTap() {
70 | this.triggerEvent('click', this.data)
71 | },
72 | },
73 | })
74 |
--------------------------------------------------------------------------------
/components/helpers/relationsBehavior.js:
--------------------------------------------------------------------------------
1 | import isEmpty from './isEmpty'
2 | import debounce from './debounce'
3 |
4 | /**
5 | * bind func to obj
6 | */
7 | function bindFunc(obj, method, observer) {
8 | const oldFn = obj[method]
9 | obj[method] = function(target) {
10 | if (observer) {
11 | observer.call(this, target, {
12 | [method]: true,
13 | })
14 | }
15 | if (oldFn) {
16 | oldFn.call(this, target)
17 | }
18 | }
19 | }
20 |
21 | // default methods
22 | const methods = ['linked', 'linkChanged', 'unlinked']
23 |
24 | // extra props
25 | const extProps = ['observer']
26 |
27 | export default Behavior({
28 | lifetimes: {
29 | created() {
30 | this._debounce = null
31 | },
32 | detached() {
33 | if (this._debounce && this._debounce.cancel) {
34 | this._debounce.cancel()
35 | }
36 | },
37 | },
38 | definitionFilter(defFields) {
39 | const { relations } = defFields
40 |
41 | if (!isEmpty(relations)) {
42 | for (const key in relations) {
43 | const relation = relations[key]
44 |
45 | // bind func
46 | methods.forEach((method) => bindFunc(relation, method, relation.observer))
47 |
48 | // delete extProps
49 | extProps.forEach((prop) => delete relation[prop])
50 | }
51 | }
52 |
53 | Object.assign(defFields.methods = (defFields.methods || {}), {
54 | getRelationsName: function(types = ['parent', 'child', 'ancestor', 'descendant']) {
55 | return Object.keys(relations || {}).map((key) => {
56 | if (relations[key] && types.includes(relations[key].type)) {
57 | return key
58 | }
59 | return null
60 | }).filter((v) => !!v)
61 | },
62 | debounce: function(func, wait = 0, immediate = false) {
63 | return (this._debounce = this._debounce || debounce(func.bind(this), wait, immediate)).call(this)
64 | },
65 | })
66 | },
67 | })
68 |
--------------------------------------------------------------------------------
/pages/ai-painter/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{status}}
8 |
9 |
10 |
11 |
13 |
14 |
15 |
16 |
17 |
18 | 图像大小
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {{item.value}}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/components/helpers/baseComponent.js:
--------------------------------------------------------------------------------
1 | import computedBehavior from './computedBehavior'
2 | import relationsBehavior from './relationsBehavior'
3 | import safeAreaBehavior from './safeAreaBehavior'
4 | import safeSetDataBehavior from './safeSetDataBehavior'
5 | import funcBehavior from './funcBehavior'
6 | import compareVersion from './compareVersion'
7 |
8 | const { platform, SDKVersion } = wx.getSystemInfoSync()
9 | const libVersion = '2.6.6'
10 |
11 | // check SDKVersion
12 | if (platform === 'devtools' && compareVersion(SDKVersion, libVersion) < 0) {
13 | if (wx && wx.showModal) {
14 | wx.showModal({
15 | title: '提示',
16 | content: `当前基础库版本(${SDKVersion})过低,无法使用 Wux Weapp 组件库,请更新基础库版本 >=${libVersion} 后重试。`,
17 | })
18 | }
19 | }
20 |
21 | const baseComponent = (options = {}) => {
22 | // add default externalClasses
23 | options.externalClasses = [
24 | 'wux-class',
25 | 'wux-hover-class',
26 | ...(options.externalClasses = options.externalClasses || []),
27 | ]
28 |
29 | // add default behaviors
30 | options.behaviors = [
31 | relationsBehavior,
32 | safeSetDataBehavior,
33 | ...(options.behaviors = options.behaviors || []),
34 | computedBehavior, // make sure it's triggered
35 | ]
36 |
37 | // use safeArea
38 | if (options.useSafeArea) {
39 | options.behaviors = [...options.behaviors, safeAreaBehavior]
40 | delete options.useSafeArea
41 | }
42 |
43 | // use func
44 | if (options.useFunc) {
45 | options.behaviors = [...options.behaviors, funcBehavior]
46 | delete options.useFunc
47 | }
48 |
49 | // use field
50 | if (options.useField) {
51 | options.behaviors = [...options.behaviors, 'wx://form-field']
52 | delete options.useField
53 | }
54 |
55 | // use export
56 | if (options.useExport) {
57 | options.behaviors = [...options.behaviors, 'wx://component-export']
58 | options.methods = {
59 | export () {
60 | return this
61 | },
62 | ...options.methods,
63 | }
64 | delete options.useExport
65 | }
66 |
67 | // add default options
68 | options.options = {
69 | multipleSlots: true,
70 | addGlobalClass: true,
71 | ...options.options,
72 | }
73 |
74 | return Component(options)
75 | }
76 |
77 | export default baseComponent
78 |
--------------------------------------------------------------------------------
/components/helpers/funcBehavior.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 过滤对象的函数属性
3 | * @param {Object} opts
4 | */
5 | const mergeOptionsToData = (opts = {}) => {
6 | const options = Object.assign({}, opts)
7 |
8 | for (const key in options) {
9 | if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
10 | delete options[key]
11 | }
12 | }
13 |
14 | return options
15 | }
16 |
17 | /**
18 | * Simple bind, faster than native
19 | *
20 | * @param {Function} fn
21 | * @param {Object} ctx
22 | * @return {Function}
23 | */
24 | const bind = (fn, ctx) => {
25 | return (...args) => {
26 | return args.length ? fn.apply(ctx, args) : fn.call(ctx)
27 | }
28 | }
29 |
30 | /**
31 | * Object assign
32 | */
33 | const assign = (...args) => Object.assign({}, ...args)
34 |
35 | export default Behavior({
36 | definitionFilter(defFields) {
37 | defFields.data = mergeOptionsToData(defFields.data)
38 | defFields.data.in = false
39 | defFields.data.visible = false
40 | },
41 | methods: {
42 | /**
43 | * 过滤对象的函数属性
44 | * @param {Object} opts
45 | */
46 | $$mergeOptionsToData: mergeOptionsToData,
47 | /**
48 | * 合并参数并绑定方法
49 | *
50 | * @param {Object} opts 参数对象
51 | * @param {Object} fns 方法挂载的属性
52 | */
53 | $$mergeOptionsAndBindMethods (opts = {}, fns = this.fns) {
54 | const options = Object.assign({}, opts)
55 |
56 | for (const key in options) {
57 | if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
58 | fns[key] = bind(options[key], this)
59 | delete options[key]
60 | }
61 | }
62 |
63 | return options
64 | },
65 | /**
66 | * Promise setData
67 | * @param {Array} args 参数对象
68 | */
69 | $$setData (...args) {
70 | const params = assign({}, ...args)
71 |
72 | return new Promise((resolve) => {
73 | this.setData(params, resolve)
74 | })
75 | },
76 | /**
77 | * 延迟指定时间执行回调函数
78 | * @param {Function} callback 回调函数
79 | * @param {Number} timeout 延迟时间
80 | */
81 | $$requestAnimationFrame (callback = () => {}, timeout = 1000 / 60) {
82 | return new Promise((resolve) => setTimeout(resolve, timeout)).then(callback)
83 | },
84 | },
85 | /**
86 | * 组件生命周期函数,在组件实例进入页面节点树时执行
87 | */
88 | created () {
89 | this.fns = {}
90 | },
91 | /**
92 | * 组件生命周期函数,在组件实例被从页面节点树移除时执行
93 | */
94 | detached () {
95 | this.fns = {}
96 | },
97 | })
98 |
--------------------------------------------------------------------------------
/miniprogram_npm/fetch-wechat/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TEXT_FILE_EXTS = /\\.(txt|json|html|txt|csv)/;\nfunction parseResponse(url, res) {\n var header = res.header || {};\n header = Object.keys(header).reduce(function (map, key) {\n map[key.toLowerCase()] = header[key];\n return map;\n }, {});\n return {\n ok: ((res.statusCode / 200) | 0) === 1,\n status: res.statusCode,\n statusText: res.statusCode,\n url: url,\n clone: function () { return parseResponse(url, res); },\n text: function () {\n return Promise.resolve(typeof res.data === 'string' ? res.data : JSON.stringify(res.data));\n },\n json: function () {\n if (typeof res.data === 'object')\n return Promise.resolve(res.data);\n var json = {};\n try {\n json = JSON.parse(res.data);\n }\n catch (err) {\n console.error(err);\n }\n return Promise.resolve(json);\n },\n arrayBuffer: function () {\n return Promise.resolve(res.data);\n },\n headers: {\n keys: function () { return Object.keys(header); },\n entries: function () {\n var all = [];\n for (var key in header) {\n if (header.hasOwnProperty(key)) {\n all.push([key, header[key]]);\n }\n }\n return all;\n },\n get: function (n) { return header[n.toLowerCase()]; },\n has: function (n) { return n.toLowerCase() in header; }\n }\n };\n}\nexports.parseResponse = parseResponse;\nfunction fetchFunc() {\n // tslint:disable-next-line:no-any\n return function (url, options) {\n options = options || {};\n var dataType = url.match(exports.TEXT_FILE_EXTS) ? 'text' : 'arraybuffer';\n return new Promise(function (resolve, reject) {\n wx.request({\n url: url,\n method: options.method || 'GET',\n data: options.body,\n header: options.headers,\n dataType: dataType,\n responseType: dataType,\n success: function (resp) { return resolve(parseResponse(url, resp)); },\n fail: function (err) { return reject(err); }\n });\n });\n };\n}\nexports.fetchFunc = fetchFunc;\nfunction setWechatFetch(debug) {\n if (debug === void 0) { debug = false; }\n // tslint:disable-next-line:no-any\n var typedGlobal = global;\n if (typeof typedGlobal.fetch !== 'function') {\n if (debug) {\n console.log('setup global fetch...');\n }\n typedGlobal.fetch = fetchFunc();\n }\n}\nexports.setWechatFetch = setWechatFetch;\n//# sourceMappingURL=index.js.map"]}
--------------------------------------------------------------------------------
/pages/ai-classifier/index.wxss:
--------------------------------------------------------------------------------
1 | page{
2 | height: 100%;
3 | width:100%;
4 | }
5 |
6 | .container {
7 | height: 100%;
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | justify-content: space-between;
12 | box-sizing: border-box;
13 | }
14 |
15 | /* 显示的题目 */
16 |
17 | .container .question {
18 | width: 100%;
19 | height: 5%;
20 | background: #f0efef;
21 | display: flex;
22 | flex-direction: row;
23 | justify-content: center;
24 | align-items: center;
25 | color: #fb21a1;
26 | box-shadow: 2rpx 5rpx 2rpx silver;
27 | }
28 |
29 | /* 刷新按钮 */
30 |
31 | .container .question .userinfo-avatar {
32 | height: 50rpx;
33 | width: 50rpx;
34 | border-radius: 50%;
35 | overflow: hidden;
36 | }
37 |
38 | .container .question text {
39 | margin: auto 10rpx auto 20rpx;
40 | font-size: 0.75em;
41 | }
42 |
43 | .container .question .refresh-btn {
44 | width: 50rpx;
45 | height: 50rpx;
46 | transform: scaleX(-1);
47 | }
48 |
49 | /* 中间画板 */
50 |
51 | .container .palette {
52 | width: 100%;
53 | height: 50%;
54 | display: flex;
55 | justify-content: center;
56 | align-items: center;
57 | box-shadow: 2rpx 5rpx 2rpx silver;
58 | }
59 |
60 | /* 按钮的panel */
61 |
62 | .container .option-panel {
63 | padding-bottom: 10rpx;
64 | font-size: 1em;
65 | text-align: center;
66 | background: #f0efef;
67 | width: 100%;
68 | height: 45%;
69 | display: flex;
70 | flex-direction: column;
71 | justify-content: space-between;
72 | align-items: center;
73 | }
74 |
75 | /* 面板中的行 */
76 |
77 | .option-panel .option-row {
78 | width: 100%;
79 | display: flex;
80 | flex-direction: row;
81 | justify-content: flex-start;
82 | align-content: center;
83 | }
84 |
85 | #firstRow{
86 | height: 10%;
87 | }
88 | #secondRow{
89 | height: 90%;
90 | }
91 |
92 |
93 | /* 面板中的第一行 */
94 |
95 | .option-panel #firstRow .width-container {
96 | width: 50%;
97 | padding-top: 10rpx;
98 | display: flex;
99 | flex-direction: row;
100 | justify-content: space-around;
101 | align-items: center;
102 | }
103 |
104 | /* 笔触大小四个字 */
105 | .option-panel #firstRow .width-container text {
106 | margin-left: 25rpx;
107 | font-size: 0.75em;
108 | color: gray;
109 | }
110 |
111 | .brush-point{
112 | width: 52rpx;
113 | height: 65rpx;
114 | }
115 |
116 | .option-panel #firstRow .useless {
117 | width: 30%;
118 | }
119 |
120 | /* 清除按钮 */
121 | .option-panel #firstRow .icon-text {
122 | padding-top: 10rpx;
123 | width: 20%;
124 | display: flex;
125 | align-items: center;
126 | font-size: 0.75em;
127 | color: gray;
128 | }
129 |
130 | /* 第二行 */
131 | .option-panel #secondRow {
132 | margin-top: 20rpx;
133 | width: 100%;
134 | display: flex;
135 | flex-direction: column;
136 | }
137 |
138 | /* 分类视图,包含名称和概率 */
139 | .option-panel #secondRow .class-view {
140 | width: 100%;
141 | display: flex;
142 | flex-direction: column;
143 | }
144 |
145 | /* 分类名称 */
146 | .option-panel #secondRow .class-view .class-name {
147 | width: 100%;
148 | margin-left: 25rpx;
149 | font-size: 0.75em;
150 | color: gray;
151 | padding: 5rpx;
152 | display: flex;
153 | flex-direction: row;
154 | justify-content: flex-start;
155 | }
156 |
157 | /* 分类概率进度条 */
158 | .option-panel #secondRow .class-view .class-prog {
159 | margin-left: 25rpx;
160 | width: 95%;
161 | }
162 |
163 | /*----------------------------------------------------------------------*/
164 |
165 | canvas {
166 | background: black;
167 | width: 100%;
168 | height: 100%;
169 | border: 1rpx solid #ebebeb;
170 | border-radius: 3rpx;
171 | }
172 |
--------------------------------------------------------------------------------
/components/helpers/styleToCssString.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * CSS properties which accept numbers but are not in units of "px".
5 | */
6 | var isUnitlessNumber = {
7 | boxFlex: true,
8 | boxFlexGroup: true,
9 | columnCount: true,
10 | flex: true,
11 | flexGrow: true,
12 | flexPositive: true,
13 | flexShrink: true,
14 | flexNegative: true,
15 | fontWeight: true,
16 | lineClamp: true,
17 | lineHeight: true,
18 | opacity: true,
19 | order: true,
20 | orphans: true,
21 | widows: true,
22 | zIndex: true,
23 | zoom: true,
24 |
25 | // SVG-related properties
26 | fillOpacity: true,
27 | strokeDashoffset: true,
28 | strokeOpacity: true,
29 | strokeWidth: true
30 | };
31 |
32 | /**
33 | * @param {string} prefix vendor-specific prefix, eg: Webkit
34 | * @param {string} key style name, eg: transitionDuration
35 | * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
36 | * WebkitTransitionDuration
37 | */
38 | function prefixKey(prefix, key) {
39 | return prefix + key.charAt(0).toUpperCase() + key.substring(1);
40 | }
41 |
42 | /**
43 | * Support style names that may come passed in prefixed by adding permutations
44 | * of vendor prefixes.
45 | */
46 | var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
47 |
48 | // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
49 | // infinite loop, because it iterates over the newly added props too.
50 | Object.keys(isUnitlessNumber).forEach(function(prop) {
51 | prefixes.forEach(function(prefix) {
52 | isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
53 | });
54 | });
55 |
56 | var msPattern = /^ms-/;
57 |
58 | var _uppercasePattern = /([A-Z])/g;
59 |
60 | /**
61 | * Hyphenates a camelcased string, for example:
62 | *
63 | * > hyphenate('backgroundColor')
64 | * < "background-color"
65 | *
66 | * For CSS style names, use `hyphenateStyleName` instead which works properly
67 | * with all vendor prefixes, including `ms`.
68 | *
69 | * @param {string} string
70 | * @return {string}
71 | */
72 | function hyphenate(string) {
73 | return string.replace(_uppercasePattern, '-$1').toLowerCase();
74 | }
75 |
76 | /**
77 | * Hyphenates a camelcased CSS property name, for example:
78 | *
79 | * > hyphenateStyleName('backgroundColor')
80 | * < "background-color"
81 | * > hyphenateStyleName('MozTransition')
82 | * < "-moz-transition"
83 | * > hyphenateStyleName('msTransition')
84 | * < "-ms-transition"
85 | *
86 | * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
87 | * is converted to `-ms-`.
88 | *
89 | * @param {string} string
90 | * @return {string}
91 | */
92 | function hyphenateStyleName(string) {
93 | return hyphenate(string).replace(msPattern, '-ms-');
94 | }
95 |
96 | var isArray = Array.isArray;
97 | var keys = Object.keys;
98 |
99 | var counter = 1;
100 | // Follows syntax at https://developer.mozilla.org/en-US/docs/Web/CSS/content,
101 | // including multiple space separated values.
102 | var unquotedContentValueRegex = /^(normal|none|(\b(url\([^)]*\)|chapter_counter|attr\([^)]*\)|(no-)?(open|close)-quote|inherit)((\b\s*)|$|\s+))+)$/;
103 |
104 | function buildRule(key, value) {
105 | if (!isUnitlessNumber[key] && typeof value === 'number') {
106 | value = '' + value + 'px';
107 | } else if (key === 'content' && !unquotedContentValueRegex.test(value)) {
108 | value = "'" + value.replace(/'/g, "\\'") + "'";
109 | }
110 |
111 | return hyphenateStyleName(key) + ': ' + value + '; ';
112 | }
113 |
114 | function styleToCssString(rules) {
115 | var result = ''
116 | if (typeof rules === 'string') {
117 | return rules
118 | }
119 | if (!rules || keys(rules).length === 0) {
120 | return result;
121 | }
122 | var styleKeys = keys(rules);
123 | for (var j = 0, l = styleKeys.length; j < l; j++) {
124 | var styleKey = styleKeys[j];
125 | var value = rules[styleKey];
126 |
127 | if (isArray(value)) {
128 | for (var i = 0, len = value.length; i < len; i++) {
129 | result += buildRule(styleKey, value[i]);
130 | }
131 | } else {
132 | result += buildRule(styleKey, value);
133 | }
134 | }
135 | return result;
136 | }
137 |
138 | export default styleToCssString
--------------------------------------------------------------------------------
/miniprogram_npm/fetch-wechat/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (function() {
2 | var __MODS__ = {};
3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
7 | __DEFINE__(1593391889373, function(require, module, exports) {
8 |
9 | Object.defineProperty(exports, "__esModule", { value: true });
10 | exports.TEXT_FILE_EXTS = /\.(txt|json|html|txt|csv)/;
11 | function parseResponse(url, res) {
12 | var header = res.header || {};
13 | header = Object.keys(header).reduce(function (map, key) {
14 | map[key.toLowerCase()] = header[key];
15 | return map;
16 | }, {});
17 | return {
18 | ok: ((res.statusCode / 200) | 0) === 1,
19 | status: res.statusCode,
20 | statusText: res.statusCode,
21 | url: url,
22 | clone: function () { return parseResponse(url, res); },
23 | text: function () {
24 | return Promise.resolve(typeof res.data === 'string' ? res.data : JSON.stringify(res.data));
25 | },
26 | json: function () {
27 | if (typeof res.data === 'object')
28 | return Promise.resolve(res.data);
29 | var json = {};
30 | try {
31 | json = JSON.parse(res.data);
32 | }
33 | catch (err) {
34 | console.error(err);
35 | }
36 | return Promise.resolve(json);
37 | },
38 | arrayBuffer: function () {
39 | return Promise.resolve(res.data);
40 | },
41 | headers: {
42 | keys: function () { return Object.keys(header); },
43 | entries: function () {
44 | var all = [];
45 | for (var key in header) {
46 | if (header.hasOwnProperty(key)) {
47 | all.push([key, header[key]]);
48 | }
49 | }
50 | return all;
51 | },
52 | get: function (n) { return header[n.toLowerCase()]; },
53 | has: function (n) { return n.toLowerCase() in header; }
54 | }
55 | };
56 | }
57 | exports.parseResponse = parseResponse;
58 | function fetchFunc() {
59 | // tslint:disable-next-line:no-any
60 | return function (url, options) {
61 | options = options || {};
62 | var dataType = url.match(exports.TEXT_FILE_EXTS) ? 'text' : 'arraybuffer';
63 | return new Promise(function (resolve, reject) {
64 | wx.request({
65 | url: url,
66 | method: options.method || 'GET',
67 | data: options.body,
68 | header: options.headers,
69 | dataType: dataType,
70 | responseType: dataType,
71 | success: function (resp) { return resolve(parseResponse(url, resp)); },
72 | fail: function (err) { return reject(err); }
73 | });
74 | });
75 | };
76 | }
77 | exports.fetchFunc = fetchFunc;
78 | function setWechatFetch(debug) {
79 | if (debug === void 0) { debug = false; }
80 | // tslint:disable-next-line:no-any
81 | var typedGlobal = global;
82 | if (typeof typedGlobal.fetch !== 'function') {
83 | if (debug) {
84 | console.log('setup global fetch...');
85 | }
86 | typedGlobal.fetch = fetchFunc();
87 | }
88 | }
89 | exports.setWechatFetch = setWechatFetch;
90 | //# sourceMappingURL=index.js.map
91 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
92 | return __REQUIRE__(1593391889373);
93 | })()
94 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/components/aiModels/autoPainter/autoPainter.js:
--------------------------------------------------------------------------------
1 | // compoents/aiModels/autoPainter/autoPainter.js
2 | // Auto painter模型推理库
3 |
4 | /* 加载tensorflow库 */
5 | const tf = require('@tensorflow/tfjs-core')
6 | const tfLayers = require('@tensorflow/tfjs-layers');
7 |
8 | /* 全局常量 */
9 | const MAX_LEN = {
10 | // 'flower': 352,
11 | // 'butterfly': 246,
12 | // 'apple': 189,
13 | // 'house': 201,
14 | // 'sun': 291,
15 | // 'tree': 579
16 | 'flower': 100,
17 | 'butterfly': 100,
18 | 'apple': 100,
19 | 'house': 100,
20 | 'sun': 100,
21 | 'tree': 100
22 | }
23 | const MODELS_URL = {
24 | 'flower': 'https://6169-ai-painter-6g4jfmfl19cfbe8f-1302478925.tcb.qcloud.la/auto-painter/flower/model.json',
25 | 'butterfly': 'https://6169-ai-painter-6g4jfmfl19cfbe8f-1302478925.tcb.qcloud.la/auto-painter/butterfly/model.json',
26 | 'apple': 'https://6169-ai-painter-6g4jfmfl19cfbe8f-1302478925.tcb.qcloud.la/auto-painter/apple/model.json',
27 | 'house': 'https://6169-ai-painter-7q1db-1302478925.tcb.qcloud.la/auto-painter/house/model.json',
28 | 'sun': 'https://6169-ai-painter-6g4jfmfl19cfbe8f-1302478925.tcb.qcloud.la/auto-painter/sun/model.json',
29 | 'tree': 'https://6169-ai-painter-7q1db-1302478925.tcb.qcloud.la/auto-painter/tree/model.json'
30 | }
31 |
32 | /* 全局变量 */
33 | // 所有的模型
34 | let models = {
35 | 'flower': null,
36 | 'butterfly': null,
37 | 'apple': null,
38 | 'sun': null
39 | };
40 | let curModelType = 'apple'; // 当前模型类型
41 |
42 | /**
43 | * 数组转成tensor
44 | */
45 | async function tensorPreprocess(inks) {
46 | return tf.tidy(() => {
47 | //convert to a tensor
48 | let tensor = tf.tensor(inks);
49 | return tensor.expandDims(0);
50 | })
51 | }
52 |
53 | /**
54 | * 加载模型
55 | * @param {string} modelType 模型类型
56 | * @returns true for success, false for failed.
57 | */
58 | async function loadModels(modelType) {
59 | if (modelType) {
60 | curModelType = modelType;
61 | }
62 |
63 | if (models[curModelType]) { // 模型已加载
64 | return true;
65 | }
66 |
67 | // console.time('LoadModel');
68 | console.log('Loading models...');
69 |
70 | /* 加载并预热模型 */
71 | try {
72 | models[curModelType] = await tfLayers.loadLayersModel(MODELS_URL[curModelType]);
73 | } catch (err) {
74 | console.log(err);
75 | return false;
76 | }
77 | let warmupStroke = [
78 | [0., 0., 0., 0.]
79 | ];
80 | let strokeTensor = await tensorPreprocess(warmupStroke);
81 | let pred = await models[curModelType].predict(strokeTensor);
82 | let predArr = await pred.dataSync();
83 | console.log(predArr);
84 | pred.dispose();
85 |
86 | // console.timeEnd('LoadModel');
87 | return true;
88 | }
89 |
90 | /**
91 | * 根据当前起始笔画预测并生成后续笔画
92 | * @param {Array} beginStroke 起始笔画
93 | * @returns 二维数组代表预测的后续笔画
94 | */
95 | async function generate(beginStroke) {
96 | if (models[curModelType] === null) {
97 | console.log("Model unloaded.!");
98 | return null;
99 | }
100 | // console.time('Generate');
101 |
102 | // The initial inks len.
103 | const initialLen = beginStroke.length;
104 | console.log("The initial inks len: " + initialLen);
105 | // Enter the initial inks.
106 | models[curModelType].resetStates();
107 | let strokeTensor = await tensorPreprocess(beginStroke);
108 | let predTf = await models[curModelType].predict(strokeTensor);
109 | let pred = await predTf.dataSync();
110 | predTf.dispose();
111 | // Find the last ink.
112 | const index = (initialLen - 1) * 4;
113 | let pred_ = [pred[index], pred[index + 1], pred[index + 2], pred[index + 3]];
114 | // Save the new ink.
115 | beginStroke.push(pred_);
116 | // Pred the left inks.
117 | let inp = null;
118 | do {
119 | // Use the last ink as input.
120 | inp = [beginStroke[beginStroke.length - 1]];
121 | // Enter the initial inks.
122 | let inpTensor = await tensorPreprocess(inp);
123 | predTf = await models[curModelType].predict(inpTensor);
124 | pred = await predTf.dataSync();
125 | predTf.dispose();
126 | // Find he last ink.
127 | pred_ = [pred[0], pred[1], pred[2] >= 0.5 ? 1.0 : 0.0, pred[3] >= 0.5 ? 1.0 : 0.0];
128 | // Save the new ink.
129 | beginStroke.push(pred_);
130 | } while (pred[3] < 0.5 && beginStroke.length <= MAX_LEN[curModelType] - initialLen);
131 | // console.log(beginStroke);
132 | // Pop the initial inks.
133 | let followStroke = beginStroke.splice(initialLen, beginStroke.length - initialLen);
134 |
135 | // console.timeEnd('Generate');
136 | return followStroke;
137 | }
138 |
139 | let autoPainter = {
140 | loadModels: loadModels,
141 | generate: generate,
142 | };
143 |
144 | module.exports = autoPainter;
--------------------------------------------------------------------------------
/components/aiModels/classifier/classifier.js:
--------------------------------------------------------------------------------
1 | // compoents/aiModels/classifier/classifier.js
2 | // Auto painter模型推理库
3 |
4 | /* 加载tensorflow库 */
5 | const tf = require('@tensorflow/tfjs-core')
6 | const tfLayers = require('@tensorflow/tfjs-layers');
7 | var plugin = requirePlugin('tfjsPlugin');
8 |
9 | /* 常量 */
10 | const MODEL_URL = 'https://6169-ai-painter-6g4jfmfl19cfbe8f-1302478925.tcb.qcloud.la/quick-draw-classifier/model.json'
11 | const CLASSES_NAME_CLOUD_ID = 'cloud://ai-painter-6g4jfmfl19cfbe8f.6169-ai-painter-6g4jfmfl19cfbe8f-1302478925/quick-draw-classifier/classes_names_zh'
12 |
13 | /* 全局变量 */
14 | let model = null; // 模型
15 | let classNames = null; // 记录所有的类型名
16 |
17 | /**
18 | * 数组转成tensor
19 | */
20 | async function tensorPreprocess(inks) {
21 | return tf.tidy(() => {
22 | //convert to a tensor
23 | let tensor = tf.tensor(inks);
24 | return tensor.expandDims(0);
25 | })
26 | }
27 |
28 | /**
29 | * 加载模型
30 | * @returns true for success, false for failed.
31 | */
32 | async function loadModels() {
33 | if (model != null) {
34 | return true;
35 | }
36 |
37 | console.log('Loading models...');
38 | // tf.tensor([1, 2]).print();
39 |
40 | /* 下载并读取类型名文件 */
41 | wx.cloud.init();
42 | wx.cloud.downloadFile({
43 | fileID: CLASSES_NAME_CLOUD_ID,
44 | success: res => {
45 | // console.log(res.tempFilePath);
46 | wx.getFileSystemManager().readFile({
47 | filePath: res.tempFilePath,
48 | encoding: 'utf-8',
49 | success(res) {
50 | if (res.data) {
51 | classNames = res.data.split('\r\n');
52 | console.log(classNames);
53 | }
54 | },
55 | fail(err) {
56 | console.log('读取classes_name失败:', err.errMsg);
57 | }
58 | });
59 | },
60 | fail: err => {
61 | console.log('下载classes_name失败: ' + err.errMsg);
62 | }
63 | });
64 |
65 | // console.time('LoadModel');
66 |
67 | /* 加载模型 */
68 | const fileStorageHandler = plugin.fileStorageIO('classifier', wx.getFileSystemManager());
69 |
70 | try {
71 | model = await tfLayers.loadLayersModel(fileStorageHandler); // 先尝试本地加载模型文件
72 | } catch (err) {
73 | console.log('Load model from local failed: ' + err);
74 | try {
75 | model = await tfLayers.loadLayersModel(MODEL_URL); // 本地加载失败,从云顿加载模型文件
76 | // model.save(fileStorageHandler); // 模型文件保存到本地
77 | } catch (err) {
78 | console.log(err);
79 | return false;
80 | }
81 | }
82 |
83 | /* 预热模型 */
84 | let warmupStroke = [
85 | [0., 0., 0.]
86 | ];
87 | // await classify(warmupStroke);
88 | let strokeTensor = await tensorPreprocess(warmupStroke);
89 | let pred = await model.predict(strokeTensor);
90 | let predArr = await pred.dataSync();
91 | pred.dispose();
92 | model.resetStates();
93 |
94 | // console.timeEnd('LoadModel');
95 | return true;
96 | }
97 |
98 | /**
99 | * get indices of the top probs
100 | */
101 | function findIndicesOfMax(inp, count) {
102 | let outp = [];
103 | for (let i = 0; i < inp.length; i++) {
104 | outp.push(i); // add index to output array
105 | if (outp.length > count) {
106 | outp.sort(function (a, b) {
107 | return inp[b] - inp[a];
108 | }); // descending sort the output array
109 | outp.pop(); // remove the last index (index of smallest element in output array)
110 | }
111 | }
112 | return outp;
113 | }
114 |
115 | /**
116 | * find the top 5 predictions
117 | */
118 | function findTopValues(inp, count) {
119 | let outp = [];
120 | let indices = findIndicesOfMax(inp, count);
121 | // show 5 greatest scores
122 | for (let i = 0; i < indices.length; i++) {
123 | outp[i] = inp[indices[i]];
124 | }
125 | return outp;
126 | }
127 |
128 | /**
129 | * get the the class names
130 | */
131 | function getClassNames(indices) {
132 | let outp = [];
133 | for (let i = 0; i < indices.length; i++) {
134 | outp[i] = classNames[indices[i]];
135 | }
136 | return outp;
137 | }
138 |
139 | /**
140 | * 对笔画进行分类
141 | * @param {Array} predictStroke 笔画
142 | * @param {function} succCb 成功的回调函数,参数obj包含两个属性,probs代表所有类型概率,names代表所有类型名
143 | * @param {function} failCb 失败的回调函数,参数obj包含一个属性,errInfo代表错误描述
144 | */
145 | async function classify(predictStroke, succCb, failCb) {
146 | if (model === null) {
147 | console.log('Model unloaded!');
148 | if (failCb) {
149 | failCb('Model unloaded!');
150 | }
151 | }
152 |
153 | model.resetStates();
154 | // Predict the class name index.
155 | let strokeTensor = await tensorPreprocess(predictStroke);
156 | let pred = await model.predict(strokeTensor);
157 | await pred.data().then(predArr => {
158 | pred.dispose();
159 | // find the top 5 predictions
160 | const indices = findIndicesOfMax(predArr, 5);
161 | // console.log('The predict indices:');
162 | // console.log(indices);
163 | const probs = findTopValues(predArr, 5);
164 | // console.log('The predict probabilities:');
165 | // console.log(probs);
166 | const names = getClassNames(indices);
167 | // console.log('The predict names:');
168 | // console.log(names);
169 | if (succCb) {
170 | succCb({
171 | probs: probs,
172 | names: names
173 | });
174 | }
175 | });
176 | }
177 |
178 | /**
179 | * 重置classifier,主要是重置RNN状态。
180 | */
181 | async function resetClassifier() {
182 | if (model !== undefined) {
183 | model.resetStates();
184 | }
185 | }
186 |
187 | let classifier = {
188 | loadModels: loadModels,
189 | classify: classify,
190 | resetClassifier: resetClassifier,
191 | };
192 |
193 | module.exports = classifier;
--------------------------------------------------------------------------------
/pages/ai-painter/index.wxss:
--------------------------------------------------------------------------------
1 | page{
2 | height: 100%;
3 | width:100%;
4 | }
5 |
6 | .container {
7 | height: 100%;
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | justify-content: space-between;
12 | box-sizing: border-box;
13 | }
14 |
15 | /* 显示的题目 */
16 |
17 | .container .question {
18 | width: 100%;
19 | height: 5%;
20 | background: #f0efef;
21 | display: flex;
22 | flex-direction: row;
23 | justify-content: center;
24 | align-items: center;
25 | color: #fb21a1;
26 | box-shadow: 2rpx 5rpx 2rpx silver;
27 | }
28 |
29 | /* 刷新按钮 */
30 |
31 | .container .question .userinfo-avatar {
32 | height: 50rpx;
33 | width: 50rpx;
34 | border-radius: 50%;
35 | overflow: hidden;
36 | }
37 |
38 | .container .question text {
39 | margin: auto 10rpx auto 20rpx;
40 | font-size: 0.75em;
41 | }
42 |
43 | .container .question .refresh-btn {
44 | width: 50rpx;
45 | height: 50rpx;
46 | transform: scaleX(-1);
47 | }
48 |
49 | /* 中间画板 */
50 |
51 | .container .palette {
52 | width: 100%;
53 | height: 60%;
54 | display: flex;
55 | justify-content: center;
56 | align-items: center;
57 | box-shadow: 2rpx 5rpx 2rpx silver;
58 | }
59 |
60 | /* 按钮的panel */
61 |
62 | .container .option-panel {
63 | padding-bottom: 10rpx;
64 | font-size: 1em;
65 | text-align: center;
66 | background: #f0efef;
67 | width: 100%;
68 | height: 35%;
69 | display: flex;
70 | flex-direction: column;
71 | justify-content: space-between;
72 | align-items: center;
73 | }
74 |
75 | /* 面板中的行 */
76 |
77 | .option-panel .option-row {
78 | width: 100%;
79 | display: flex;
80 | flex-direction: row;
81 | justify-content: flex-start;
82 | align-content: center;
83 | }
84 |
85 | #firstRow{
86 | height: 20%;
87 | }
88 |
89 | #secondRow{
90 | height: 25%;
91 | }
92 |
93 | #thirdRow{
94 | height: 25%;
95 | }
96 |
97 | #forthRow{
98 | height: 20%;
99 | }
100 | /* 面板中的第一行 */
101 |
102 | .option-panel #firstRow .width-container {
103 | width: 50%;
104 | padding-top: 10rpx;
105 | display: flex;
106 | flex-direction: row;
107 | justify-content: space-around;
108 | align-items: center;
109 | }
110 |
111 | /* 笔触大小四个字 */
112 | .option-panel #firstRow .width-container text {
113 | margin-left: 25rpx;
114 | font-size: 0.75em;
115 | color: gray;
116 | }
117 |
118 | .brush-point{
119 | width: 52rpx;
120 | height: 65rpx;
121 | }
122 |
123 | .option-panel #firstRow .useless {
124 | width: 30%;
125 | }
126 |
127 | /* 清除按钮 */
128 | .option-panel #firstRow .icon-text {
129 | padding-top: 10rpx;
130 | width: 20%;
131 | display: flex;
132 | align-items: center;
133 | font-size: 0.75em;
134 | color: gray;
135 | }
136 |
137 | /* option-panel的第二行 */
138 |
139 | .option-panel #secondRow {
140 | display: flex;
141 | justify-content: space-around;
142 | flex-direction: row;
143 | align-items: center;
144 | }
145 |
146 | /*选颜色*/
147 |
148 | /* 左边的箭头 */
149 |
150 | .option-panel #secondRow .color-slecotr-left {
151 | width: 0;
152 | height: 0;
153 | margin-left: 3%;
154 | border-top: 25rpx solid transparent;
155 | border-right: 30rpx solid #c9c8c8;
156 | border-bottom: 25rpx solid transparent;
157 | }
158 |
159 | /* 右边的箭头 */
160 |
161 | .option-panel #secondRow .color-slecotr-right {
162 | width: 0;
163 | height: 0;
164 | margin-right: 3%;
165 | border-top: 25rpx solid transparent;
166 | border-left: 30rpx solid #c9c8c8;
167 | border-bottom: 25rpx solid transparent;
168 | }
169 |
170 | /* 装颜色盒子的srcoll-view */
171 |
172 | .option-panel #secondRow scroll-view {
173 | width: 80%;
174 | display: flex;
175 | flex-direction: row;
176 | white-space: nowrap;
177 | align-content: flex-end;
178 | }
179 |
180 | .option-panel #secondRow scroll-view .color-box {
181 | display: inline-block;
182 | width: 60rpx;
183 | height: 100%;
184 | margin: 0 10rpx 0 10rpx;
185 | }
186 |
187 | /* option-panel的第三行 */
188 |
189 | .option-panel #thirdRow {
190 | display: flex;
191 | justify-content: space-around;
192 | flex-direction: row;
193 | align-items: center;
194 | }
195 |
196 | /*选颜色*/
197 |
198 | /* 左边的箭头 */
199 |
200 | .option-panel #thirdRow .color-slecotr-left {
201 | width: 0;
202 | height: 0;
203 | margin-left: 3%;
204 | border-top: 25rpx solid transparent;
205 | border-right: 30rpx solid #c9c8c8;
206 | border-bottom: 25rpx solid transparent;
207 | }
208 |
209 | /* 右边的箭头 */
210 |
211 | .option-panel #thirdRow .color-slecotr-right {
212 | width: 0;
213 | height: 0;
214 | margin-right: 3%;
215 | border-top: 25rpx solid transparent;
216 | border-left: 30rpx solid #c9c8c8;
217 | border-bottom: 25rpx solid transparent;
218 | }
219 |
220 | /* 装颜色盒子的srcoll-view */
221 |
222 | .option-panel #thirdRow scroll-view {
223 | width: 80%;
224 | display: flex;
225 | flex-direction: row;
226 | white-space: nowrap;
227 | align-content: flex-end;
228 | }
229 |
230 | .option-panel #thirdRow scroll-view .radio-group {
231 | display: flex;
232 | flex-direction: row;
233 | width: 60rpx;
234 | height: 100%;
235 | margin: 0 10rpx 0 10rpx;
236 | }
237 |
238 | .option-panel #thirdRow scroll-view .radio {
239 | width: 50rpx;
240 | height: 100%;
241 | margin-left: 35rpx;
242 | margin-right: 45rpx;
243 | }
244 |
245 | .option-panel #thirdRow scroll-view .radio .text {
246 | font-size: 28rpx;
247 | }
248 |
249 | /* 第四行 */
250 | .option-panel #forthRow .share-btn{
251 | background: #ffcc01;
252 | }
253 |
254 | /* 生成佳作的按钮 */
255 |
256 | .option-panel .share-btn {
257 | width: 90%;
258 | height: 80rpx;
259 | line-height: 80rpx;
260 | font-size: 34rpx;
261 | color: #000;
262 | }
263 |
264 | /*----------------------------------------------------------------------*/
265 |
266 | canvas {
267 | background: black;
268 | width: 100%;
269 | height: 100%;
270 | border: 1rpx solid #ebebeb;
271 | border-radius: 3rpx;
272 | }
273 |
--------------------------------------------------------------------------------
/components/canvasdrawer/canvasdrawer.js:
--------------------------------------------------------------------------------
1 | /* global Component wx */
2 |
3 | Component({
4 | properties: {
5 | painting: {
6 | type: Object,
7 | value: {view: []},
8 | observer (newVal, oldVal) {
9 | if (!this.data.isPainting) {
10 | if (newVal.width && newVal.height) {
11 | this.setData({
12 | isPainting: true
13 | })
14 | this.readyPigment()
15 | }
16 | }
17 | }
18 | }
19 | },
20 | data: {
21 | width: 100,
22 | height: 100,
23 |
24 | index: 0,
25 | imageList: [],
26 | tempFileList: [],
27 |
28 | isPainting: false
29 | },
30 | ctx: null,
31 | cache: {},
32 | ready () {
33 | wx.removeStorageSync('canvasdrawer_pic_cache')
34 | this.cache = wx.getStorageSync('canvasdrawer_pic_cache') || {}
35 | this.ctx = wx.createCanvasContext('canvasdrawer', this)
36 | },
37 | methods: {
38 | readyPigment () {
39 | const { width, height, views } = this.data.painting
40 | this.setData({
41 | width,
42 | height
43 | })
44 |
45 | const inter = setInterval(() => {
46 | if (this.ctx) {
47 | clearInterval(inter)
48 | this.ctx.clearActions()
49 | this.drawRect({
50 | background: 'white',
51 | top: 0,
52 | left: 0,
53 | width,
54 | height
55 | })
56 | this.getImageList(views)
57 | this.downLoadImages(0)
58 | }
59 | }, 100)
60 | },
61 | getImageList (views) {
62 | const imageList = []
63 | for (let i = 0; i < views.length; i++) {
64 | if (views[i].type === 'image') {
65 | imageList.push(views[i].url)
66 | }
67 | }
68 | this.setData({
69 | imageList
70 | })
71 | },
72 | downLoadImages (index) {
73 | const { imageList, tempFileList } = this.data
74 | if (index < imageList.length) {
75 | // console.log(imageList[index])
76 | this.getImageInfo(imageList[index]).then(file => {
77 | tempFileList.push(file)
78 | this.setData({
79 | tempFileList
80 | })
81 | this.downLoadImages(index + 1)
82 | })
83 | } else {
84 | this.startPainting()
85 | }
86 | },
87 | startPainting () {
88 | const { tempFileList, painting: { views } } = this.data
89 | for (let i = 0, imageIndex = 0; i < views.length; i++) {
90 | if (views[i].type === 'image') {
91 | this.drawImage({
92 | ...views[i],
93 | url: tempFileList[imageIndex]
94 | })
95 | imageIndex++
96 | } else if (views[i].type === 'text') {
97 | if (!this.ctx.measureText) {
98 | wx.showModal({
99 | title: '提示',
100 | content: '当前微信版本过低,无法使用 measureText 功能,请升级到最新微信版本后重试。'
101 | })
102 | } else {
103 | this.drawText(views[i])
104 | }
105 | } else if (views[i].type === 'rect') {
106 | this.drawRect(views[i])
107 | }
108 | }
109 | this.ctx.draw(true, () => {
110 | wx.setStorageSync('canvasdrawer_pic_cache', this.cache)
111 | this.saveImageToLocal()
112 | })
113 | },
114 | drawImage (params) {
115 | // console.log(params)
116 | const { url, top = 0, left = 0, width = 0, height = 0 } = params
117 | this.ctx.drawImage(url, left, top, width, height)
118 | },
119 | drawText (params) {
120 | const {
121 | MaxLineNumber = 2,
122 | breakWord = false,
123 | color = 'black',
124 | content = '',
125 | fontSize = 16,
126 | top = 0,
127 | left = 0,
128 | lineHeight = 20,
129 | textAlign = 'left',
130 | width,
131 | bolder = false,
132 | textDecoration = 'none'
133 | } = params
134 |
135 | this.ctx.setTextBaseline('top')
136 | this.ctx.setTextAlign(textAlign)
137 | this.ctx.setFillStyle(color)
138 | this.ctx.setFontSize(fontSize)
139 |
140 | if (!breakWord) {
141 | this.ctx.fillText(content, left, top)
142 | this.drawTextLine(left, top, textDecoration, color, fontSize, content)
143 | } else {
144 | let fillText = ''
145 | let fillTop = top
146 | let lineNum = 1
147 | for (let i = 0; i < content.length; i++) {
148 | fillText += [content[i]]
149 | if (this.ctx.measureText(fillText).width > width) {
150 | if (lineNum === MaxLineNumber) {
151 | if (i !== content.length) {
152 | fillText = fillText.substring(0, fillText.length - 1) + '...'
153 | this.ctx.fillText(fillText, left, fillTop)
154 | this.drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText)
155 | fillText = ''
156 | break
157 | }
158 | }
159 | this.ctx.fillText(fillText, left, fillTop)
160 | this.drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText)
161 | fillText = ''
162 | fillTop += lineHeight
163 | lineNum ++
164 | }
165 | }
166 | this.ctx.fillText(fillText, left, fillTop)
167 | this.drawTextLine(left, fillTop, textDecoration, color, fontSize, fillText)
168 | }
169 |
170 | if (bolder) {
171 | this.drawText({
172 | ...params,
173 | left: left + 0.3,
174 | top: top + 0.3,
175 | bolder: false,
176 | textDecoration: 'none'
177 | })
178 | }
179 | },
180 | drawTextLine (left, top, textDecoration, color, fontSize, content) {
181 | if (textDecoration === 'underline') {
182 | this.drawRect({
183 | background: color,
184 | top: top + fontSize * 1.2,
185 | left: left - 1,
186 | width: this.ctx.measureText(content).width + 2,
187 | height: 1
188 | })
189 | } else if (textDecoration === 'line-through') {
190 | this.drawRect({
191 | background: color,
192 | top: top + fontSize * 0.6,
193 | left: left - 1,
194 | width: this.ctx.measureText(content).width + 2,
195 | height: 1
196 | })
197 | }
198 | },
199 | drawRect (params) {
200 | // console.log(params)
201 | const { background, top = 0, left = 0, width = 0, height = 0 } = params
202 | this.ctx.setFillStyle(background)
203 | this.ctx.fillRect(left, top, width, height)
204 | },
205 | getImageInfo (url) {
206 | return new Promise((resolve, reject) => {
207 | /* 获得要在画布上绘制的图片 */
208 | if (this.cache[url]) {
209 | resolve(this.cache[url])
210 | } else {
211 | const objExp = new RegExp(/^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/)
212 | if (objExp.test(url)) {
213 | wx.getImageInfo({
214 | src: url,
215 | complete: res => {
216 | if (res.errMsg === 'getImageInfo:ok') {
217 | this.cache[url] = res.path
218 | resolve(res.path)
219 | } else {
220 | reject(new Error('getImageInfo fail'))
221 | }
222 | }
223 | })
224 | } else {
225 | this.cache[url] = url
226 | resolve(url)
227 | }
228 | }
229 | })
230 | },
231 | saveImageToLocal () {
232 | const { width, height } = this.data
233 | wx.canvasToTempFilePath({
234 | x: 0,
235 | y: 0,
236 | width,
237 | height,
238 | canvasId: 'canvasdrawer',
239 | success: res => {
240 | if (res.errMsg === 'canvasToTempFilePath:ok') {
241 | this.setData({
242 | isPainting: false,
243 | imageList: [],
244 | tempFileList: []
245 | })
246 | this.triggerEvent('getImage', {tempFilePath: res.tempFilePath})
247 | }
248 | }
249 | }, this)
250 | }
251 | }
252 | })
253 |
--------------------------------------------------------------------------------
/components/helpers/popupMixin.js:
--------------------------------------------------------------------------------
1 | import classNames from './classNames'
2 | import eventsMixin from './eventsMixin'
3 |
4 | const DEFAULT_TRIGGER = 'onClick'
5 | const CELL_NAME = '../cell/index'
6 | const FIELD_NAME = '../field/index'
7 |
8 | const defaultToolbar = {
9 | title: '请选择',
10 | cancelText: '取消',
11 | confirmText: '确定',
12 | }
13 |
14 | const defaultEvents = {
15 | onChange() {},
16 | onConfirm() {},
17 | onCancel() {},
18 | onVisibleChange() {},
19 | onValueChange() {},
20 | }
21 |
22 | const defaultPlatformProps = {
23 | labelPropName: 'label',
24 | format(values, props) {
25 | return Array.isArray(values.displayValue) ? values.displayValue.join(',') : values.displayValue
26 | },
27 | }
28 |
29 | export default function popupMixin(selector = '#wux-picker', platformProps = defaultPlatformProps) {
30 | return Behavior({
31 | behaviors: [eventsMixin({ defaultEvents })],
32 | properties: {
33 | toolbar: {
34 | type: Object,
35 | value: defaultToolbar,
36 | },
37 | trigger: {
38 | type: String,
39 | value: DEFAULT_TRIGGER,
40 | },
41 | defaultVisible: {
42 | type: Boolean,
43 | value: false,
44 | },
45 | visible: {
46 | type: Boolean,
47 | value: false,
48 | },
49 | controlled: {
50 | type: Boolean,
51 | value: false,
52 | },
53 | disabled: {
54 | type: Boolean,
55 | value: false,
56 | },
57 | },
58 | data: {
59 | mounted: false,
60 | popupVisible: false,
61 | inputValue: [],
62 | },
63 | methods: {
64 | /**
65 | * 设置组件显示或隐藏
66 | */
67 | setVisibleState(popupVisible, callback = () => {}) {
68 | if (this.data.popupVisible !== popupVisible) {
69 | const params = {
70 | mounted: true,
71 | inputValue: this.data.value, // forceUpdate
72 | popupVisible,
73 | }
74 | this.setData(popupVisible ? params : { popupVisible }, () => {
75 | // collect field component & forceUpdate
76 | if (popupVisible && this.hasFieldDecorator) {
77 | const field = this.getFieldElem()
78 | if (field) {
79 | field.changeValue(field.data.value)
80 | }
81 | }
82 | callback()
83 | })
84 | }
85 | },
86 | /**
87 | * 触发 visibleChange 事件
88 | */
89 | fireVisibleChange(popupVisible) {
90 | if (this.data.popupVisible !== popupVisible) {
91 | if (!this.data.controlled) {
92 | this.setVisibleState(popupVisible)
93 | }
94 | this.setScrollValue(undefined)
95 | this.triggerEvent('visibleChange', { visible: popupVisible })
96 | }
97 | },
98 | /**
99 | * 打开
100 | */
101 | open() {
102 | this.fireVisibleChange(true)
103 | },
104 | /**
105 | * 关闭
106 | */
107 | close(callback) {
108 | if (typeof callback === 'function') {
109 | const values = this.getPickerValue(this.scrollValue || this.data.inputValue)
110 | callback.call(this, this.formatPickerValue(values))
111 | }
112 | this.fireVisibleChange(false)
113 | },
114 | /**
115 | * 组件关闭时重置其内部数据
116 | */
117 | onClosed() {
118 | this.picker = null
119 | this.setData({ mounted: false, inputValue: null })
120 | },
121 | /**
122 | * 点击确定按钮时的回调函数
123 | */
124 | onConfirm() {
125 | this.close((values) => {
126 | this.triggerEvent('change', values) // collect field component
127 | this.triggerEvent('confirm', values)
128 | })
129 | },
130 | /**
131 | * 点击取消按钮时的回调函数
132 | */
133 | onCancel() {
134 | this.close((values) => this.triggerEvent('cancel', values))
135 | },
136 | /**
137 | * 每列数据选择变化后的回调函数
138 | */
139 | onValueChange(e) {
140 | if (!this.data.mounted) return
141 | const { value } = e.detail
142 | if (this.data.cascade) {
143 | this.setCasecadeScrollValue(value)
144 | } else {
145 | this.setScrollValue(value)
146 | }
147 |
148 | this.updated(value, true)
149 | this.triggerEvent('valueChange', this.formatPickerValue(e.detail))
150 | },
151 | /**
152 | * 获取当前 picker 的值
153 | */
154 | getPickerValue(value = this.data.inputValue) {
155 | this.picker = this.picker || this.selectComponent(selector)
156 | return this.picker && this.picker.getValue(value)
157 | },
158 | /**
159 | * 格式化 picker 返回值
160 | */
161 | formatPickerValue(values) {
162 | return {
163 | ...values,
164 | [platformProps.labelPropName]: platformProps.format(values, this.data),
165 | }
166 | },
167 | /**
168 | * 获取 field 父元素
169 | */
170 | getFieldElem() {
171 | return this.field = (this.field || this.getRelationNodes(FIELD_NAME)[0])
172 | },
173 | /**
174 | * 设置子元素 props
175 | */
176 | setChildProps() {
177 | if (this.data.disabled) return
178 | const elements = this.getRelationNodes(CELL_NAME)
179 | const { trigger = DEFAULT_TRIGGER } = this.data
180 | if (elements.length > 0) {
181 | elements.forEach((inputElem) => {
182 | const { inputEvents } = inputElem.data
183 | const oriInputEvents = inputElem.data.oriInputEvents || { ...inputEvents }
184 | inputEvents[trigger] = (...args) => {
185 | if (oriInputEvents && oriInputEvents[trigger]) {
186 | oriInputEvents[trigger](...args)
187 | }
188 | this.onTriggerClick()
189 | }
190 | inputElem.setData({ oriInputEvents, inputEvents })
191 | })
192 | }
193 | },
194 | /**
195 | * 触发事件
196 | */
197 | onTriggerClick() {
198 | this.fireVisibleChange(!this.data.popupVisible)
199 | },
200 | /**
201 | * 阻止移动触摸
202 | */
203 | noop() {},
204 | /**
205 | * 更新值
206 | */
207 | updated(inputValue, isForce) {
208 | if (!this.hasFieldDecorator || isForce) {
209 | if (this.data.inputValue !== inputValue) {
210 | this.setData({ inputValue })
211 | }
212 | }
213 | },
214 | /**
215 | * 记录每列数据的变化值
216 | */
217 | setScrollValue(value) {
218 | this.scrollValue = value
219 | },
220 | /**
221 | * 记录每列数据的变化值 - 针对于级联
222 | */
223 | setCasecadeScrollValue(value) {
224 | if (value && this.scrollValue) {
225 | const length = this.scrollValue.length
226 | if (length === value.length && this.scrollValue[length - 1] === value[length - 1]) {
227 | return
228 | }
229 | }
230 | this.setScrollValue(value)
231 | },
232 | },
233 | lifetimes: {
234 | ready() {
235 | const { defaultVisible, visible, controlled, value } = this.data
236 | const popupVisible = controlled ? visible : defaultVisible
237 |
238 | this.mounted = true
239 | this.scrollValue = undefined
240 | this.setVisibleState(popupVisible)
241 | this.setChildProps()
242 | },
243 | detached() {
244 | this.mounted = false
245 | },
246 | },
247 | definitionFilter(defFields) {
248 | // set default child
249 | Object.assign(defFields.relations = (defFields.relations || {}), {
250 | [CELL_NAME]: {
251 | type: 'child',
252 | observer() {
253 | this.setChildProps()
254 | },
255 | },
256 | [FIELD_NAME]: {
257 | type: 'ancestor',
258 | },
259 | })
260 |
261 | // set default classes
262 | Object.assign(defFields.computed = (defFields.computed || {}), {
263 | classes: ['prefixCls', function(prefixCls) {
264 | const wrap = classNames(prefixCls)
265 | const toolbar = `${prefixCls}__toolbar`
266 | const inner = `${prefixCls}__inner`
267 | const cancel = classNames(`${prefixCls}__button`, {
268 | [`${prefixCls}__button--cancel`]: true
269 | })
270 | const confirm = classNames(`${prefixCls}__button`, {
271 | [`${prefixCls}__button--confirm`]: true
272 | })
273 | const hover = `${prefixCls}__button--hover`
274 | const title = `${prefixCls}__title`
275 |
276 | return {
277 | wrap,
278 | toolbar,
279 | inner,
280 | cancel,
281 | confirm,
282 | hover,
283 | title,
284 | }
285 | }],
286 | })
287 |
288 | // set default observers
289 | Object.assign(defFields.observers = (defFields.observers || {}), {
290 | visible(popupVisible) {
291 | if (this.data.controlled) {
292 | this.setVisibleState(popupVisible)
293 | }
294 | },
295 | value(value) {
296 | this.updated(value)
297 | },
298 | })
299 | },
300 | })
301 | }
302 |
--------------------------------------------------------------------------------
/pages/ai-classifier/index.js:
--------------------------------------------------------------------------------
1 | // pages/ai-classifier/index.js
2 |
3 | const app = getApp();
4 |
5 | const classifier = require('../../components/aiModels/classifier/classifier.js');
6 | const fetchWechat = require('fetch-wechat');
7 | const tf = require('@tensorflow/tfjs-core');
8 | const cpu = require('@tensorflow/tfjs-backend-cpu');
9 | const webgl = require('@tensorflow/tfjs-backend-webgl');
10 | const plugin = requirePlugin('tfjsPlugin');
11 | const util = require('../../utils/util.js')
12 |
13 | const DRAW_SIZE = [255, 255];
14 | const DRAW_PREC = 0.03;
15 |
16 | var drawInfos = [];
17 | var startX = 0;
18 | var startY = 0;
19 | var bgColor = "white";
20 | var begin = false;
21 | var curDrawArr = [];
22 | var lastCoord = null; // 记录画笔上一个坐标
23 | var predictStroke = []; // 用于预测的笔画偏移量数组
24 | var drawing = false; // 正在画
25 | var classifing = false; // 正在猜
26 | const promiseObj = new util.promiseContainer(); // 只响应最后一次promise
27 |
28 | Page({
29 | /**
30 | * 页面的初始数据
31 | */
32 | data: {
33 | levelId: 0,
34 | status: "正在努力加载模型ᕙ༼ ͝°益° ༽ᕗ",
35 | hidden: true, // 是否隐藏生成海报的canvas
36 | bgColor: bgColor,
37 | currentColor: 'black',
38 | avatarUrl: "",
39 | curWidthIndex: 0,
40 | lineWidthArr: [3, 5, 10, 20],
41 | avaliableColors: ["black", "red", "blue", "gray", "#ff4081",
42 | "#009681", "#259b24", "green", "#0000CD", "#1E90FF", "#ADD8E6", "#FAEBD7", "#FFF0F5", // orange
43 | '#FFEBA3', '#FFDE7A', '#FFCE52', '#FFBB29', '#FFA500', '#D98600', '#B36800', '#8C4D00', '#663500',
44 | // red
45 | '#FFAFA3', '#FF887A', '#FF5D52', '#FF3029', '#FF0000', '#D90007', '#B3000C', '#8C000E', '#66000E',
46 | // green
47 | '#7BB372', '#58A650', '#389931', '#1A8C16', '#008000', '#005903', '#003303',
48 | // yellow
49 | '#FFF27A', '#FFF352', '#FFF829', '#FFFF00', '#D2D900', '#A7B300', '#7E8C00', '#586600',
50 | // cyan
51 | '#A3FFF3', '#7AFFF2', '#52FFF3', '#29FFF8', '#00FFFF', '#00D2D9', '#00A7B3', '#007E8C', '#005866',
52 | // blue
53 | '#A3AFFF', '#7A88FF', '#525DFF', '#2930FF', '#0000FF', '#0700D9', '#0C00B3', '#0E008C', '#0E0066',
54 | // Violet
55 | '#FAB1F7', '#EE82EE', '#C463C7', '#9B48A1', '#73317A', '#4D2154',
56 | "black"
57 | ],
58 | classesName: ['class1', 'class2', 'class3', 'class4', 'class5'],
59 | classesProg: [0, 0, 0, 0, 0]
60 | },
61 |
62 | /**
63 | * 生命周期函数--监听页面加载
64 | */
65 | onLoad: function (options) {
66 | wx.showShareMenu({
67 | withShareTicket: true
68 | });
69 |
70 | let systemInfo = wx.getSystemInfoSync();
71 | console.log(systemInfo.platform);
72 | if (systemInfo.platform == 'android') {
73 | plugin.configPlugin({
74 | // polyfill fetch function
75 | fetchFunc: fetchWechat.fetchFunc(),
76 | // inject tfjs runtime
77 | tf,
78 | cpu,
79 | canvas: wx.createOffscreenCanvas()
80 | });
81 | // setWasmPath('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@2.0.0/wasm-out/tfjs-backend-wasm.wasm', true);
82 | // tf.setBackend('wasm').then(() => console.log('set wasm backend'));
83 | } else {
84 | plugin.configPlugin({
85 | // polyfill fetch function
86 | fetchFunc: fetchWechat.fetchFunc(),
87 | // inject tfjs runtime
88 | tf,
89 | // inject backend
90 | webgl,
91 | // provide webgl canvas
92 | canvas: wx.createOffscreenCanvas()
93 | });
94 | }
95 | },
96 |
97 | /**
98 | * 生命周期函数--监听页面初次渲染完成
99 | */
100 | onReady: function () {
101 | this.context = wx.createCanvasContext("firstCanvas");
102 | this.init();
103 | this.fillBackground(this.context);
104 | this.draw();
105 |
106 | predictStroke = [];
107 | wx.showLoading({
108 | title: '加载模型...',
109 | mask: true,
110 | });
111 | classifier.loadModels().then((res) => {
112 | wx.hideLoading();
113 | if (res) {
114 | this.setData({
115 | status: '你来作画,我来猜!(⊙ᗜ⊙)'
116 | });
117 | this.setCurrentColor(this.data.avaliableColors[Math.floor((Math.random() * this.data.avaliableColors.length))]);
118 | wx.showToast({
119 | title: '加载完成',
120 | })
121 | } else {
122 | this.setData({
123 | status: '模型加载超时(´□`川)'
124 | });
125 | wx.showModal({
126 | title: '加载失败',
127 | content: '刷新一下?',
128 | success(res) {
129 | if (res.confirm) {
130 | wx.reLaunch({
131 | url: '/pages/ai-classifier/index',
132 | })
133 | }
134 | }
135 | });
136 | }
137 | });
138 | },
139 |
140 | /**
141 | * 生命周期函数--监听页面显示
142 | */
143 | onShow: function () {},
144 |
145 | /**
146 | * 生命周期函数--监听页面隐藏
147 | */
148 | onHide: function () {
149 |
150 | },
151 |
152 | /**
153 | * 生命周期函数--监听页面卸载
154 | */
155 | onUnload: function () {
156 |
157 | },
158 |
159 | /**
160 | * 页面相关事件处理函数--监听用户下拉动作
161 | */
162 | onPullDownRefresh: function () {
163 |
164 | },
165 |
166 | /**
167 | * 页面上拉触底事件的处理函数
168 | */
169 | onReachBottom: function () {
170 |
171 | },
172 |
173 | /**
174 | * 用户点击右上角分享
175 | */
176 | onShareAppMessage: function (res) {
177 |
178 | },
179 |
180 | /*--------------------- UI事件 --------------------------------------------------- */
181 | // 绘制开始 手指开始按到屏幕上
182 | touchStart: function (e) {
183 | if (classifing) {
184 | return;
185 | }
186 | drawing = true;
187 |
188 | this.lineBegin(e.touches[0].x, e.touches[0].y);
189 | this.recordPredictStroke(e.touches[0].x, e.touches[0].y);
190 | this.draw(true);
191 | curDrawArr.push({
192 | x: e.touches[0].x,
193 | y: e.touches[0].y
194 | });
195 | },
196 |
197 | // 绘制中 手指在屏幕上移动
198 | touchMove: function (e) {
199 | if (classifing) {
200 | return;
201 | }
202 | drawing = true;
203 |
204 | if (begin) {
205 | this.lineAddPoint(e.touches[0].x, e.touches[0].y);
206 | this.recordPredictStroke(e.touches[0].x, e.touches[0].y);
207 | this.draw(true);
208 | curDrawArr.push({
209 | x: e.touches[0].x,
210 | y: e.touches[0].y
211 | });
212 | }
213 | },
214 |
215 | // 绘制结束 手指抬起
216 | touchEnd: function () {
217 | drawing = false;
218 | if (classifing) {
219 | return;
220 | }
221 |
222 | drawInfos.push({
223 | drawArr: curDrawArr,
224 | color: this.data.currentColor,
225 | lineWidth: this.data.lineWidthArr[this.data.curWidthIndex],
226 | });
227 | // console.log(curDrawArr);
228 | curDrawArr = [];
229 | this.lineEnd();
230 | predictStroke[predictStroke.length - 1][2] = 1.0;
231 |
232 | this.setData({
233 | status: Math.random() > 0.5 ? '正在思索中[(--)]zzz' : '绞尽脑汁中[(--)]zzz',
234 | });
235 |
236 | promiseObj.addPromise(new Promise((resolve) => setTimeout(resolve, 800)), () => {
237 | if (drawing) {
238 | console.log('Drawing, do not predict.');
239 | return true;
240 | }
241 | classifing = true;
242 | wx.showLoading({
243 | title: Math.random() > 0.5 ? '冥思中' : '思考中',
244 | mask: true
245 | })
246 | classifier.classify(predictStroke, (res) => {
247 | // console.log(res);
248 | wx.hideLoading();
249 | let probs = res.probs;
250 | for (let i = 0; i < probs.length; i++) {
251 | probs[i] = Number(probs[i] * 100).toFixed(1);
252 | }
253 |
254 | classifing = false;
255 | this.setData({
256 | status: '我猜出来了!(⊙ᗜ⊙)'
257 | });
258 | this.setData({
259 | classesName: res.names,
260 | classesProg: probs
261 | });
262 | // predictStroke = [];
263 | }, res => {
264 | wx.hideLoading();
265 | classifing = false;
266 | this.setData({
267 | status: '我猜不出来(╥╯^╰╥)',
268 | });
269 | });
270 | });
271 |
272 | this.setCurrentColor(this.data.avaliableColors[Math.floor((Math.random() * this.data.avaliableColors.length))]);
273 | },
274 |
275 |
276 | // 点击设置线条宽度
277 | clickChangeWidth: function (e) {
278 | let index = e.currentTarget.dataset.index;
279 | this.setLineWidthByIndex(index);
280 | },
281 |
282 | // 点击设置线条颜色
283 | clickChangeColor: function (e) {
284 | let color = e.currentTarget.dataset.color;
285 | this.setCurrentColor(color);
286 | },
287 |
288 | // 点击切换到擦除
289 | clickErase: function () {
290 | this.setCurrentColor(bgColor);
291 | },
292 |
293 | // 点击清空canvas
294 | clickClearAll: function () {
295 | this.fillBackground(this.context);
296 | this.draw();
297 | drawInfos = [];
298 | lastCoord = null;
299 | predictStroke = [];
300 | this.setData({
301 | classesName: ['class1', 'class2', 'class3', 'class4', 'class5'],
302 | classesProg: [0, 0, 0, 0, 0]
303 | });
304 | classifier.resetClassifier();
305 | this.init();
306 | },
307 |
308 | // 点击撤销上一步
309 | clickFallback: function () {
310 | if (drawInfos.length >= 1) {
311 | drawInfos.pop();
312 | }
313 | this.reDraw();
314 | },
315 |
316 | // 点击重绘canvas
317 | clickReDraw: function () {
318 | this.reDraw();
319 | },
320 |
321 | // 保存canvas图像到本地缓存
322 | clickStore: function () {
323 | let that = this;
324 | this.store("firstCanvas", res => {
325 | wx.saveImageToPhotosAlbum({
326 | filePath: res,
327 | })
328 | });
329 | },
330 |
331 | // 点击分享
332 | // 现在的功能是拼接一个分享出去的图像,然后保存到本地相册
333 | clickShare: function () {
334 | let that = this;
335 | // 在用户看不到的地方显示隐藏的canvas
336 | this.setData({
337 | hidden: false
338 | });
339 | // 截图的时候屏蔽用户操作
340 | wx.showLoading({
341 | title: '请稍等',
342 | mask: true
343 | })
344 | // 300毫秒后恢复正常
345 | setTimeout(() => {
346 | wx.hideLoading();
347 | that.setData({
348 | hidden: true
349 | });
350 | }, 300);
351 |
352 | // 截图用户绘制的canvaas
353 | this.store("firstCanvas", filePath => {
354 | // 生成海报并分享
355 |
356 | that.sharePost(filePath);
357 |
358 | });
359 | },
360 |
361 | /*---------------------end of 界面绑定的函数------------------------------------------ */
362 |
363 | // 初始化
364 | init: function () {
365 | this.context.setLineCap('round') // 让线条圆润
366 | this.context.strokeStyle = this.data.currentColor;
367 | this.context.setLineWidth(this.data.lineWidthArr[this.data.curWidthIndex]);
368 | this.setData({
369 | currentColor: this.data.currentColor,
370 | curWidthIndex: this.data.curWidthIndex
371 | });
372 | },
373 |
374 | // 设置线条颜色
375 | setCurrentColor: function (color) {
376 | this.data.currentColor = color;
377 | this.context.strokeStyle = color;
378 | this.setData({
379 | currentColor: color
380 | });
381 | },
382 |
383 | // 设置线条宽度
384 | setLineWidthByIndex: function (index) {
385 | let width = this.data.lineWidthArr[index];
386 | this.context.setLineWidth(width);
387 | this.setData({
388 | curWidthIndex: index
389 | });
390 | },
391 |
392 | // 开始绘制线条
393 | lineBegin: function (x, y) {
394 | begin = true;
395 | this.context.beginPath()
396 | startX = x;
397 | startY = y;
398 | this.context.moveTo(startX, startY)
399 | this.lineAddPoint(x, y);
400 | },
401 |
402 | // 绘制线条中间添加点
403 | lineAddPoint: function (x, y) {
404 | this.context.moveTo(startX, startY)
405 | this.context.lineTo(x, y)
406 | this.context.stroke();
407 | startX = x;
408 | startY = y;
409 | },
410 |
411 | // 绘制线条结束
412 | lineEnd: function () {
413 | this.context.closePath();
414 | begin = false;
415 | },
416 |
417 | // 根据保存的绘制信息重新绘制
418 | reDraw: function () {
419 | this.init();
420 | this.fillBackground(this.context);
421 | // this.draw(false);
422 | for (var i = 0; i < drawInfos.length; i++) {
423 | this.context.strokeStyle = drawInfos[i].color;
424 | this.context.setLineWidth(drawInfos[i].lineWidth);
425 | let drawArr = drawInfos[i].drawArr;
426 | this.lineBegin(drawArr[0].x, drawArr[0].y)
427 | for (var j = 1; j < drawArr.length; j++) {
428 | this.lineAddPoint(drawArr[j].x, drawArr[j].y);
429 | // this.draw(true);
430 | }
431 |
432 | this.lineEnd();
433 | }
434 |
435 | this.draw();
436 | },
437 |
438 | // 将canvas导出为临时图像文件
439 | // canvasId: 要导出的canvas的id
440 | // cb: 回调函数
441 | store: function (canvasId, cb) {
442 | wx.canvasToTempFilePath({
443 | destWidth: 400,
444 | destHeight: 300,
445 | canvasId: canvasId,
446 | success: function (res) {
447 | typeof (cb) == 'function' && cb(res.tempFilePath);
448 | },
449 | fail: function (res) {
450 | console.log("store fail");
451 | console.log(res);
452 | }
453 | })
454 | },
455 |
456 | // 绘制canvas
457 | // isReverse: 是否保留之前的像素
458 | draw: function (isReverse = false, cb) {
459 | this.context.draw(isReverse, () => {
460 | if (cb && typeof (cb) == "function") {
461 | cb();
462 | }
463 | });
464 | },
465 |
466 |
467 | // canvas上下文设置背景为白色
468 | fillBackground: function (context) {
469 | context.setFillStyle(bgColor);
470 | context.fillRect(0, 0, 500, 500); //TODO context的宽和高待定
471 | context.fill();
472 | },
473 |
474 |
475 | // 预览
476 | pageView: function () {
477 | wx.canvasToTempFilePath({
478 | canvasId: 'firstCanvas',
479 | success: function (res) {
480 | wx.previewImage({
481 | current: res.tempFilePath,
482 | urls: [res.tempFilePath],
483 | success: function (res) {
484 |
485 | },
486 | fail: function (res) {
487 |
488 | },
489 | });
490 | },
491 | fail: function (res) {
492 | wx.showToast({
493 | title: '保存失败',
494 | icon: 'none'
495 | })
496 | }
497 | }, this)
498 | },
499 |
500 | // 分享海报
501 | sharePost: function (filePath) {
502 | let url = `/pages/share/share?imgUrl=${filePath}&levelId=${this.data.levelId}`;
503 | wx.navigateTo({
504 | url: url
505 | })
506 | },
507 |
508 | // 记录笔画偏移量数组,用于预测分类
509 | recordPredictStroke: function (x, y) {
510 | var posX = x;
511 | var posY = y;
512 | if (lastCoord !== null) {
513 | // Calc the delta.
514 | let xDelta = posX - lastCoord[0];
515 | let yDelta = lastCoord[1] - posY; // Reverse the y coordinate.
516 | // Normalization.
517 | xDelta = Number((xDelta / DRAW_SIZE[0]).toFixed(10));
518 | yDelta = Number((yDelta / DRAW_SIZE[1]).toFixed(10));
519 | // xDelta = xDelta / DRAW_SIZE[0];
520 | // yDelta = yDelta / DRAW_SIZE[1];
521 | if (predictStroke.length > 0) {
522 | if (xDelta === 0.0 && predictStroke[predictStroke.length - 1][0] === 0.0) {
523 | // Merge if only move in y axis.
524 | predictStroke[predictStroke.length - 1][1] += yDelta;
525 | lastCoord = [posX, posY];
526 | return;
527 | }
528 | if (yDelta === 0.0 && predictStroke[predictStroke.length - 1][1] === 0.0) {
529 | // Merge if only move in x axis.
530 | predictStroke[predictStroke.length - 1][0] += xDelta;
531 | lastCoord = [posX, posY];
532 | return;
533 | }
534 | }
535 | // Ignore < DRAW_PREC.
536 | if (Math.abs(xDelta) >= DRAW_PREC || Math.abs(yDelta) >= DRAW_PREC) {
537 | predictStroke.push([xDelta, yDelta, 0.0]);
538 | lastCoord = [posX, posY];
539 | }
540 | // console.log(predictStroke)
541 | } else {
542 | lastCoord = [posX, posY];
543 | }
544 | }
545 | })
--------------------------------------------------------------------------------
/pages/ai-painter/index.js:
--------------------------------------------------------------------------------
1 | // pages/ai-painter/index.js
2 |
3 | const app = getApp();
4 |
5 | const autoPainter = require('../../components/aiModels/autoPainter/autoPainter.js');
6 | const fetchWechat = require('fetch-wechat');
7 | const tf = require('@tensorflow/tfjs-core');
8 | const cpu = require('@tensorflow/tfjs-backend-cpu');
9 | const webgl = require('@tensorflow/tfjs-backend-webgl');
10 | const plugin = requirePlugin('tfjsPlugin');
11 |
12 | // 不同图像尺寸大小
13 | const DRAW_SIZE = [
14 | [61.25, 61.25],
15 | [122.5, 122.5],
16 | [183.75, 183.75],
17 | [255, 255]
18 | ];
19 | const DRAW_PREC = 0.02;
20 |
21 | var drawInfos = []
22 | var startX = 0
23 | var startY = 0
24 | var bgColor = "white"
25 | var begin = false
26 | var curDrawArr = []
27 | var lastCoord = null; // 记录画笔上一个坐标
28 | let beginPosition = null; // 记录后续笔画的其实坐标
29 | var predictStroke = []; // 用于预测的笔画偏移量数组
30 | var predicting = false; // 正在预测
31 |
32 | Page({
33 | /**
34 | * 页面的初始数据
35 | */
36 | data: {
37 | levelId: 0,
38 | status: "正在努力加载模型ᕙ༼ ͝°益° ༽ᕗ",
39 | hidden: true, // 是否隐藏生成海报的canvas
40 | bgColor: bgColor,
41 | currentColor: 'black',
42 | avatarUrl: "",
43 | curSizeIndex: 1,
44 | imgSizeArr: [1, 5, 10, 20],
45 | curWidthIndex: 0,
46 | lineWidthArr: [3, 5, 10, 20],
47 | avaliableColors: ["black", "red", "blue", "gray", "#ff4081",
48 | "#009681", "#259b24", "green", "#0000CD", "#1E90FF", "#ADD8E6", "#FAEBD7", "#FFF0F5", // orange
49 | '#FFEBA3', '#FFDE7A', '#FFCE52', '#FFBB29', '#FFA500', '#D98600', '#B36800', '#8C4D00', '#663500',
50 | // red
51 | '#FFAFA3', '#FF887A', '#FF5D52', '#FF3029', '#FF0000', '#D90007', '#B3000C', '#8C000E', '#66000E',
52 | // green
53 | '#7BB372', '#58A650', '#389931', '#1A8C16', '#008000', '#005903', '#003303',
54 | // yellow
55 | '#FFF27A', '#FFF352', '#FFF829', '#FFFF00', '#D2D900', '#A7B300', '#7E8C00', '#586600',
56 | // cyan
57 | '#A3FFF3', '#7AFFF2', '#52FFF3', '#29FFF8', '#00FFFF', '#00D2D9', '#00A7B3', '#007E8C', '#005866',
58 | // blue
59 | '#A3AFFF', '#7A88FF', '#525DFF', '#2930FF', '#0000FF', '#0700D9', '#0C00B3', '#0E008C', '#0E0066',
60 | // Violet
61 | '#FAB1F7', '#EE82EE', '#C463C7', '#9B48A1', '#73317A', '#4D2154',
62 | "black"
63 | ],
64 | avaliableModels: [{
65 | name: 'apple',
66 | value: '苹果',
67 | checked: true
68 | },
69 | {
70 | name: 'flower',
71 | value: '花朵',
72 | checked: false
73 | },
74 | {
75 | name: 'butterfly',
76 | value: '蝴蝶',
77 | checked: false
78 | },
79 | {
80 | name: 'sun',
81 | value: '太阳',
82 | checked: false
83 | },
84 | ],
85 | },
86 |
87 | /**
88 | * 生命周期函数--监听页面加载
89 | */
90 | onLoad: function (options) {
91 | wx.showShareMenu({
92 | withShareTicket: true
93 | });
94 |
95 | plugin.configPlugin({
96 | // polyfill fetch function
97 | fetchFunc: fetchWechat.fetchFunc(),
98 | // inject tfjs runtime
99 | tf,
100 | // inject backend
101 | webgl,
102 | // provide webgl canvas
103 | canvas: wx.createOffscreenCanvas()
104 | });
105 | },
106 |
107 | /**
108 | * 生命周期函数--监听页面初次渲染完成
109 | */
110 | onReady: function () {
111 | this.context = wx.createCanvasContext("firstCanvas");
112 | this.init();
113 | this.fillBackground(this.context);
114 | this.draw();
115 |
116 | predictStroke = [];
117 | wx.showLoading({
118 | title: '加载模型...',
119 | mask: true,
120 | });
121 | autoPainter.loadModels('apple').then((res) => {
122 | wx.hideLoading();
123 | if (res) {
124 | this.setData({
125 | status: '给我一个起始笔画吧,' + (Math.random() > 0.5 ? '不要太长哦!(⊙ᗜ⊙)' : '短一些会更好!(⊙ᗜ⊙)')
126 | });
127 | this.setCurrentColor(this.data.avaliableColors[Math.floor((Math.random() * this.data.avaliableColors.length))]);
128 | wx.showToast({
129 | title: '加载完成',
130 | })
131 | } else {
132 | this.setData({
133 | status: '模型加载超时(´□`川)'
134 | });
135 | wx.showModal({
136 | title: '加载失败',
137 | content: '选择其他模型试试吧',
138 | showCancel: false,
139 | });
140 | }
141 | });
142 | },
143 |
144 | /**
145 | * 生命周期函数--监听页面显示
146 | */
147 | onShow: function () {},
148 |
149 | /**
150 | * 生命周期函数--监听页面隐藏
151 | */
152 | onHide: function () {
153 |
154 | },
155 |
156 | /**
157 | * 生命周期函数--监听页面卸载
158 | */
159 | onUnload: function () {
160 |
161 | },
162 |
163 | /**
164 | * 页面相关事件处理函数--监听用户下拉动作
165 | */
166 | onPullDownRefresh: function () {
167 |
168 | },
169 |
170 | /**
171 | * 页面上拉触底事件的处理函数
172 | */
173 | onReachBottom: function () {
174 |
175 | },
176 |
177 | /**
178 | * 用户点击右上角分享
179 | */
180 | onShareAppMessage: function (res) {
181 |
182 | },
183 |
184 | /*--------------------- UI事件 --------------------------------------------------- */
185 | // 绘制开始 手指开始按到屏幕上
186 | touchStart: function (e) {
187 | if (predicting) {
188 | return;
189 | }
190 |
191 | this.lineBegin(e.touches[0].x, e.touches[0].y);
192 | this.recordPredictStroke(e.touches[0].x, e.touches[0].y);
193 | this.draw(true);
194 | curDrawArr.push({
195 | x: e.touches[0].x,
196 | y: e.touches[0].y
197 | });
198 | },
199 |
200 | // 绘制中 手指在屏幕上移动
201 | touchMove: function (e) {
202 | if (predicting) {
203 | return;
204 | }
205 |
206 | if (begin) {
207 | this.lineAddPoint(e.touches[0].x, e.touches[0].y);
208 | this.recordPredictStroke(e.touches[0].x, e.touches[0].y);
209 | this.draw(true);
210 | curDrawArr.push({
211 | x: e.touches[0].x,
212 | y: e.touches[0].y
213 | });
214 | }
215 | },
216 |
217 | // 绘制结束 手指抬起
218 | touchEnd: function () {
219 | if (predicting) {
220 | return;
221 | }
222 |
223 | drawInfos.push({
224 | drawArr: curDrawArr,
225 | color: this.data.currentColor,
226 | lineWidth: this.data.lineWidthArr[this.data.curWidthIndex],
227 | });
228 |
229 | // 后续笔画的起始坐标
230 | beginPosition = [curDrawArr[curDrawArr.length - 1]['x'], curDrawArr[curDrawArr.length - 1]['y']];
231 |
232 | curDrawArr = [];
233 | this.lineEnd();
234 |
235 | this.setData({
236 | status: '想象中[(--)]zzz'
237 | });
238 |
239 | if (predictStroke.length <= 1) {
240 | this.setData({
241 | status: '笔画太少了(╥╯^╰╥)'
242 | });
243 | return;
244 | }
245 |
246 | wx.showLoading({
247 | title: Math.random() > 0.5 ? '想象中' : '冥思中',
248 | mask: true
249 | })
250 | predicting = true; // 开始预测与绘制
251 | autoPainter.generate(predictStroke).then(followStroke => {
252 | wx.hideLoading();
253 | if (followStroke) {
254 | console.log('The followStroke length: ' + followStroke.length);
255 | this.setData({
256 | status: '绘制中(∪。∪)。。。zzz'
257 | });
258 | this.drawFollowStroke(followStroke, beginPosition); // 绘制后续笔画
259 | predictStroke = [];
260 | lastCoord = null;
261 | } else {
262 | wx.hideLoading();
263 | this.setData({
264 | status: '我没想出来(╥╯^╰╥)'
265 | });
266 | predicting = false;
267 | }
268 | });
269 | },
270 |
271 | // 点击图像大小
272 | clickChangeSize: function (e) {
273 | let index = e.currentTarget.dataset.index;
274 | this.setData({
275 | curSizeIndex: index
276 | });
277 | },
278 |
279 | // 点击设置线条宽度
280 | clickChangeWidth: function (e) {
281 | let index = e.currentTarget.dataset.index;
282 | this.setLineWidthByIndex(index);
283 | },
284 |
285 | // 点击设置线条颜色
286 | clickChangeColor: function (e) {
287 | let color = e.currentTarget.dataset.color;
288 | this.setCurrentColor(color);
289 | },
290 |
291 | // 点击切换到擦除
292 | clickErase: function () {
293 | this.setCurrentColor(bgColor);
294 | },
295 |
296 | // 点击清空canvas
297 | clickClearAll: function () {
298 | this.fillBackground(this.context);
299 | this.draw();
300 | drawInfos = [];
301 | this.init();
302 | predicting = false;
303 | },
304 |
305 | // 点击撤销上一步
306 | clickFallback: function () {
307 | if (drawInfos.length >= 1) {
308 | drawInfos.pop();
309 | }
310 | this.reDraw();
311 | },
312 |
313 | // 点击重绘canvas
314 | clickReDraw: function () {
315 | this.reDraw();
316 | },
317 |
318 | // 保存canvas图像到本地缓存
319 | clickStore: function () {
320 | let that = this;
321 | this.store("firstCanvas", res => {
322 | wx.saveImageToPhotosAlbum({
323 | filePath: res,
324 | })
325 | });
326 | },
327 |
328 | // 点击分享
329 | // 现在的功能是拼接一个分享出去的图像,然后保存到本地相册
330 | clickShare: function () {
331 | let that = this;
332 | // 在用户看不到的地方显示隐藏的canvas
333 | this.setData({
334 | hidden: false
335 | });
336 | // 截图的时候屏蔽用户操作
337 | wx.showLoading({
338 | title: '请稍等',
339 | mask: true
340 | })
341 | // 300毫秒后恢复正常
342 | setTimeout(() => {
343 | wx.hideLoading();
344 | that.setData({
345 | hidden: true
346 | });
347 | }, 300);
348 |
349 | // 截图用户绘制的canvaas
350 | this.store("firstCanvas", filePath => {
351 | // 生成海报并分享
352 |
353 | that.sharePost(filePath);
354 |
355 | });
356 | },
357 |
358 | modelChange: async function (e) {
359 | this.setData({
360 | status: '正在努力加载模型ᕙ༼ ͝°益° ༽ᕗ'
361 | });
362 |
363 | console.log(e.detail.value);
364 | wx.showLoading({
365 | title: '加载模型...',
366 | mask: true,
367 | });
368 | await autoPainter.loadModels(e.detail.value).then((res) => {
369 | wx.hideLoading();
370 | if (res) {
371 | this.setData({
372 | status: '给我一个起始笔画吧,' + (Math.random() > 0.5 ? '不要太长哦!(⊙ᗜ⊙)' : '短一些会更好!(⊙ᗜ⊙)')
373 | });
374 | this.setCurrentColor(this.data.avaliableColors[Math.floor((Math.random() * this.data.avaliableColors.length))]);
375 | wx.showToast({
376 | title: '加载完成',
377 | });
378 | } else {
379 | this.setData({
380 | status: '模型加载超时(´□`川)'
381 | });
382 | wx.showModal({
383 | title: '加载失败',
384 | content: '选择其他模型试试吧',
385 | showCancel: false,
386 | });
387 | }
388 | });
389 | },
390 |
391 | /*---------------------end of 界面绑定的函数------------------------------------------ */
392 |
393 | // 初始化
394 | init: function () {
395 | this.context.setLineCap('round') // 让线条圆润
396 | this.context.strokeStyle = this.data.currentColor;
397 | this.context.setLineWidth(this.data.lineWidthArr[this.data.curWidthIndex]);
398 | this.setData({
399 | currentColor: this.data.currentColor,
400 | curWidthIndex: this.data.curWidthIndex
401 | });
402 | },
403 |
404 | // 设置线条颜色
405 | setCurrentColor: function (color) {
406 | this.data.currentColor = color;
407 | this.context.strokeStyle = color;
408 | this.setData({
409 | currentColor: color
410 | });
411 | },
412 |
413 | // 设置线条宽度
414 | setLineWidthByIndex: function (index) {
415 | let width = this.data.lineWidthArr[index];
416 | this.context.setLineWidth(width);
417 | this.setData({
418 | curWidthIndex: index
419 | });
420 | },
421 |
422 | // 开始绘制线条
423 | lineBegin: function (x, y) {
424 | begin = true;
425 | this.context.beginPath()
426 | startX = x;
427 | startY = y;
428 | this.context.moveTo(startX, startY)
429 | this.lineAddPoint(x, y);
430 | },
431 |
432 | // 绘制线条中间添加点
433 | lineAddPoint: function (x, y) {
434 | this.context.moveTo(startX, startY)
435 | this.context.lineTo(x, y)
436 | this.context.stroke();
437 | startX = x;
438 | startY = y;
439 | },
440 |
441 | // 绘制线条结束
442 | lineEnd: function () {
443 | this.context.closePath();
444 | begin = false;
445 | },
446 |
447 | // 根据保存的绘制信息重新绘制
448 | reDraw: function () {
449 | this.init();
450 | this.fillBackground(this.context);
451 | // this.draw(false);
452 | for (var i = 0; i < drawInfos.length; i++) {
453 | this.context.strokeStyle = drawInfos[i].color;
454 | this.context.setLineWidth(drawInfos[i].lineWidth);
455 | let drawArr = drawInfos[i].drawArr;
456 | this.lineBegin(drawArr[0].x, drawArr[0].y)
457 | for (var j = 1; j < drawArr.length; j++) {
458 | this.lineAddPoint(drawArr[j].x, drawArr[j].y);
459 | // this.draw(true);
460 | }
461 |
462 | this.lineEnd();
463 | }
464 |
465 | this.draw();
466 | },
467 |
468 | // 将canvas导出为临时图像文件
469 | // canvasId: 要导出的canvas的id
470 | // cb: 回调函数
471 | store: function (canvasId, cb) {
472 | wx.canvasToTempFilePath({
473 | destWidth: 400,
474 | destHeight: 300,
475 | canvasId: canvasId,
476 | success: function (res) {
477 | typeof (cb) == 'function' && cb(res.tempFilePath);
478 | },
479 | fail: function (res) {
480 | console.log("store fail");
481 | console.log(res);
482 | }
483 | })
484 | },
485 |
486 | // 绘制canvas
487 | // isReverse: 是否保留之前的像素
488 | draw: function (isReverse = false, cb) {
489 | this.context.draw(isReverse, () => {
490 | if (cb && typeof (cb) == "function") {
491 | cb();
492 | }
493 | });
494 | },
495 |
496 |
497 | // canvas上下文设置背景为白色
498 | fillBackground: function (context) {
499 | context.setFillStyle(bgColor);
500 | context.fillRect(0, 0, 500, 500); //TODO context的宽和高待定
501 | context.fill();
502 | },
503 |
504 |
505 | // 预览
506 | pageView: function () {
507 | wx.canvasToTempFilePath({
508 | canvasId: 'firstCanvas',
509 | success: function (res) {
510 | wx.previewImage({
511 | current: res.tempFilePath,
512 | urls: [res.tempFilePath],
513 | success: function (res) {
514 |
515 | },
516 | fail: function (res) {
517 |
518 | },
519 | });
520 | },
521 | fail: function (res) {
522 | wx.showToast({
523 | title: '保存失败',
524 | icon: 'none'
525 | })
526 | }
527 | }, this)
528 | },
529 |
530 | // 分享海报
531 | sharePost: function (filePath) {
532 | let url = `/pages/share/share?imgUrl=${filePath}&levelId=${this.data.levelId}`;
533 | wx.navigateTo({
534 | url: url
535 | })
536 | },
537 |
538 | // 记录笔画偏移量数组,用于预测后续笔画
539 | recordPredictStroke: function (x, y) {
540 | var posX = x;
541 | var posY = y;
542 | if (lastCoord !== null) {
543 | // Calc the delta.
544 | let xDelta = posX - lastCoord[0];
545 | let yDelta = lastCoord[1] - posY; // Reverse the y coordinate.
546 | // Normalization.
547 | xDelta = xDelta / DRAW_SIZE[this.data.curSizeIndex][0];
548 | yDelta = yDelta / DRAW_SIZE[this.data.curSizeIndex][1];
549 | if (predictStroke.length > 0) {
550 | if (xDelta === 0.0 && predictStroke[predictStroke.length - 1][0] === 0.0) {
551 | // Merge if only move in y axis.
552 | predictStroke[predictStroke.length - 1][1] += yDelta;
553 | lastCoord = [posX, posY];
554 | return;
555 | }
556 | if (yDelta === 0.0 && predictStroke[predictStroke.length - 1][1] === 0.0) {
557 | // Merge if only move in x axis.
558 | predictStroke[predictStroke.length - 1][0] += xDelta;
559 | lastCoord = [posX, posY];
560 | return;
561 | }
562 | }
563 | // Ignore < DRAW_PREC.
564 | if (Math.abs(xDelta) >= DRAW_PREC || Math.abs(yDelta) >= DRAW_PREC) {
565 | predictStroke.push([xDelta, yDelta, 0.0, 0.0]);
566 | lastCoord = [posX, posY];
567 | }
568 | // console.log(predictStroke)
569 | } else {
570 | lastCoord = [posX, posY];
571 | }
572 | },
573 |
574 | // 绘制预测的后续笔画
575 | drawFollowStroke: async function (inks, beginPos) {
576 | console.log('drawFollowStroke');
577 | // 1, generate coords by deltas.
578 | let inkCoords = [
579 | [beginPos[0] / DRAW_SIZE[this.data.curSizeIndex][0], beginPos[1] / DRAW_SIZE[this.data.curSizeIndex][1], 0, 0]
580 | ];
581 | for (let ink in inks) {
582 | let posX = inkCoords[ink][0] + inks[ink][0];
583 | let posY = inkCoords[ink][1] - inks[ink][1];
584 | let endFlag = inks[ink][2];
585 | let completeFlag = inks[ink][3];
586 | inkCoords.push([posX, posY, endFlag, completeFlag]);
587 | }
588 | // 2, zoom in to DRAW_SIZE scale.
589 | for (let ink in inkCoords) {
590 | inkCoords[ink][0] *= DRAW_SIZE[this.data.curSizeIndex][0];
591 | inkCoords[ink][1] *= DRAW_SIZE[this.data.curSizeIndex][1];
592 | }
593 |
594 | // console.log(inkCoords);
595 | // 3. Draw every stroke.
596 | let stroke = [];
597 | for (let ink in inkCoords) {
598 | // Check if is complete ink.
599 | if (inkCoords[ink][3] > 0.5) {
600 | await this.drawStroke(stroke);
601 | stroke = [];
602 | predicting = false; // 预测与绘制完毕
603 | this.setData({
604 | status: '我画完了(⊙ᗜ⊙)' + (Math.random() > 0.5 ? '不满意的话尝试调整或缩短一下起始笔画吧' : '')
605 | });
606 | return;
607 | }
608 | // Check if is stroke end ink.
609 | if (inkCoords[ink][2] > 0.5) {
610 | // It's the stroke end ink, draw current stroke.
611 | stroke.push([inkCoords[ink][0], inkCoords[ink][1]]);
612 | await this.drawStroke(stroke);
613 | stroke = [];
614 | } else {
615 | // It's one point in stroke, add into current stroke.
616 | stroke.push([inkCoords[ink][0], inkCoords[ink][1]]);
617 | }
618 | }
619 | if (stroke.length !== 0) {
620 | // There has been left inks.
621 | await this.drawStroke(stroke);
622 | }
623 | predicting = false; // 预测与绘制完毕
624 | this.setData({
625 | status: '我画完了(⊙ᗜ⊙),不满意的话尝试调整或缩短一下起始笔画吧'
626 | });
627 | },
628 |
629 | // 绘制一笔
630 | drawStroke: async function (stroke) {
631 | // 睡眠
632 | let sleep = function (time) {
633 | let systemInfo = wx.getSystemInfoSync();
634 | if (systemInfo.platform == 'android') {
635 | // android sleep will very slow.
636 | return;
637 | }
638 | return new Promise((resolve) => setTimeout(resolve, time));
639 | }
640 | // 欧氏距离
641 | let eucDistance = function (x0, x1, y0, y1) {
642 | return ((x1 - x0) ** 2 + (y1 - y0) ** 2) ** 0.5;
643 | }
644 |
645 | // 随机颜色
646 | this.setCurrentColor(this.data.avaliableColors[Math.floor((Math.random() * this.data.avaliableColors.length))]);
647 |
648 | if (stroke.length === 1) {
649 | console.log("Only have one ink.");
650 | this.lineAddPoint(stroke[0][0], stroke[0][1]);
651 | this.draw(true);
652 | await sleep(10);
653 | return;
654 | }
655 |
656 | for (let ink in stroke) {
657 | if (ink == 0) {
658 | continue;
659 | }
660 | this.lineAddPoint(stroke[ink][0], stroke[ink][1]);
661 | this.draw(true);
662 |
663 | // If euclidean distance of two ink < 3, add path later and don't delay.
664 | // console.log(eucDistance(stroke[ink - 1][0], stroke[ink][0], stroke[ink - 1][1], stroke[ink][1]));
665 | if (eucDistance(stroke[ink - 1][0], stroke[ink][0], stroke[ink - 1][1], stroke[ink][1]) < 3) {
666 | console.log("If euclidean distance of two ink < 3, add path later and don't delay.");
667 | } else {
668 | await sleep(10);
669 | }
670 | }
671 | }
672 | })
--------------------------------------------------------------------------------
/miniprogram_npm/regenerator-runtime/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["runtime.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nvar runtime = (function (exports) {\n \n\n var Op = Object.prototype;\n var hasOwn = Op.hasOwnProperty;\n var undefined; // More compressible than void 0.\n var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n function wrap(innerFn, outerFn, self, tryLocsList) {\n // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n var generator = Object.create(protoGenerator.prototype);\n var context = new Context(tryLocsList || []);\n\n // The ._invoke method unifies the implementations of the .next,\n // .throw, and .return methods.\n generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n return generator;\n }\n exports.wrap = wrap;\n\n // Try/catch helper to minimize deoptimizations. Returns a completion\n // record like context.tryEntries[i].completion. This interface could\n // have been (and was previously) designed to take a closure to be\n // invoked without arguments, but in all the cases we care about we\n // already have an existing method we want to call, so there's no need\n // to create a new function object. We can even get away with assuming\n // the method takes exactly one argument, since that happens to be true\n // in every case, so we don't have to touch the arguments object. The\n // only additional allocation required is the completion record, which\n // has a stable shape and so hopefully should be cheap to allocate.\n function tryCatch(fn, obj, arg) {\n try {\n return { type: \"normal\", arg: fn.call(obj, arg) };\n } catch (err) {\n return { type: \"throw\", arg: err };\n }\n }\n\n var GenStateSuspendedStart = \"suspendedStart\";\n var GenStateSuspendedYield = \"suspendedYield\";\n var GenStateExecuting = \"executing\";\n var GenStateCompleted = \"completed\";\n\n // Returning this object from the innerFn has the same effect as\n // breaking out of the dispatch switch statement.\n var ContinueSentinel = {};\n\n // Dummy constructor functions that we use as the .constructor and\n // .constructor.prototype properties for functions that return Generator\n // objects. For full spec compliance, you may wish to configure your\n // minifier not to mangle the names of these two functions.\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n\n // This is a polyfill for %IteratorPrototype% for environments that\n // don't natively support it.\n var IteratorPrototype = {};\n IteratorPrototype[iteratorSymbol] = function () {\n return this;\n };\n\n var getProto = Object.getPrototypeOf;\n var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n if (NativeIteratorPrototype &&\n NativeIteratorPrototype !== Op &&\n hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n // This environment has a native %IteratorPrototype%; use it instead\n // of the polyfill.\n IteratorPrototype = NativeIteratorPrototype;\n }\n\n var Gp = GeneratorFunctionPrototype.prototype =\n Generator.prototype = Object.create(IteratorPrototype);\n GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;\n GeneratorFunctionPrototype.constructor = GeneratorFunction;\n GeneratorFunctionPrototype[toStringTagSymbol] =\n GeneratorFunction.displayName = \"GeneratorFunction\";\n\n // Helper for defining the .next, .throw, and .return methods of the\n // Iterator interface in terms of a single ._invoke method.\n function defineIteratorMethods(prototype) {\n [\"next\", \"throw\", \"return\"].forEach(function(method) {\n prototype[method] = function(arg) {\n return this._invoke(method, arg);\n };\n });\n }\n\n exports.isGeneratorFunction = function(genFun) {\n var ctor = typeof genFun === \"function\" && genFun.constructor;\n return ctor\n ? ctor === GeneratorFunction ||\n // For the native GeneratorFunction constructor, the best we can\n // do is to check its .name property.\n (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n : false;\n };\n\n exports.mark = function(genFun) {\n if (Object.setPrototypeOf) {\n Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n } else {\n genFun.__proto__ = GeneratorFunctionPrototype;\n if (!(toStringTagSymbol in genFun)) {\n genFun[toStringTagSymbol] = \"GeneratorFunction\";\n }\n }\n genFun.prototype = Object.create(Gp);\n return genFun;\n };\n\n // Within the body of any async function, `await x` is transformed to\n // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n // meant to be awaited.\n exports.awrap = function(arg) {\n return { __await: arg };\n };\n\n function AsyncIterator(generator, PromiseImpl) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (record.type === \"throw\") {\n reject(record.arg);\n } else {\n var result = record.arg;\n var value = result.value;\n if (value &&\n typeof value === \"object\" &&\n hasOwn.call(value, \"__await\")) {\n return PromiseImpl.resolve(value.__await).then(function(value) {\n invoke(\"next\", value, resolve, reject);\n }, function(err) {\n invoke(\"throw\", err, resolve, reject);\n });\n }\n\n return PromiseImpl.resolve(value).then(function(unwrapped) {\n // When a yielded Promise is resolved, its final value becomes\n // the .value of the Promise<{value,done}> result for the\n // current iteration.\n result.value = unwrapped;\n resolve(result);\n }, function(error) {\n // If a rejected Promise was yielded, throw the rejection back\n // into the async generator function so it can be handled there.\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new PromiseImpl(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n AsyncIterator.prototype[asyncIteratorSymbol] = function () {\n return this;\n };\n exports.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {\n if (PromiseImpl === void 0) PromiseImpl = Promise;\n\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList),\n PromiseImpl\n );\n\n return exports.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\n var delegate = context.delegate;\n if (delegate) {\n var delegateResult = maybeInvokeDelegate(delegate, context);\n if (delegateResult) {\n if (delegateResult === ContinueSentinel) continue;\n return delegateResult;\n }\n }\n\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n // Note: [\"return\"] must be used for ES3 parsing compatibility.\n if (delegate.iterator[\"return\"]) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n Gp[toStringTagSymbol] = \"Generator\";\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n Gp[iteratorSymbol] = function() {\n return this;\n };\n\n Gp.toString = function() {\n return \"[object Generator]\";\n };\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n exports.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n exports.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n\n // Regardless of whether this script is executing as a CommonJS module\n // or not, return the runtime object so that we can declare the variable\n // regeneratorRuntime in the outer scope, which allows this module to be\n // injected easily by `bin/regenerator --include-runtime script.js`.\n return exports;\n\n}(\n // If this script is executing as a CommonJS module, use module.exports\n // as the regeneratorRuntime namespace. Otherwise create a new empty\n // object. Either way, the resulting object will be used to initialize\n // the regeneratorRuntime variable at the top of this file.\n typeof module === \"object\" ? module.exports : {}\n));\n\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n // This module should not be running in strict mode, so the above\n // assignment should always work unless something is misconfigured. Just\n // in case runtime.js accidentally runs in strict mode, we can escape\n // strict mode using a global Function call. This could conceivably fail\n // if a Content Security Policy forbids using Function, but in that case\n // the proper solution is to fix the accidental strict mode problem. If\n // you've misconfigured your bundler to force strict mode and applied a\n // CSP to forbid Function, and you're not willing to fix either of those\n // problems, please detail your unique predicament in a GitHub issue.\n Function(\"r\", \"regeneratorRuntime = r\")(runtime);\n}\n"]}
--------------------------------------------------------------------------------
/miniprogram_npm/regenerator-runtime/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (function() {
2 | var __MODS__ = {};
3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
7 | __DEFINE__(1593391889375, function(require, module, exports) {
8 | /**
9 | * Copyright (c) 2014-present, Facebook, Inc.
10 | *
11 | * This source code is licensed under the MIT license found in the
12 | * LICENSE file in the root directory of this source tree.
13 | */
14 |
15 | var runtime = (function (exports) {
16 |
17 |
18 | var Op = Object.prototype;
19 | var hasOwn = Op.hasOwnProperty;
20 | var undefined; // More compressible than void 0.
21 | var $Symbol = typeof Symbol === "function" ? Symbol : {};
22 | var iteratorSymbol = $Symbol.iterator || "@@iterator";
23 | var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
24 | var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
25 |
26 | function wrap(innerFn, outerFn, self, tryLocsList) {
27 | // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
28 | var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
29 | var generator = Object.create(protoGenerator.prototype);
30 | var context = new Context(tryLocsList || []);
31 |
32 | // The ._invoke method unifies the implementations of the .next,
33 | // .throw, and .return methods.
34 | generator._invoke = makeInvokeMethod(innerFn, self, context);
35 |
36 | return generator;
37 | }
38 | exports.wrap = wrap;
39 |
40 | // Try/catch helper to minimize deoptimizations. Returns a completion
41 | // record like context.tryEntries[i].completion. This interface could
42 | // have been (and was previously) designed to take a closure to be
43 | // invoked without arguments, but in all the cases we care about we
44 | // already have an existing method we want to call, so there's no need
45 | // to create a new function object. We can even get away with assuming
46 | // the method takes exactly one argument, since that happens to be true
47 | // in every case, so we don't have to touch the arguments object. The
48 | // only additional allocation required is the completion record, which
49 | // has a stable shape and so hopefully should be cheap to allocate.
50 | function tryCatch(fn, obj, arg) {
51 | try {
52 | return { type: "normal", arg: fn.call(obj, arg) };
53 | } catch (err) {
54 | return { type: "throw", arg: err };
55 | }
56 | }
57 |
58 | var GenStateSuspendedStart = "suspendedStart";
59 | var GenStateSuspendedYield = "suspendedYield";
60 | var GenStateExecuting = "executing";
61 | var GenStateCompleted = "completed";
62 |
63 | // Returning this object from the innerFn has the same effect as
64 | // breaking out of the dispatch switch statement.
65 | var ContinueSentinel = {};
66 |
67 | // Dummy constructor functions that we use as the .constructor and
68 | // .constructor.prototype properties for functions that return Generator
69 | // objects. For full spec compliance, you may wish to configure your
70 | // minifier not to mangle the names of these two functions.
71 | function Generator() {}
72 | function GeneratorFunction() {}
73 | function GeneratorFunctionPrototype() {}
74 |
75 | // This is a polyfill for %IteratorPrototype% for environments that
76 | // don't natively support it.
77 | var IteratorPrototype = {};
78 | IteratorPrototype[iteratorSymbol] = function () {
79 | return this;
80 | };
81 |
82 | var getProto = Object.getPrototypeOf;
83 | var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
84 | if (NativeIteratorPrototype &&
85 | NativeIteratorPrototype !== Op &&
86 | hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
87 | // This environment has a native %IteratorPrototype%; use it instead
88 | // of the polyfill.
89 | IteratorPrototype = NativeIteratorPrototype;
90 | }
91 |
92 | var Gp = GeneratorFunctionPrototype.prototype =
93 | Generator.prototype = Object.create(IteratorPrototype);
94 | GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
95 | GeneratorFunctionPrototype.constructor = GeneratorFunction;
96 | GeneratorFunctionPrototype[toStringTagSymbol] =
97 | GeneratorFunction.displayName = "GeneratorFunction";
98 |
99 | // Helper for defining the .next, .throw, and .return methods of the
100 | // Iterator interface in terms of a single ._invoke method.
101 | function defineIteratorMethods(prototype) {
102 | ["next", "throw", "return"].forEach(function(method) {
103 | prototype[method] = function(arg) {
104 | return this._invoke(method, arg);
105 | };
106 | });
107 | }
108 |
109 | exports.isGeneratorFunction = function(genFun) {
110 | var ctor = typeof genFun === "function" && genFun.constructor;
111 | return ctor
112 | ? ctor === GeneratorFunction ||
113 | // For the native GeneratorFunction constructor, the best we can
114 | // do is to check its .name property.
115 | (ctor.displayName || ctor.name) === "GeneratorFunction"
116 | : false;
117 | };
118 |
119 | exports.mark = function(genFun) {
120 | if (Object.setPrototypeOf) {
121 | Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
122 | } else {
123 | genFun.__proto__ = GeneratorFunctionPrototype;
124 | if (!(toStringTagSymbol in genFun)) {
125 | genFun[toStringTagSymbol] = "GeneratorFunction";
126 | }
127 | }
128 | genFun.prototype = Object.create(Gp);
129 | return genFun;
130 | };
131 |
132 | // Within the body of any async function, `await x` is transformed to
133 | // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
134 | // `hasOwn.call(value, "__await")` to determine if the yielded value is
135 | // meant to be awaited.
136 | exports.awrap = function(arg) {
137 | return { __await: arg };
138 | };
139 |
140 | function AsyncIterator(generator, PromiseImpl) {
141 | function invoke(method, arg, resolve, reject) {
142 | var record = tryCatch(generator[method], generator, arg);
143 | if (record.type === "throw") {
144 | reject(record.arg);
145 | } else {
146 | var result = record.arg;
147 | var value = result.value;
148 | if (value &&
149 | typeof value === "object" &&
150 | hasOwn.call(value, "__await")) {
151 | return PromiseImpl.resolve(value.__await).then(function(value) {
152 | invoke("next", value, resolve, reject);
153 | }, function(err) {
154 | invoke("throw", err, resolve, reject);
155 | });
156 | }
157 |
158 | return PromiseImpl.resolve(value).then(function(unwrapped) {
159 | // When a yielded Promise is resolved, its final value becomes
160 | // the .value of the Promise<{value,done}> result for the
161 | // current iteration.
162 | result.value = unwrapped;
163 | resolve(result);
164 | }, function(error) {
165 | // If a rejected Promise was yielded, throw the rejection back
166 | // into the async generator function so it can be handled there.
167 | return invoke("throw", error, resolve, reject);
168 | });
169 | }
170 | }
171 |
172 | var previousPromise;
173 |
174 | function enqueue(method, arg) {
175 | function callInvokeWithMethodAndArg() {
176 | return new PromiseImpl(function(resolve, reject) {
177 | invoke(method, arg, resolve, reject);
178 | });
179 | }
180 |
181 | return previousPromise =
182 | // If enqueue has been called before, then we want to wait until
183 | // all previous Promises have been resolved before calling invoke,
184 | // so that results are always delivered in the correct order. If
185 | // enqueue has not been called before, then it is important to
186 | // call invoke immediately, without waiting on a callback to fire,
187 | // so that the async generator function has the opportunity to do
188 | // any necessary setup in a predictable way. This predictability
189 | // is why the Promise constructor synchronously invokes its
190 | // executor callback, and why async functions synchronously
191 | // execute code before the first await. Since we implement simple
192 | // async functions in terms of async generators, it is especially
193 | // important to get this right, even though it requires care.
194 | previousPromise ? previousPromise.then(
195 | callInvokeWithMethodAndArg,
196 | // Avoid propagating failures to Promises returned by later
197 | // invocations of the iterator.
198 | callInvokeWithMethodAndArg
199 | ) : callInvokeWithMethodAndArg();
200 | }
201 |
202 | // Define the unified helper method that is used to implement .next,
203 | // .throw, and .return (see defineIteratorMethods).
204 | this._invoke = enqueue;
205 | }
206 |
207 | defineIteratorMethods(AsyncIterator.prototype);
208 | AsyncIterator.prototype[asyncIteratorSymbol] = function () {
209 | return this;
210 | };
211 | exports.AsyncIterator = AsyncIterator;
212 |
213 | // Note that simple async functions are implemented on top of
214 | // AsyncIterator objects; they just return a Promise for the value of
215 | // the final result produced by the iterator.
216 | exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {
217 | if (PromiseImpl === void 0) PromiseImpl = Promise;
218 |
219 | var iter = new AsyncIterator(
220 | wrap(innerFn, outerFn, self, tryLocsList),
221 | PromiseImpl
222 | );
223 |
224 | return exports.isGeneratorFunction(outerFn)
225 | ? iter // If outerFn is a generator, return the full iterator.
226 | : iter.next().then(function(result) {
227 | return result.done ? result.value : iter.next();
228 | });
229 | };
230 |
231 | function makeInvokeMethod(innerFn, self, context) {
232 | var state = GenStateSuspendedStart;
233 |
234 | return function invoke(method, arg) {
235 | if (state === GenStateExecuting) {
236 | throw new Error("Generator is already running");
237 | }
238 |
239 | if (state === GenStateCompleted) {
240 | if (method === "throw") {
241 | throw arg;
242 | }
243 |
244 | // Be forgiving, per 25.3.3.3.3 of the spec:
245 | // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
246 | return doneResult();
247 | }
248 |
249 | context.method = method;
250 | context.arg = arg;
251 |
252 | while (true) {
253 | var delegate = context.delegate;
254 | if (delegate) {
255 | var delegateResult = maybeInvokeDelegate(delegate, context);
256 | if (delegateResult) {
257 | if (delegateResult === ContinueSentinel) continue;
258 | return delegateResult;
259 | }
260 | }
261 |
262 | if (context.method === "next") {
263 | // Setting context._sent for legacy support of Babel's
264 | // function.sent implementation.
265 | context.sent = context._sent = context.arg;
266 |
267 | } else if (context.method === "throw") {
268 | if (state === GenStateSuspendedStart) {
269 | state = GenStateCompleted;
270 | throw context.arg;
271 | }
272 |
273 | context.dispatchException(context.arg);
274 |
275 | } else if (context.method === "return") {
276 | context.abrupt("return", context.arg);
277 | }
278 |
279 | state = GenStateExecuting;
280 |
281 | var record = tryCatch(innerFn, self, context);
282 | if (record.type === "normal") {
283 | // If an exception is thrown from innerFn, we leave state ===
284 | // GenStateExecuting and loop back for another invocation.
285 | state = context.done
286 | ? GenStateCompleted
287 | : GenStateSuspendedYield;
288 |
289 | if (record.arg === ContinueSentinel) {
290 | continue;
291 | }
292 |
293 | return {
294 | value: record.arg,
295 | done: context.done
296 | };
297 |
298 | } else if (record.type === "throw") {
299 | state = GenStateCompleted;
300 | // Dispatch the exception by looping back around to the
301 | // context.dispatchException(context.arg) call above.
302 | context.method = "throw";
303 | context.arg = record.arg;
304 | }
305 | }
306 | };
307 | }
308 |
309 | // Call delegate.iterator[context.method](context.arg) and handle the
310 | // result, either by returning a { value, done } result from the
311 | // delegate iterator, or by modifying context.method and context.arg,
312 | // setting context.delegate to null, and returning the ContinueSentinel.
313 | function maybeInvokeDelegate(delegate, context) {
314 | var method = delegate.iterator[context.method];
315 | if (method === undefined) {
316 | // A .throw or .return when the delegate iterator has no .throw
317 | // method always terminates the yield* loop.
318 | context.delegate = null;
319 |
320 | if (context.method === "throw") {
321 | // Note: ["return"] must be used for ES3 parsing compatibility.
322 | if (delegate.iterator["return"]) {
323 | // If the delegate iterator has a return method, give it a
324 | // chance to clean up.
325 | context.method = "return";
326 | context.arg = undefined;
327 | maybeInvokeDelegate(delegate, context);
328 |
329 | if (context.method === "throw") {
330 | // If maybeInvokeDelegate(context) changed context.method from
331 | // "return" to "throw", let that override the TypeError below.
332 | return ContinueSentinel;
333 | }
334 | }
335 |
336 | context.method = "throw";
337 | context.arg = new TypeError(
338 | "The iterator does not provide a 'throw' method");
339 | }
340 |
341 | return ContinueSentinel;
342 | }
343 |
344 | var record = tryCatch(method, delegate.iterator, context.arg);
345 |
346 | if (record.type === "throw") {
347 | context.method = "throw";
348 | context.arg = record.arg;
349 | context.delegate = null;
350 | return ContinueSentinel;
351 | }
352 |
353 | var info = record.arg;
354 |
355 | if (! info) {
356 | context.method = "throw";
357 | context.arg = new TypeError("iterator result is not an object");
358 | context.delegate = null;
359 | return ContinueSentinel;
360 | }
361 |
362 | if (info.done) {
363 | // Assign the result of the finished delegate to the temporary
364 | // variable specified by delegate.resultName (see delegateYield).
365 | context[delegate.resultName] = info.value;
366 |
367 | // Resume execution at the desired location (see delegateYield).
368 | context.next = delegate.nextLoc;
369 |
370 | // If context.method was "throw" but the delegate handled the
371 | // exception, let the outer generator proceed normally. If
372 | // context.method was "next", forget context.arg since it has been
373 | // "consumed" by the delegate iterator. If context.method was
374 | // "return", allow the original .return call to continue in the
375 | // outer generator.
376 | if (context.method !== "return") {
377 | context.method = "next";
378 | context.arg = undefined;
379 | }
380 |
381 | } else {
382 | // Re-yield the result returned by the delegate method.
383 | return info;
384 | }
385 |
386 | // The delegate iterator is finished, so forget it and continue with
387 | // the outer generator.
388 | context.delegate = null;
389 | return ContinueSentinel;
390 | }
391 |
392 | // Define Generator.prototype.{next,throw,return} in terms of the
393 | // unified ._invoke helper method.
394 | defineIteratorMethods(Gp);
395 |
396 | Gp[toStringTagSymbol] = "Generator";
397 |
398 | // A Generator should always return itself as the iterator object when the
399 | // @@iterator function is called on it. Some browsers' implementations of the
400 | // iterator prototype chain incorrectly implement this, causing the Generator
401 | // object to not be returned from this call. This ensures that doesn't happen.
402 | // See https://github.com/facebook/regenerator/issues/274 for more details.
403 | Gp[iteratorSymbol] = function() {
404 | return this;
405 | };
406 |
407 | Gp.toString = function() {
408 | return "[object Generator]";
409 | };
410 |
411 | function pushTryEntry(locs) {
412 | var entry = { tryLoc: locs[0] };
413 |
414 | if (1 in locs) {
415 | entry.catchLoc = locs[1];
416 | }
417 |
418 | if (2 in locs) {
419 | entry.finallyLoc = locs[2];
420 | entry.afterLoc = locs[3];
421 | }
422 |
423 | this.tryEntries.push(entry);
424 | }
425 |
426 | function resetTryEntry(entry) {
427 | var record = entry.completion || {};
428 | record.type = "normal";
429 | delete record.arg;
430 | entry.completion = record;
431 | }
432 |
433 | function Context(tryLocsList) {
434 | // The root entry object (effectively a try statement without a catch
435 | // or a finally block) gives us a place to store values thrown from
436 | // locations where there is no enclosing try statement.
437 | this.tryEntries = [{ tryLoc: "root" }];
438 | tryLocsList.forEach(pushTryEntry, this);
439 | this.reset(true);
440 | }
441 |
442 | exports.keys = function(object) {
443 | var keys = [];
444 | for (var key in object) {
445 | keys.push(key);
446 | }
447 | keys.reverse();
448 |
449 | // Rather than returning an object with a next method, we keep
450 | // things simple and return the next function itself.
451 | return function next() {
452 | while (keys.length) {
453 | var key = keys.pop();
454 | if (key in object) {
455 | next.value = key;
456 | next.done = false;
457 | return next;
458 | }
459 | }
460 |
461 | // To avoid creating an additional object, we just hang the .value
462 | // and .done properties off the next function object itself. This
463 | // also ensures that the minifier will not anonymize the function.
464 | next.done = true;
465 | return next;
466 | };
467 | };
468 |
469 | function values(iterable) {
470 | if (iterable) {
471 | var iteratorMethod = iterable[iteratorSymbol];
472 | if (iteratorMethod) {
473 | return iteratorMethod.call(iterable);
474 | }
475 |
476 | if (typeof iterable.next === "function") {
477 | return iterable;
478 | }
479 |
480 | if (!isNaN(iterable.length)) {
481 | var i = -1, next = function next() {
482 | while (++i < iterable.length) {
483 | if (hasOwn.call(iterable, i)) {
484 | next.value = iterable[i];
485 | next.done = false;
486 | return next;
487 | }
488 | }
489 |
490 | next.value = undefined;
491 | next.done = true;
492 |
493 | return next;
494 | };
495 |
496 | return next.next = next;
497 | }
498 | }
499 |
500 | // Return an iterator with no values.
501 | return { next: doneResult };
502 | }
503 | exports.values = values;
504 |
505 | function doneResult() {
506 | return { value: undefined, done: true };
507 | }
508 |
509 | Context.prototype = {
510 | constructor: Context,
511 |
512 | reset: function(skipTempReset) {
513 | this.prev = 0;
514 | this.next = 0;
515 | // Resetting context._sent for legacy support of Babel's
516 | // function.sent implementation.
517 | this.sent = this._sent = undefined;
518 | this.done = false;
519 | this.delegate = null;
520 |
521 | this.method = "next";
522 | this.arg = undefined;
523 |
524 | this.tryEntries.forEach(resetTryEntry);
525 |
526 | if (!skipTempReset) {
527 | for (var name in this) {
528 | // Not sure about the optimal order of these conditions:
529 | if (name.charAt(0) === "t" &&
530 | hasOwn.call(this, name) &&
531 | !isNaN(+name.slice(1))) {
532 | this[name] = undefined;
533 | }
534 | }
535 | }
536 | },
537 |
538 | stop: function() {
539 | this.done = true;
540 |
541 | var rootEntry = this.tryEntries[0];
542 | var rootRecord = rootEntry.completion;
543 | if (rootRecord.type === "throw") {
544 | throw rootRecord.arg;
545 | }
546 |
547 | return this.rval;
548 | },
549 |
550 | dispatchException: function(exception) {
551 | if (this.done) {
552 | throw exception;
553 | }
554 |
555 | var context = this;
556 | function handle(loc, caught) {
557 | record.type = "throw";
558 | record.arg = exception;
559 | context.next = loc;
560 |
561 | if (caught) {
562 | // If the dispatched exception was caught by a catch block,
563 | // then let that catch block handle the exception normally.
564 | context.method = "next";
565 | context.arg = undefined;
566 | }
567 |
568 | return !! caught;
569 | }
570 |
571 | for (var i = this.tryEntries.length - 1; i >= 0; --i) {
572 | var entry = this.tryEntries[i];
573 | var record = entry.completion;
574 |
575 | if (entry.tryLoc === "root") {
576 | // Exception thrown outside of any try block that could handle
577 | // it, so set the completion value of the entire function to
578 | // throw the exception.
579 | return handle("end");
580 | }
581 |
582 | if (entry.tryLoc <= this.prev) {
583 | var hasCatch = hasOwn.call(entry, "catchLoc");
584 | var hasFinally = hasOwn.call(entry, "finallyLoc");
585 |
586 | if (hasCatch && hasFinally) {
587 | if (this.prev < entry.catchLoc) {
588 | return handle(entry.catchLoc, true);
589 | } else if (this.prev < entry.finallyLoc) {
590 | return handle(entry.finallyLoc);
591 | }
592 |
593 | } else if (hasCatch) {
594 | if (this.prev < entry.catchLoc) {
595 | return handle(entry.catchLoc, true);
596 | }
597 |
598 | } else if (hasFinally) {
599 | if (this.prev < entry.finallyLoc) {
600 | return handle(entry.finallyLoc);
601 | }
602 |
603 | } else {
604 | throw new Error("try statement without catch or finally");
605 | }
606 | }
607 | }
608 | },
609 |
610 | abrupt: function(type, arg) {
611 | for (var i = this.tryEntries.length - 1; i >= 0; --i) {
612 | var entry = this.tryEntries[i];
613 | if (entry.tryLoc <= this.prev &&
614 | hasOwn.call(entry, "finallyLoc") &&
615 | this.prev < entry.finallyLoc) {
616 | var finallyEntry = entry;
617 | break;
618 | }
619 | }
620 |
621 | if (finallyEntry &&
622 | (type === "break" ||
623 | type === "continue") &&
624 | finallyEntry.tryLoc <= arg &&
625 | arg <= finallyEntry.finallyLoc) {
626 | // Ignore the finally entry if control is not jumping to a
627 | // location outside the try/catch block.
628 | finallyEntry = null;
629 | }
630 |
631 | var record = finallyEntry ? finallyEntry.completion : {};
632 | record.type = type;
633 | record.arg = arg;
634 |
635 | if (finallyEntry) {
636 | this.method = "next";
637 | this.next = finallyEntry.finallyLoc;
638 | return ContinueSentinel;
639 | }
640 |
641 | return this.complete(record);
642 | },
643 |
644 | complete: function(record, afterLoc) {
645 | if (record.type === "throw") {
646 | throw record.arg;
647 | }
648 |
649 | if (record.type === "break" ||
650 | record.type === "continue") {
651 | this.next = record.arg;
652 | } else if (record.type === "return") {
653 | this.rval = this.arg = record.arg;
654 | this.method = "return";
655 | this.next = "end";
656 | } else if (record.type === "normal" && afterLoc) {
657 | this.next = afterLoc;
658 | }
659 |
660 | return ContinueSentinel;
661 | },
662 |
663 | finish: function(finallyLoc) {
664 | for (var i = this.tryEntries.length - 1; i >= 0; --i) {
665 | var entry = this.tryEntries[i];
666 | if (entry.finallyLoc === finallyLoc) {
667 | this.complete(entry.completion, entry.afterLoc);
668 | resetTryEntry(entry);
669 | return ContinueSentinel;
670 | }
671 | }
672 | },
673 |
674 | "catch": function(tryLoc) {
675 | for (var i = this.tryEntries.length - 1; i >= 0; --i) {
676 | var entry = this.tryEntries[i];
677 | if (entry.tryLoc === tryLoc) {
678 | var record = entry.completion;
679 | if (record.type === "throw") {
680 | var thrown = record.arg;
681 | resetTryEntry(entry);
682 | }
683 | return thrown;
684 | }
685 | }
686 |
687 | // The context.catch method must only be called with a location
688 | // argument that corresponds to a known catch block.
689 | throw new Error("illegal catch attempt");
690 | },
691 |
692 | delegateYield: function(iterable, resultName, nextLoc) {
693 | this.delegate = {
694 | iterator: values(iterable),
695 | resultName: resultName,
696 | nextLoc: nextLoc
697 | };
698 |
699 | if (this.method === "next") {
700 | // Deliberately forget the last sent value so that we don't
701 | // accidentally pass it on to the delegate.
702 | this.arg = undefined;
703 | }
704 |
705 | return ContinueSentinel;
706 | }
707 | };
708 |
709 | // Regardless of whether this script is executing as a CommonJS module
710 | // or not, return the runtime object so that we can declare the variable
711 | // regeneratorRuntime in the outer scope, which allows this module to be
712 | // injected easily by `bin/regenerator --include-runtime script.js`.
713 | return exports;
714 |
715 | }(
716 | // If this script is executing as a CommonJS module, use module.exports
717 | // as the regeneratorRuntime namespace. Otherwise create a new empty
718 | // object. Either way, the resulting object will be used to initialize
719 | // the regeneratorRuntime variable at the top of this file.
720 | typeof module === "object" ? module.exports : {}
721 | ));
722 |
723 | try {
724 | regeneratorRuntime = runtime;
725 | } catch (accidentalStrictMode) {
726 | // This module should not be running in strict mode, so the above
727 | // assignment should always work unless something is misconfigured. Just
728 | // in case runtime.js accidentally runs in strict mode, we can escape
729 | // strict mode using a global Function call. This could conceivably fail
730 | // if a Content Security Policy forbids using Function, but in that case
731 | // the proper solution is to fix the accidental strict mode problem. If
732 | // you've misconfigured your bundler to force strict mode and applied a
733 | // CSP to forbid Function, and you're not willing to fix either of those
734 | // problems, please detail your unique predicament in a GitHub issue.
735 | Function("r", "regeneratorRuntime = r")(runtime);
736 | }
737 |
738 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
739 | return __REQUIRE__(1593391889375);
740 | })()
741 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------