张延森 před 3 roky
revize
710e77d036

+ 12
- 0
.editorconfig Zobrazit soubor

@@ -0,0 +1,12 @@
1
+# http://editorconfig.org
2
+root = true
3
+
4
+[*]
5
+indent_style = space
6
+indent_size = 2
7
+charset = utf-8
8
+trim_trailing_whitespace = true
9
+insert_final_newline = true
10
+
11
+[*.md]
12
+trim_trailing_whitespace = false

+ 7
- 0
.eslintrc Zobrazit soubor

@@ -0,0 +1,7 @@
1
+{
2
+  "extends": ["taro/react"],
3
+  "rules": {
4
+    "react/jsx-uses-react": "off",
5
+    "react/react-in-jsx-scope": "off"
6
+  }
7
+}

+ 6
- 0
.gitignore Zobrazit soubor

@@ -0,0 +1,6 @@
1
+dist/
2
+deploy_versions/
3
+.temp/
4
+.rn_temp/
5
+node_modules/
6
+.DS_Store

+ 21
- 0
babel.config.js Zobrazit soubor

@@ -0,0 +1,21 @@
1
+// babel-preset-taro 更多选项和默认值:
2
+// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
3
+module.exports = {
4
+  presets: [
5
+    ['taro', {
6
+      framework: 'react',
7
+      ts: false
8
+    }]
9
+  ],
10
+  plugins: [
11
+    [
12
+      "import",
13
+      {
14
+        libraryName: "@antmjs/vantui",
15
+        libraryDirectory: "es",
16
+        style: (name) => `${name}/style/less`,
17
+      },
18
+      "@antmjs/vantui"
19
+    ]
20
+  ]
21
+}

+ 9
- 0
config/dev.js Zobrazit soubor

@@ -0,0 +1,9 @@
1
+module.exports = {
2
+  env: {
3
+    NODE_ENV: '"development"'
4
+  },
5
+  defineConstants: {
6
+  },
7
+  mini: {},
8
+  h5: {}
9
+}

+ 80
- 0
config/index.js Zobrazit soubor

@@ -0,0 +1,80 @@
1
+const path = require('path')
2
+
3
+const config = {
4
+  projectName: 'YunShake',
5
+  date: '2021-11-26',
6
+  designWidth: 750,
7
+  deviceRatio: {
8
+    640: 2.34 / 2,
9
+    750: 1,
10
+    828: 1.81 / 2
11
+  },
12
+  sourceRoot: 'src',
13
+  outputRoot: 'dist',
14
+  plugins: [],
15
+  defineConstants: {
16
+  },
17
+  alias: {
18
+    '@/': path.resolve(__dirname, '..', 'src/'),
19
+    '@/components': path.resolve(__dirname, '..', 'src/components'),
20
+    '@/utils': path.resolve(__dirname, '..', 'src/utils'),
21
+  },
22
+  copy: {
23
+    patterns: [
24
+    ],
25
+    options: {
26
+    }
27
+  },
28
+  framework: 'react',
29
+  mini: {
30
+    miniCssExtractPluginOption: {
31
+      ignoreOrder: true
32
+    },  
33
+    postcss: {
34
+      pxtransform: {
35
+        enable: true,
36
+        config: {
37
+
38
+        }
39
+      },
40
+      url: {
41
+        enable: true,
42
+        config: {
43
+          limit: 1024 // 设定转换尺寸上限
44
+        }
45
+      },
46
+      cssModules: {
47
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
48
+        config: {
49
+          namingPattern: 'module', // 转换模式,取值为 global/module
50
+          generateScopedName: '[name]__[local]___[hash:base64:5]'
51
+        }
52
+      }
53
+    }
54
+  },
55
+  h5: {
56
+    publicPath: '/',
57
+    staticDirectory: 'static',
58
+    postcss: {
59
+      autoprefixer: {
60
+        enable: true,
61
+        config: {
62
+        }
63
+      },
64
+      cssModules: {
65
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
66
+        config: {
67
+          namingPattern: 'module', // 转换模式,取值为 global/module
68
+          generateScopedName: '[name]__[local]___[hash:base64:5]'
69
+        }
70
+      }
71
+    }
72
+  }
73
+}
74
+
75
+module.exports = function (merge) {
76
+  if (process.env.NODE_ENV === 'development') {
77
+    return merge({}, config, require('./dev'))
78
+  }
79
+  return merge({}, config, require('./prod'))
80
+}

+ 18
- 0
config/prod.js Zobrazit soubor

@@ -0,0 +1,18 @@
1
+module.exports = {
2
+  env: {
3
+    NODE_ENV: '"production"'
4
+  },
5
+  defineConstants: {
6
+  },
7
+  mini: {},
8
+  h5: {
9
+    /**
10
+     * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。
11
+     * 参考代码如下:
12
+     * webpackChain (chain) {
13
+     *   chain.plugin('analyzer')
14
+     *     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
15
+     * }
16
+     */
17
+  }
18
+}

+ 14
- 0
jsconfig.json Zobrazit soubor

@@ -0,0 +1,14 @@
1
+// 本文件, 仅仅是为了支持 IDE 跳转, 与项目打包没有任何关系
2
+
3
+{
4
+  "compilerOptions": {
5
+    "jsx": "react",
6
+    "experimentalDecorators": true,
7
+    "baseUrl": ".",
8
+    "paths": {      
9
+      "@/assets/*": ["./src/assets/*"],
10
+      "@/components/*": ["./src/components/*"],
11
+      "@/utils/*": ["./src/utils/*"],
12
+    }
13
+  }
14
+}

