Skip to content

Commit

Permalink
feature: 添加数字动画,添加vue_types
Browse files Browse the repository at this point in the history
  • Loading branch information
jsxiaosi committed Jan 10, 2022
1 parent 77ff040 commit 46573cb
Show file tree
Hide file tree
Showing 16 changed files with 554 additions and 77 deletions.
8 changes: 4 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ module.exports = {
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2016,
ecmaVersion: 2020,
parser: '@typescript-eslint/parser',
// 对Babel解析器的包装使其与 ESLint 兼容。
// parser: 'babel-eslint',
// 代码是 ECMAScript 模块
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
// plugins: ['vue'],
rules: {
Expand Down
18 changes: 9 additions & 9 deletions mock/_createProductionServer.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';

const modules = import.meta.globEager('./**/*.ts')
const modules = import.meta.globEager('./**/*.ts');

const mockModules: any[] = []
const mockModules: any[] = [];
Object.keys(modules).forEach((key) => {
if (key.includes('/_')) {
return
}
mockModules.push(...modules[key].default)
})
if (key.includes('/_')) {
return;
}
mockModules.push(...modules[key].default);
});

export function setupProdMockServer() {
createProdMockServer(mockModules)
createProdMockServer(mockModules);
}
101 changes: 49 additions & 52 deletions mock/demo/account.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,56 @@
import { MockMethod, Recordable } from 'vite-plugin-mock'
import { MockMethod, Recordable } from 'vite-plugin-mock';

const userInfo = {
name: '爱喝蜂蜜绿的小斯斯',
userid: '00000001',
email: '[email protected]',
signature:
'甜甜的蜂蜜,甘甜的绿茶,蜂蜜中和了绿茶的苦涩保留了绿茶回甘,绝妙啊',
introduction: '微笑着,努力着,欣赏着',
title: '小斯斯',
token: '',
}
name: '爱喝蜂蜜绿的小斯斯',
userid: '00000001',
email: '[email protected]',
signature: '甜甜的蜂蜜,甘甜的绿茶,蜂蜜中和了绿茶的苦涩保留了绿茶回甘,绝妙啊',
introduction: '微笑着,努力着,欣赏着',
title: '小斯斯',
token: '',
};

export default [
{
url: '/mock_api/login',
timeout: 1000,
method: 'post',
response: ({ body }: { body: Recordable }) => {
const { username, password } = body
if (username == 'admin' && password == 'admin123') {
userInfo.token = genID(16)
return {
data: userInfo,
code: 1,
message: 'ok',
}
} else {
return {
data: userInfo,
code: -1,
message: '账号密码错误',
}
}
},
// rawResponse: async (req, res) => {
// console.log(req, res);
// let reqbody = {};
// res.setHeader('Content-Type', 'application/json');
// reqbody = { data: userInfo };
// res.statusCode = 500;
// res.end(JSON.stringify(reqbody));
// },
},
{
url: '/mock_api/getUserInfo',
timeout: 1000,
method: 'get',
response: () => {
return userInfo
},
},
] as MockMethod[]
{
url: '/mock_api/login',
timeout: 1000,
method: 'post',
response: ({ body }: { body: Recordable }) => {
const { username, password } = body;
if (username == 'admin' && password == 'admin123') {
userInfo.token = genID(16);
return {
data: userInfo,
code: 1,
message: 'ok',
};
} else {
return {
data: userInfo,
code: -1,
message: '账号密码错误',
};
}
},
// rawResponse: async (req, res) => {
// console.log(req, res);
// let reqbody = {};
// res.setHeader('Content-Type', 'application/json');
// reqbody = { data: userInfo };
// res.statusCode = 500;
// res.end(JSON.stringify(reqbody));
// },
},
{
url: '/mock_api/getUserInfo',
timeout: 1000,
method: 'get',
response: () => {
return userInfo;
},
},
] as MockMethod[];