+ 62
- 0
package.json Zobrazit soubor

@@ -0,0 +1,62 @@
1
+{
2
+  "name": "YunShake",
3
+  "version": "1.0.0",
4
+  "private": true,
5
+  "description": "",
6
+  "templateInfo": {
7
+    "name": "default",
8
+    "typescript": false,
9
+    "css": "less"
10
+  },
11
+  "scripts": {
12
+    "build:weapp": "taro build --type weapp",
13
+    "build:swan": "taro build --type swan",
14
+    "build:alipay": "taro build --type alipay",
15
+    "build:tt": "taro build --type tt",
16
+    "build:h5": "taro build --type h5",
17
+    "build:rn": "taro build --type rn",
18
+    "build:qq": "taro build --type qq",
19
+    "build:jd": "taro build --type jd",
20
+    "build:quickapp": "taro build --type quickapp",
21
+    "dev:weapp": "npm run build:weapp -- --watch",
22
+    "dev:swan": "npm run build:swan -- --watch",
23
+    "dev:alipay": "npm run build:alipay -- --watch",
24
+    "dev:tt": "npm run build:tt -- --watch",
25
+    "dev:h5": "npm run build:h5 -- --watch",
26
+    "dev:rn": "npm run build:rn -- --watch",
27
+    "dev:qq": "npm run build:qq -- --watch",
28
+    "dev:jd": "npm run build:jd -- --watch",
29
+    "dev:quickapp": "npm run build:quickapp -- --watch"
30
+  },
31
+  "browserslist": [
32
+    "last 3 versions",
33
+    "Android >= 4.1",
34
+    "ios >= 8"
35
+  ],
36
+  "author": "",
37
+  "dependencies": {
38
+    "@antmjs/vantui": "^1.2.5",
39
+    "@babel/runtime": "^7.7.7",
40
+    "@tarojs/components": "3.2.13",
41
+    "@tarojs/react": "3.2.13",
42
+    "@tarojs/runtime": "3.2.13",
43
+    "@tarojs/taro": "3.2.13",
44
+    "react": "^17.0.0",
45
+    "react-dom": "^17.0.0"
46
+  },
47
+  "devDependencies": {
48
+    "@babel/core": "^7.8.0",
49
+    "@tarojs/mini-runner": "3.2.13",
50
+    "@tarojs/webpack-runner": "3.2.13",
51
+    "@types/react": "^17.0.2",
52
+    "@types/webpack-env": "^1.13.6",
53
+    "babel-plugin-import": "^1.13.3",
54
+    "babel-preset-taro": "3.2.13",
55
+    "eslint": "^6.8.0",
56
+    "eslint-config-taro": "3.2.13",
57
+    "eslint-plugin-import": "^2.12.0",
58
+    "eslint-plugin-react": "^7.8.2",
59
+    "eslint-plugin-react-hooks": "^4.2.0",
60
+    "stylelint": "9.3.0"
61
+  }
62
+}

+ 13
- 0
project.config.json Zobrazit soubor

@@ -0,0 +1,13 @@
1
+{
2
+  "miniprogramRoot": "./dist",
3
+  "projectname": "YunShake",
4
+  "description": "",
5
+  "appid": "wxc96058d57e77f373",
6
+  "setting": {
7
+    "urlCheck": true,
8
+    "es6": false,
9
+    "postcss": false,
10
+    "minified": false
11
+  },
12
+  "compileType": "miniprogram"
13
+}

+ 9
- 0
project.tt.json Zobrazit soubor

@@ -0,0 +1,9 @@
1
+{
2
+  "miniprogramRoot": "./",
3
+  "projectname": "YunShake",
4
+  "appid": "touristappid",
5
+  "setting": {
6
+    "es6": false,
7
+    "minified": false
8
+  }
9
+}

+ 12
- 0
src/app.config.js Zobrazit soubor

@@ -0,0 +1,12 @@
1
+export default {
2
+  pages: [
3
+    'pages/index/index',
4
+    'pages/topList/index'
5
+  ],
6
+  window: {
7
+    backgroundTextStyle: 'light',
8
+    navigationBarBackgroundColor: '#fff',
9
+    navigationBarTitleText: 'WeChat',
10
+    navigationBarTextStyle: 'black'
11
+  }
12
+}

+ 20
- 0
src/app.js Zobrazit soubor

@@ -0,0 +1,20 @@
1
+import { Component } from 'react'
2
+import './app.less'
3
+
4
+class App extends Component {
5
+
6
+  componentDidMount () {}
7
+
8
+  componentDidShow () {}
9
+
10
+  componentDidHide () {}
11
+
12
+  componentDidCatchError () {}
13
+
14
+  // this.props.children 是将要会渲染的页面
15
+  render () {
16
+    return this.props.children
17
+  }
18
+}
19
+
20
+export default App

+ 19
- 0
src/app.less Zobrazit soubor

@@ -0,0 +1,19 @@
1
+@import './theme.less';
2
+
3
+.page-wrapper {
4
+  width: 100vw;
5
+  height: 100vh;
6
+  background-color: @primary-color;
7
+}
8
+
9
+.flex-box {
10
+  display: flex;
11
+
12
+  &-item {
13
+    flex: 1;
14
+  }
15
+
16
+  &-item-fixed {
17
+    flex: none;
18
+  }
19
+}

+ 112
- 0
src/components/Shake/index.jsx Zobrazit soubor

@@ -0,0 +1,112 @@
1
+import { View, Canvas } from '@tarojs/components'
2
+import Taro, { useDidHide, useDidShow } from '@tarojs/taro'
3
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
+import { throttle } from '@/utils'
5
+import newShake from '@/utils/shake'
6
+import S from '@/utils/shape-shifter'
7
+
8
+import './style.less'
9
+
10
+const { onShake } = newShake()
11
+
12
+const ShakeCount = (props) => {
13
+  const { className, onTimes } = props
14
+  const classNames = useMemo(() => [className, 'shake-count'].filter(Boolean).join(' '), [className])
15
+
16
+  // const [times, setTimes] = useState(0)
17
+  const timesRef = useRef(0)
18
+  // timesRef.current = times
19
+
20
+  const canvasId = useMemo(() => `f-${Math.random().toString(36).substring(2)}`, [])
21
+  const canvasRef = useRef()
22
+  const canvasShapeRef = useRef()
23
+
24
+  // eslint-disable-next-line react-hooks/exhaustive-deps
25
+  const handleShake = useCallback(throttle((times) => {
26
+    S.performAction(times)
27
+
28
+    if (onTimes) {
29
+      onTimes(times)
30
+    }
31
+  }, 500, { trailing: true, immediate: true }), [])
32
+
33
+  const initS = useCallback(() => {
34
+    if (canvasRef.current && canvasShapeRef.current) {
35
+
36
+      S.init({
37
+        canvas: canvasRef.current,
38
+        shape: canvasShapeRef.current,
39
+        width: canvasRef.current.width,
40
+        height: canvasRef.current.height
41
+      })
42
+
43
+      S.performAction(0)
44
+    }
45
+  }, [])
46
+
47
+  useDidShow(() => {
48
+    Taro.startAccelerometer()
49
+
50
+    Taro.onAccelerometerChange((e) => {
51
+      onShake(e, () => {
52
+        timesRef.current += 1
53
+        console.log('---times: ', timesRef.current)
54
+        handleShake(timesRef.current)
55
+        // S.performAction(timesRef.current)
56
+      })
57
+    })
58
+  })
59
+
60
+  useDidHide(() => {
61
+    Taro.startAccelerometer()
62
+  })
63
+
64
+  useEffect(() => {
65
+    Taro.nextTick(() => {
66
+      Taro.createSelectorQuery()
67
+        .select(`#${canvasId}`)
68
+        .fields({ node: true, size: true })
69
+        .exec((res) => {
70
+          const canvas = res[0].node
71
+          const ctx = canvas.getContext('2d')
72
+
73
+          const dpr = Taro.getSystemInfoSync().pixelRatio
74
+          const width = res[0].width * dpr
75
+          const height = res[0].height * dpr
76
+          canvas.width = width
77
+          canvas.height = height
78
+          ctx.scale(dpr, dpr)
79
+          
80
+          canvasRef.current = canvas
81
+          initS()
82
+        })
83
+
84
+      Taro.createSelectorQuery()
85
+        .select(`#${canvasId}-canvas-shape`)
86
+        .fields({ node: true, size: true })
87
+        .exec((res) => {
88
+          const canvas = res[0].node
89
+          const ctx = canvas.getContext('2d')
90
+
91
+          const dpr = Taro.getSystemInfoSync().pixelRatio
92
+          const width = res[0].width * dpr
93
+          const height = res[0].height * dpr
94
+          canvas.width = width
95
+          canvas.height = height
96
+          ctx.scale(dpr, dpr)
97
+          
98
+          canvasShapeRef.current = canvas
99
+          initS()
100
+        })
101
+    })
102
+  }, [canvasId, initS])
103
+
104
+  return (
105
+    <View className={classNames}>
106
+      <Canvas id={`${canvasId}-canvas-shape`} className='canvas-shape' type='2d'></Canvas>
107
+      <Canvas id={canvasId} type='2d'></Canvas>
108
+    </View>
109
+  )
110
+}
111
+
112
+export default ShakeCount;

+ 17
- 0
src/components/Shake/style.less Zobrazit soubor

@@ -0,0 +1,17 @@
1
+.shake-count {
2
+  width: 100%;
3
+  height: 100%;
4
+
5
+  canvas {
6
+    width: 100%;
7
+    height: 100%;
8
+    position: absolute;
9
+    top: 0;
10
+    left: 0;
11
+
12
+    &.canvas-shape {
13
+      top: -200vw;
14
+      left: -200vh;
15
+    }
16
+  }
17
+}

+ 19
- 0
src/index.html Zobrazit soubor

@@ -0,0 +1,19 @@
1
+<!DOCTYPE html>
2
+<html>
3
+<head>
4
+  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
5
+  <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
6
+  <meta name="apple-mobile-web-app-capable" content="yes">
7
+  <meta name="apple-touch-fullscreen" content="yes">
8
+  <meta name="format-detection" content="telephone=no,address=no">
9
+  <meta name="apple-mobile-web-app-status-bar-style" content="white">
10
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
11
+  <title></title>
12
+  <script>
13
+    !function(x){function w(){var v,u,t,tes,s=x.document,r=s.documentElement,a=r.getBoundingClientRect().width;if(!v&&!u){var n=!!x.navigator.appVersion.match(/AppleWebKit.*Mobile.*/);v=x.devicePixelRatio;tes=x.devicePixelRatio;v=n?v:1,u=1/v}if(a>=640){r.style.fontSize="40px"}else{if(a<=320){r.style.fontSize="20px"}else{r.style.fontSize=a/320*20+"px"}}}x.addEventListener("resize",function(){w()});w()}(window);
14
+  </script>
15
+</head>
16
+<body>
17
+  <div id="app"></div>
18
+</body>
19
+</html>