function genID(length: number) {
return Number(
Math.random().toString().substr(3, length) + Date.now()
).toString(36)
return Number(Math.random().toString().substr(3, length) + Date.now()).toString(36);
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"vue": "^3.2.22",
"vue-i18n": "^9.1.9",
"vue-router": "^4.0.12",
"vue-types": "^4.1.1",
"vuex": "^4.0.2"
},
"devDependencies": {
Expand Down
71 changes: 71 additions & 0 deletions src/components/CountTo/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script lang="tsx">
import { defineComponent, ref, watch } from 'vue';
import Normal from './src/normal';
import Rebound from './src/rebound/index.vue';
import { propTypes } from '@/utils/propTypes';
import { converToArray } from '@/utils';
/**
* @param type 动画类型 normal:典型动画 , rebound:回退动画
* @param prefix 前置文案
* @param duration 动画时长 只针对normal有效
* @param startVal 动画起始值 只针对normal有效
* @param numVal 最终显示值
*/
const CountToProps = {
type: propTypes.string.def('normal'),
prefix: propTypes.string.def(),
duration: propTypes.number.def(2000),
startVal: propTypes.number.def(0),
numVal: propTypes.number.def(1666),
};
export default defineComponent({
name: 'CountTo',
components: {
Normal,
Rebound,
},
props: CountToProps,
setup(props) {
const reboundValue = ref<Array<Number>>([]);
watch(
[() => props.numVal],
() => {
if (props.type === 'rebound') {
reboundValue.value = converToArray(props.numVal);
}
},
{
immediate: true,
},
);
return () => (
<div>
{props.type === 'normal' ? (
<Normal
prefix={props.prefix}
duration={props.duration}
start-val={props.startVal}
end-val={props.numVal}
/>
) : (
<div class="rebound">
<span>{props.prefix}</span>
{reboundValue.value.map((num, inx) => {
return <Rebound key={inx} i={num as number} blur={inx} delay={inx + 1} />;
})}
</div>
)}
</div>
);
},
});
</script>

<style lang="scss" scoped>
.rebound {
display: flex;
align-items: center;
}
</style>
152 changes: 152 additions & 0 deletions src/components/CountTo/src/normal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { defineComponent, reactive, computed, watch, onMounted, unref } from 'vue';
import { countToProps } from './props';
import { isNumber } from '@/utils/is';

export default defineComponent({
name: 'CountToNormal',
props: countToProps,
emits: ['mounted', 'callback'],
setup(props, { emit }) {
const state = reactive<{
localStartVal: number;
printVal: number | null;
displayValue: string;
paused: boolean;
localDuration: number | null;
startTime: number | null;
timestamp: number | null;
rAF: any;
remaining: number | null;
}>({
localStartVal: props.startVal,
displayValue: formatNumber(props.startVal),
printVal: null,
paused: false,
localDuration: props.duration,
startTime: null,
timestamp: null,
remaining: null,
rAF: null,
});

const getCountDown = computed(() => {
return props.startVal > props.endVal;
});

watch([() => props.startVal, () => props.endVal], () => {
if (props.autoplay) {
start();
}
});

function start() {
const { startVal, duration } = props;
state.localStartVal = startVal;
state.startTime = null;
state.localDuration = duration;
state.paused = false;
state.rAF = requestAnimationFrame(count);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
function pauseResume() {
if (state.paused) {
resume();
state.paused = false;
} else {
pause();
state.paused = true;
}
}

function pause() {
cancelAnimationFrame(state.rAF);
}

function resume() {
state.startTime = null;
state.localDuration = +(state.remaining as number);
state.localStartVal = +(state.printVal as number);
requestAnimationFrame(count);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
function reset() {
state.startTime = null;
cancelAnimationFrame(state.rAF);
state.displayValue = formatNumber(props.startVal);
}

function count(timestamp: number) {
const { useEasing, easingFn, endVal } = props;
if (!state.startTime) state.startTime = timestamp;
state.timestamp = timestamp;
const progress = timestamp - state.startTime;
state.remaining = (state.localDuration as number) - progress;
if (useEasing) {
if (unref(getCountDown)) {
state.printVal =
state.localStartVal -
easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number);
} else {
state.printVal = easingFn(
progress,
state.localStartVal,
endVal - state.localStartVal,
state.localDuration as number,
);
}
} else {
if (unref(getCountDown)) {
state.printVal =
state.localStartVal -
(state.localStartVal - endVal) * (progress / (state.localDuration as number));
} else {
state.printVal =
state.localStartVal +
(endVal - state.localStartVal) * (progress / (state.localDuration as number));
}
}
if (unref(getCountDown)) {
state.printVal = state.printVal < endVal ? endVal : state.printVal;
} else {
state.printVal = state.printVal > endVal ? endVal : state.printVal;
}
state.displayValue = formatNumber(state.printVal);
if (progress < (state.localDuration as number)) {
state.rAF = requestAnimationFrame(count);
} else {
emit('callback');
}
}

function formatNumber(num: number | string) {
const { decimals, decimal, separator, suffix, prefix } = props;
num = Number(num).toFixed(decimals);
num += '';
const x = num.split('.');
let x1 = x[0];
const x2 = x.length > 1 ? decimal + x[1] : '';
const rgx = /(\d+)(\d{3})/;
if (separator && !isNumber(separator)) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + separator + '$2');
}
}
return prefix + x1 + x2 + suffix;
}

onMounted(() => {
if (props.autoplay) {
start();
}
emit('mounted');
});

return () => (
<>
<span>{state.displayValue}</span>
</>
);
},
});
Loading

0 comments on commit 46573cb

Please sign in to comment.