+ 21
- 0
src/pages/index/components/TopBar/index.jsx Zobrazit soubor

@@ -0,0 +1,21 @@
1
+import { View } from '@tarojs/components'
2
+import Taro from '@tarojs/taro'
3
+import React from 'react'
4
+// import { Tab, Tabs } from '@antmjs/vantui';
5
+
6
+import './style.less'
7
+
8
+export default (props) => {
9
+
10
+  const showTopList = () => {
11
+    console.log('----------------------')
12
+    Taro.navigateTo({ url: '/pages/topList/index' })
13
+  }
14
+
15
+  return (
16
+    <View className='top-list'>
17
+      <View className='top-list-item' onClick={showTopList}>高分榜</View>
18
+      <View className='top-list-item'>低分榜</View>
19
+    </View>
20
+  )
21
+}

+ 22
- 0
src/pages/index/components/TopBar/style.less Zobrazit soubor

@@ -0,0 +1,22 @@
1
+.top-list {
2
+  position: relative;
3
+  display: flex;
4
+  background: rgba(0, 0, 0, 0.2);
5
+  border-radius: 2em;
6
+  border: 2px solid rgba(255, 255, 255, 0.4);
7
+  color: #fff;
8
+  font-size: 1em;
9
+  float: right;
10
+  margin-top: 5vh;
11
+  margin-right: 5vw;
12
+
13
+  &-item {
14
+    display: inline-block;
15
+    flex: 1;
16
+    padding: .5em 1em;
17
+
18
+    & + & {
19
+      border-left: 2px solid rgba(255, 255, 255, 0.4);
20
+    }
21
+  }
22
+}

+ 3
- 0
src/pages/index/index.config.js Zobrazit soubor

@@ -0,0 +1,3 @@
1
+export default {
2
+  navigationBarTitleText: '首页'
3
+}

+ 21
- 0
src/pages/index/index.jsx Zobrazit soubor

@@ -0,0 +1,21 @@
1
+import { View } from '@tarojs/components'
2
+import ShakeCount from '@/components/Shake'
3
+import { Button } from '@antmjs/vantui';
4
+import TopBar from './components/TopBar'
5
+
6
+import './index.less'
7
+
8
+const IndexPage = (props) => {
9
+
10
+  return (
11
+    <View className='page-wrapper page-index'>
12
+      <ShakeCount className='shake-bg' />
13
+      <TopBar />
14
+      <View style={{ width: '80vw', position: 'fixed', bottom: '2em', left: '10vw' }} >
15
+        <Button block round color='linear-gradient(to right, #4bb0ff, #6149f6)'>开 始</Button>
16
+      </View>      
17
+    </View>
18
+  )
19
+}
20
+
21
+export default IndexPage;

+ 8
- 0
src/pages/index/index.less Zobrazit soubor

@@ -0,0 +1,8 @@
1
+
2
+.page-index {
3
+  .shake-bg {
4
+    position: absolute;
5
+    left: 0;
6
+    top: 0;
7
+  }
8
+}

+ 25
- 0
src/pages/topList/components/Topic1/index.jsx Zobrazit soubor

@@ -0,0 +1,25 @@
1
+import React from 'react'
2
+import { View } from '@tarojs/components'
3
+import { Image } from '@antmjs/vantui'
4
+import './style.less'
5
+
6
+export default (props) => {
7
+  const { info } = props
8
+  const { avatar, nickname, score } = info || {}
9
+
10
+  const size = 128
11
+
12
+  return (
13
+    <View className='topic-1'>
14
+      <View className='topic-1-item'>
15
+        <Image src={avatar} width={size} height={size} round></Image>
16
+      </View>
17
+      <View className='topic-1-item'>
18
+        <View className='flex-box'>          
19
+          <View className='flex-box-item topic-1-tag'>{nickname}</View>
20
+          <View className='flex-box-item topic-1-tag'>{score}</View>
21
+        </View>
22
+      </View>
23
+    </View>
24
+  )
25
+}

+ 25
- 0
src/pages/topList/components/Topic1/style.less Zobrazit soubor

@@ -0,0 +1,25 @@
1
+
2
+.topic-1 {
3
+  padding: 80rpx 0 120rpx;
4
+
5
+  &-item {
6
+    display: flex;
7
+    justify-content: center;
8
+
9
+    & + & {
10
+      margin-top: 1.8em;
11
+    }
12
+  }
13
+
14
+  &-tag {
15
+    text-align: center;
16
+    color: #fff;
17
+    font-size: 1.4em;
18
+    line-height: 24rpx;
19
+    padding: 0 1em;
20
+
21
+    & + & {
22
+      border-left: 2px solid #ccc;
23
+    }
24
+  }
25
+}

+ 30
- 0
src/pages/topList/components/TopicItem/index.jsx Zobrazit soubor

@@ -0,0 +1,30 @@
1
+import React, { useMemo } from 'react'
2
+import { View } from '@tarojs/components'
3
+import { Image } from '@antmjs/vantui'
4
+import './style.less'
5
+
6
+export default (props) => {
7
+  const { info, num } = props
8
+  const { avatar, nickname, score } = info || {}
9
+
10
+  const size = 64
11
+
12
+  const avatarStyle = useMemo(() => {
13
+    return {
14
+      width: `${size}px`
15
+    }
16
+  }, [size])
17
+
18
+  return (
19
+    <View className='topic-item flex-box'>
20
+      <View className='topic-item-num flex-box-item-fixed'>{num}</View>
21
+      <View className='flex-box topic-item-border flex-box-item'>
22
+        <View className='flex-box-item-fixed topic-item-avatar' style={avatarStyle}>
23
+          <Image src={avatar} width={size} height={size} round></Image>
24
+        </View>
25
+        <View className='flex-box-item topic-item-nickname'>{nickname}</View>
26
+        <View className='flex-box-item topic-item-score'>{score}</View>
27
+      </View>
28
+    </View>
29
+  )
30
+}

+ 46
- 0
src/pages/topList/components/TopicItem/style.less Zobrazit soubor

@@ -0,0 +1,46 @@
1
+@import '../../../../theme.less';
2
+
3
+@numWidth : 80rpx;
4
+@tpHeight : 120rpx;
5
+
6
+.topic-item {
7
+  position: relative;
8
+  width: 100%;
9
+  overflow-x: hidden;
10
+  box-sizing: border-box;
11
+  height: @tpHeight;
12
+  color: @primary-color;
13
+  background: #fff;
14
+
15
+  &-num {
16
+    width: @numWidth;
17
+    text-align: center;
18
+    line-height: @tpHeight;
19
+    font-size: 1.2em;
20
+    font-weight: bold;
21
+  }
22
+
23
+  &-avatar {
24
+    display: flex;
25
+    align-items: center;
26
+  }
27
+
28
+  &-nickname {
29
+    font-size: 1em;
30
+    line-height: @tpHeight;
31
+  }
32
+
33
+  &-score {
34
+    text-align: right;
35
+    font-size: 1.2em;
36
+    font-weight: bold;
37
+    line-height: @tpHeight;
38
+    padding-right: 2em;
39
+  }
40
+
41
+  & + & {
42
+    .topic-item-border {
43
+      border-top: 1rpx solid #ccc;
44
+    }
45
+  }
46
+}

+ 4
- 0
src/pages/topList/index.config.js Zobrazit soubor

@@ -0,0 +1,4 @@
1
+export default {
2
+  navigationBarTitleText: '排行榜',
3
+  navigationStyle: 'custom'
4
+}

+ 81
- 0
src/pages/topList/index.jsx Zobrazit soubor

@@ -0,0 +1,81 @@
1
+import { View } from '@tarojs/components'
2
+import React, { useMemo, useState } from 'react'
3
+import Taro from '@tarojs/taro'
4
+import Topic1 from './components/Topic1'
5
+import TopicItem from './components/TopicItem'
6
+
7
+import './style.less'
8
+
9
+const testData = [
10
+  {
11
+    nickname: 'test1',
12
+    score: 234234,
13
+  },
14
+  {
15
+    nickname: 'test2',
16
+    score: 234234,
17
+  },
18
+  {
19
+    nickname: 'test3',
20
+    score: 234234,
21
+  },
22
+  {
23
+    nickname: 'test4',
24
+    score: 234234,
25
+  },
26
+  {
27
+    nickname: 'test5',
28
+    score: 234234,
29
+  },
30
+  {
31
+    nickname: 'test6',
32
+    score: 234234,
33
+  },
34
+  {
35
+    nickname: 'test7',
36
+    score: 234234,
37
+  },
38
+  {
39
+    nickname: 'test8',
40
+    score: 234234,
41
+  },
42
+  {
43
+    nickname: 'test9',
44
+    score: 234234,
45
+  },
46
+  {
47
+    nickname: 'test10',
48
+    score: 234234,
49
+  },
50
+]
51
+
52
+export default (props) => {
53
+
54
+  const [list, setList] = useState(testData)
55
+  const menuBtnBounding = useMemo(() => Taro.getMenuButtonBoundingClientRect(), [])
56
+  const style = useMemo(() => {
57
+    const top = menuBtnBounding.height + menuBtnBounding.top
58
+
59
+    return {
60
+      paddingTop: `${top}px`
61
+    }
62
+  }, [menuBtnBounding.height, menuBtnBounding.top])
63
+
64
+  const [first, leftData] = useMemo(() => {
65
+    const [f, ...l] =  list || []
66
+    return [f, l]
67
+  }, [list])
68
+
69
+  return (
70
+    <View className='page-wrapper page-toplist' style={style}>
71
+      <Topic1 info={first} />
72
+      <View>
73
+        {
74
+          leftData.map((item, inx) => (
75
+            <TopicItem key={inx} num={inx+2} info={item} />
76
+          ))
77
+        }
78
+      </View>
79
+    </View>
80
+  )
81
+}

+ 4
- 0
src/pages/topList/style.less Zobrazit soubor

@@ -0,0 +1,4 @@
1
+
2
+.page-toplist {
3
+  
4
+}

+ 2
- 0
src/theme.less Zobrazit soubor

@@ -0,0 +1,2 @@
1
+
2
+@primary-color: #13c2c2;

+ 35
- 0
src/utils/index.js Zobrazit soubor

@@ -0,0 +1,35 @@
1
+
2
+export function throttle(fn, delay, options = {}) {
3
+  let timer = null;
4
+  let last = 0;
5
+
6
+  return function() {
7
+      const context = this;
8
+      const args = arguments;
9
+
10
+      const now = +new Date();
11
+      
12
+      if (last === 0 && options.immediate === false) {
13
+          last = now;
14
+      }
15
+
16
+      const offset = now - last;
17
+
18
+      if (offset > delay) {
19
+          if (timer) {
20
+              clearTimeout(timer);
21
+              timer = null;
22
+          }
23
+
24
+          last = now;
25
+          fn.apply(context, args);
26
+      }
27
+      else if (!timer && options.trailing !== false) {
28
+          timer = setTimeout(() => {
29
+              last = options.immediate === false ? 0 : +new Date();;
30
+              timer = null;
31
+              fn.apply(context, args);
32
+          }, delay - offset);
33
+      }
34
+  };
35
+}

+ 44
- 0
src/utils/shake.js Zobrazit soubor

@@ -0,0 +1,44 @@
1
+
2
+export default function Shake() {
3
+  const shakeData = {
4
+    shakeSpeed: 110, //设置阈值,越小越灵敏
5
+    // shakeStep: 2000, //摇一摇成功后间隔
6
+    lastTime: 0, //此变量用来记录上次摇动的时间
7
+    x: 0,
8
+    y: 0,
9
+    z: 0,
10
+    lastX: 0,
11
+    lastY: 0,
12
+    lastZ: 0,
13
+  }
14
+
15
+  function onShake(acceleration, next) {
16
+    //记录当前时间
17
+    const nowTime = new Date().getTime();
18
+
19
+    //如果这次摇的时间距离上次摇的时间有一定间隔 才执行
20
+    const diff = 20;
21
+    if (nowTime - shakeData.lastTime > diff) {
22
+     var diffTime = nowTime - shakeData.lastTime; //记录时间段
23
+     shakeData.lastTime = nowTime; //记录本次摇动时间,为下次计算摇动时间做准备
24
+     shakeData.x = acceleration.x; //获取 x 轴数值,x 轴为垂直于北轴,向东为正
25
+     shakeData.y = acceleration.y; //获取 y 轴数值,y 轴向正北为正
26
+     shakeData.z = acceleration.z; //获取 z 轴数值,z 轴垂直于地面,向上为正
27
+     //计算 公式的意思是 单位时间内运动的路程,即为我们想要的速度
28
+     var speed = Math.abs(shakeData.x + shakeData.y + shakeData.z - shakeData.lastX - shakeData.lastY - shakeData.lastZ) / diffTime * 10000;
29
+     //console.log(speed)
30
+     if (speed > shakeData.shakeSpeed) { //如果计算出来的速度超过了阈值,那么就算作用户成功摇一摇
31
+        next();
32
+      }
33
+
34
+     shakeData.lastX = shakeData.x; //赋值,为下一次计算做准备
35
+     shakeData.lastY = shakeData.y; //赋值,为下一次计算做准备
36
+     shakeData.lastZ = shakeData.z; //赋值,为下一次计算做准备
37
+    }
38
+  }
39
+
40
+  return {
41
+    onShake
42
+  }
43
+}
44
+

+ 402
- 0
src/utils/shape-shifter.js Zobrazit soubor

@@ -0,0 +1,402 @@
1
+
2
+/*
3
+
4
+  Shape Shifter
5
+  =============
6
+  A canvas experiment by Kenneth Cachia
7
+  http://www.kennethcachia.com
8
+
9
+  Updated code
10
+  ------------
11
+  https://github.com/kennethcachia/Shape-Shifter 
12
+
13
+*/
14
+
15
+
16
+const S = {
17
+  init: function (conf) {
18
+    S.Drawing.init(conf);
19
+    S.ShapeBuilder.init(conf);
20
+
21
+    S.Drawing.loop(function () {
22
+      S.Shape.render();
23
+    });
24
+  },
25
+  performAction: function (value) {
26
+    const fast = true;
27
+    S.Shape.switchShape(S.ShapeBuilder.letter(value), fast);
28
+  }
29
+};
30
+
31
+S.Drawing = (function () {
32
+  let canvas,
33
+      context,
34
+      renderFn,
35
+      requestFrame = function(callback) {
36
+                      setTimeout(callback, 1000 / 60);
37
+                    };
38
+
39
+  return {
40
+    init: function (conf) {
41
+      canvas = conf.canvas;
42
+      context = conf.canvas.getContext('2d');
43
+      this.adjustCanvas(conf.width, conf.height);
44
+    },
45
+
46
+    loop: function (fn) {
47
+      renderFn = !renderFn ? fn : renderFn;
48
+      this.clearFrame();
49
+      renderFn();
50
+      requestFrame.call(null, this.loop.bind(this));
51
+    },
52
+
53
+    adjustCanvas: function (width, height) {
54
+      canvas.width = width;
55
+      canvas.height = height;
56
+    },
57
+
58
+    clearFrame: function () {
59
+      context.clearRect(0, 0, canvas.width, canvas.height);
60
+    },
61
+
62
+    getArea: function () {
63
+      return { w: canvas.width, h: canvas.height };
64
+    },
65
+
66
+    drawCircle: function (p, c) {
67
+      context.fillStyle = c.render();
68
+      context.beginPath();
69
+      context.arc(p.x, p.y, p.z, 0, 2 * Math.PI, true);
70
+      context.closePath();
71
+      context.fill();
72
+    }
73
+  }
74
+}());
75
+
76
+S.Point = function (args) {
77
+  this.x = args.x;
78
+  this.y = args.y;
79
+  this.z = args.z;
80
+  this.a = args.a;
81
+  this.h = args.h;
82
+};
83
+
84
+
85
+S.Color = function (r, g, b, a) {
86
+  this.r = r;
87
+  this.g = g;
88
+  this.b = b;
89
+  this.a = a;
90
+};
91
+
92
+S.Color.prototype = {
93
+  render: function () {
94
+    return 'rgba(' + this.r + ',' +  + this.g + ',' + this.b + ',' + this.a + ')';
95
+  }
96
+};
97
+
98
+
99
+S.Dot = function (x, y) {
100
+  this.p = new S.Point({
101
+    x: x,
102
+    y: y,
103
+    z: 5,
104
+    a: 1,
105
+    h: 0
106
+  });
107
+
108
+  this.e = 0.07;
109
+  this.s = true;
110
+
111
+  this.c = new S.Color(255, 255, 255, this.p.a);
112
+
113
+  this.t = this.clone();
114
+  this.q = [];
115
+};
116
+
117
+S.Dot.prototype = {
118
+  clone: function () {
119
+    return new S.Point({
120
+      x: this.x,
121
+      y: this.y,
122
+      z: this.z,
123
+      a: this.a,
124
+      h: this.h
125
+    });
126
+  },
127
+
128
+  _draw: function () {
129
+    this.c.a = this.p.a;
130
+    S.Drawing.drawCircle(this.p, this.c);
131
+  },
132
+
133
+  _moveTowards: function (n) {
134
+    let details = this.distanceTo(n, true),
135
+        dx = details[0],
136
+        dy = details[1],
137
+        d = details[2],
138
+        e = this.e * d;
139
+
140
+    if (this.p.h === -1) {
141
+      this.p.x = n.x;
142
+      this.p.y = n.y;
143
+      return true;
144
+    }
145
+
146
+    if (d > 1) {
147
+      this.p.x -= ((dx / d) * e);
148
+      this.p.y -= ((dy / d) * e);
149
+    } else {
150
+      if (this.p.h > 0) {
151
+        this.p.h--;
152
+      } else {
153
+        return true;
154
+      }
155
+    }
156
+
157
+    return false;
158
+  },
159
+
160
+  _update: function () {
161
+    if (this._moveTowards(this.t)) {
162
+      let p = this.q.shift();
163
+
164
+      if (p) {
165
+        this.t.x = p.x || this.p.x;
166
+        this.t.y = p.y || this.p.y;
167
+        this.t.z = p.z || this.p.z;
168
+        this.t.a = p.a || this.p.a;
169
+        this.p.h = p.h || 0;
170
+      } else {
171
+        if (this.s) {
172
+          this.p.x -= Math.sin(Math.random() * 3.142);
173
+          this.p.y -= Math.sin(Math.random() * 3.142);
174
+        } else {
175
+          this.move(new S.Point({
176
+            x: this.p.x + (Math.random() * 50) - 25,
177
+            y: this.p.y + (Math.random() * 50) - 25,
178
+          }));
179
+        }
180
+      }
181
+    }
182
+
183
+    let d = this.p.a - this.t.a;
184
+    this.p.a = Math.max(0.1, this.p.a - (d * 0.05));
185
+    d = this.p.z - this.t.z;
186
+    this.p.z = Math.max(1, this.p.z - (d * 0.05));
187
+  },
188
+
189
+  distanceTo: function (n, details) {
190
+    let dx = this.p.x - n.x,
191
+        dy = this.p.y - n.y,
192
+        d = Math.sqrt(dx * dx + dy * dy);
193
+
194
+    return details ? [dx, dy, d] : d;
195
+  },
196
+
197
+  move: function (p, avoidStatic) {
198
+    if (!avoidStatic || (avoidStatic && this.distanceTo(p) > 1)) {
199
+      this.q.push(p);
200
+    }
201
+  },
202
+
203
+  render: function () {
204
+    this._update();
205
+    this._draw();
206
+  }
207
+}
208
+
209
+
210
+S.ShapeBuilder = (function () {
211
+  let gap = 13,
212
+      shapeCanvas,
213
+      shapeContext,
214
+      fontSize = 500,
215
+      fontFamily = 'Avenir, Helvetica Neue, Helvetica, Arial, sans-serif';
216
+
217
+  function fit({ width, height }) {
218
+    shapeCanvas.width = Math.floor(width / gap) * gap;
219
+    shapeCanvas.height = Math.floor(height / gap) * gap;
220
+    shapeContext.fillStyle = 'red';
221
+    shapeContext.textBaseline = 'middle';
222
+    shapeContext.textAlign = 'center';
223
+  }
224
+
225
+  function processCanvas() {
226
+    let pixels = shapeContext.getImageData(0, 0, shapeCanvas.width, shapeCanvas.height).data,
227
+        dots = [],
228
+        x = 0,
229
+        y = 0,
230
+        fx = shapeCanvas.width,
231
+        fy = shapeCanvas.height,
232
+        w = 0,
233
+        h = 0;
234
+
235
+    for (let p = 0; p < pixels.length; p += (4 * gap)) {
236
+      if (pixels[p + 3] > 0) {
237
+        dots.push(new S.Point({
238
+          x: x,
239
+          y: y
240
+        }));
241
+
242
+        w = x > w ? x : w;
243
+        h = y > h ? y : h;
244
+        fx = x < fx ? x : fx;
245
+        fy = y < fy ? y : fy;
246
+      }
247
+
248
+      x += gap;
249
+
250
+      if (x >= shapeCanvas.width) {
251
+        x = 0;
252
+        y += gap;
253
+        p += gap * 4 * shapeCanvas.width;
254
+      }
255
+    }
256
+
257
+    return { dots: dots, w: w + fx, h: h + fy };
258
+  }
259
+
260
+  function setFontSize(s) {
261
+    shapeContext.font = 'bold ' + s + 'px ' + fontFamily;
262
+  }
263
+
264
+  function isNumber(n) {
265
+    // eslint-disable-next-line no-restricted-globals
266
+    return !isNaN(parseFloat(n)) && isFinite(n);
267
+  }
268
+
269
+  function init(conf) {
270
+    shapeCanvas = conf.shape;
271
+    shapeContext = conf.shape.getContext('2d');
272
+
273
+    fit(conf);
274
+  }
275
+
276
+  return {
277
+    init,
278
+    letter: function (l) {
279
+      let s = 0;
280
+
281
+      setFontSize(fontSize);
282
+      s = Math.min(fontSize,
283
+                  (shapeCanvas.width / shapeContext.measureText(l).width) * 0.8 * fontSize, 
284
+                  (shapeCanvas.height / fontSize) * (isNumber(l) ? 1 : 0.45) * fontSize);
285
+      setFontSize(s);
286
+
287
+      shapeContext.clearRect(0, 0, shapeCanvas.width, shapeCanvas.height);
288
+      shapeContext.fillText(l, shapeCanvas.width / 2, shapeCanvas.height / 2);
289
+
290
+      return processCanvas();
291
+    }
292
+  };
293
+}());
294
+
295
+
296
+S.Shape = (function () {
297
+  let dots = [],
298
+      width = 0,
299
+      height = 0,
300
+      cx = 0,
301
+      cy = 0;
302
+
303
+  function compensate() {
304
+    let a = S.Drawing.getArea();
305
+
306
+    cx = a.w / 2 - width / 2;
307
+    cy = a.h / 2 - height / 2;
308
+  }
309
+
310
+  return {
311
+    shuffleIdle: function () {
312
+      let a = S.Drawing.getArea();
313
+
314
+      for (let d = 0; d < dots.length; d++) {
315
+        if (!dots[d].s) {
316
+          dots[d].move({
317
+            x: Math.random() * a.w,
318
+            y: Math.random() * a.h
319
+          });
320
+        }
321
+      }
322
+    },
323
+
324
+    switchShape: function (n, fast) {
325
+      let size,
326
+          a = S.Drawing.getArea();
327
+
328
+      width = n.w;
329
+      height = n.h;
330
+
331
+      compensate();
332
+
333
+      if (n.dots.length > dots.length) {
334
+        size = n.dots.length - dots.length;
335
+        for (let d = 1; d <= size; d++) {
336
+          dots.push(new S.Dot(a.w / 2, a.h / 2));
337
+        }
338
+      }
339
+
340
+      let d = 0,
341
+          i = 0;
342
+
343
+      while (n.dots.length > 0) {
344
+        i = Math.floor(Math.random() * n.dots.length);
345
+        dots[d].e = fast ? 0.25 : (dots[d].s ? 0.14 : 0.11);
346
+
347
+        if (dots[d].s) {
348
+          dots[d].move(new S.Point({
349
+            z: Math.random() * 20 + 10,
350
+            a: Math.random(),
351
+            h: 18
352
+          }));
353
+        } else {
354
+          dots[d].move(new S.Point({
355
+            z: Math.random() * 5 + 5,
356
+            h: fast ? 18 : 30
357
+          }));
358
+        }
359
+
360
+        dots[d].s = true;
361
+        dots[d].move(new S.Point({
362
+          x: n.dots[i].x + cx,
363
+          y: n.dots[i].y + cy,
364
+          a: 1,
365
+          z: 5,
366
+          h: 0
367
+        }));
368
+
369
+        n.dots = n.dots.slice(0, i).concat(n.dots.slice(i + 1));
370
+        d++;
371
+      }
372
+
373
+      for (i = d; i < dots.length; i++) {
374
+        if (dots[i].s) {
375
+          dots[i].move(new S.Point({
376
+            z: Math.random() * 20 + 10,
377
+            a: Math.random(),
378
+            h: 20
379
+          }));
380
+
381
+          dots[i].s = false;
382
+          dots[i].e = 0.04;
383
+          dots[i].move(new S.Point({ 
384
+            x: Math.random() * a.w,
385
+            y: Math.random() * a.h,
386
+            a: 0.3, //.4
387
+            z: Math.random() * 4,
388
+            h: 0
389
+          }));
390
+        }
391
+      }
392
+    },
393
+
394
+    render: function () {
395
+      for (let d = 0; d < dots.length; d++) {
396
+        dots[d].render();
397
+      }
398
+    }
399
+  }
400
+}());
401
+
402
+export default S;

+ 11268
- 0
yarn.lock
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor