This commit is contained in:
pipipi-pikachu 2020-12-25 23:52:48 +08:00
parent 462f5120af
commit 93785a9ebc
16 changed files with 474 additions and 321 deletions

348
package-lock.json generated
View File

@ -2258,122 +2258,6 @@
"tslint": "^5.20.1",
"webpack": "^4.0.0",
"yorkie": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792369066&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687000046&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz?cache=0&sync_timestamp=1566248870121&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor-convert%2Fdownload%2Fcolor-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
"resolved": "https://registry.npm.taobao.org/fork-ts-checker-webpack-plugin/download/fork-ts-checker-webpack-plugin-5.2.1.tgz?cache=0&sync_timestamp=1607084938170&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffork-ts-checker-webpack-plugin%2Fdownload%2Ffork-ts-checker-webpack-plugin-5.2.1.tgz",
"integrity": "sha1-eTJthpeXkG+osk4qvPlCH8gFRQ0=",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz?cache=0&sync_timestamp=1594427484405&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-6.0.0.tgz",
"integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=",
"dev": true,
"optional": true,
"requires": {
"yallist": "^4.0.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-2.7.0.tgz",
"integrity": "sha1-FxUfdtjq5n+793lgwzxnatn078c=",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"semver": {
"version": "7.3.4",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.4.tgz?cache=0&sync_timestamp=1606852064928&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.4.tgz",
"integrity": "sha1-J6qn0uTKdkUvmNOt0JOnLJQ+3Jc=",
"dev": true,
"optional": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1606205010380&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz",
"integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=",
"dev": true,
"optional": true
}
}
},
"@vue/cli-plugin-unit-jest": {
@ -2537,17 +2421,6 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687000046&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npm.taobao.org/cliui/download/cliui-6.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-6.0.0.tgz",
@ -2610,18 +2483,6 @@
"graceful-fs": "^4.1.6"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz",
@ -2670,18 +2531,6 @@
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=",
"dev": true
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.2.tgz?cache=0&sync_timestamp=1608187974157&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.2.tgz",
"integrity": "sha1-XAO2xQ0qX5g8fOuhXFDXjKKymPQ=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-6.2.0.tgz",
@ -7478,6 +7327,122 @@
"worker-rpc": "^0.1.0"
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
"resolved": "https://registry.npm.taobao.org/fork-ts-checker-webpack-plugin/download/fork-ts-checker-webpack-plugin-5.2.1.tgz",
"integrity": "sha1-eTJthpeXkG+osk4qvPlCH8gFRQ0=",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792302448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1596294337050&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz",
"integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=",
"dev": true,
"optional": true,
"requires": {
"yallist": "^4.0.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-2.7.0.tgz",
"integrity": "sha1-FxUfdtjq5n+793lgwzxnatn078c=",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"semver": {
"version": "7.3.4",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.4.tgz?cache=0&sync_timestamp=1606852122426&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.4.tgz",
"integrity": "sha1-J6qn0uTKdkUvmNOt0JOnLJQ+3Jc=",
"dev": true,
"optional": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz",
"integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=",
"dev": true,
"optional": true
}
}
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz",
@ -15923,6 +15888,87 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.2.tgz?cache=0&sync_timestamp=1608187974157&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.2.tgz",
"integrity": "sha1-XAO2xQ0qX5g8fOuhXFDXjKKymPQ=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792302448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1596294337050&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-style-loader": {
"version": "4.1.2",
"resolved": "https://registry.npm.taobao.org/vue-style-loader/download/vue-style-loader-4.1.2.tgz",

View File

@ -1,38 +1,90 @@
export const ANIMATIONS = [
{ name: '渐显', value: 'fadeIn' },
{ name: '向右进入', value: 'fadeInLeft' },
{ name: '向左进入', value: 'fadeInRight' },
{ name: '向上进入', value: 'fadeInUp' },
{ name: '向下进入', value: 'fadeInDown' },
{ name: '向右长距进入', value: 'fadeInLeftBig' },
{ name: '向左长距进入', value: 'fadeInRightBig' },
{ name: '向上长距进入', value: 'fadeInUpBig' },
{ name: '向下长距进入', value: 'fadeInDownBig' },
{ name: '旋转进入', value: 'rotateIn' },
{ name: '左顺时针旋转', value: 'rotateInDownLeft' },
{ name: '右逆时针旋转', value: 'rotateInDownRight' },
{ name: '左逆时针旋转', value: 'rotateInUpLeft' },
{ name: '右逆时针旋转', value: 'rotateInUpRight' },
{ name: '弹入', value: 'bounceIn' },
{ name: '向右弹入', value: 'bounceInLeft' },
{ name: '向左弹入', value: 'bounceInRight' },
{ name: '向上弹入', value: 'bounceInUp' },
{ name: '向下弹入', value: 'bounceInDown' },
{ name: '光速从右进入', value: 'lightSpeedInRight' },
{ name: '光速从左进入', value: 'lightSpeedInLeft' },
{ name: '光速从右退出', value: 'lightSpeedOutRight' },
{ name: '光速从左退出', value: 'lightSpeedOutLeft' },
{ name: 'Y轴旋转', value: 'flip' },
{ name: '中心X轴旋转', value: 'flipInX' },
{ name: '中心Y轴旋转', value: 'flipInY' },
{ name: '左长半径旋转', value: 'rollIn' },
{ name: '由小变大进入', value: 'zoomIn' },
{ name: '左变大进入', value: 'zoomInLeft' },
{ name: '右变大进入', value: 'zoomInRight' },
{ name: '向上变大进入', value: 'zoomInUp' },
{ name: '向下变大进入', value: 'zoomInDown' },
{ name: '向右滑动展开', value: 'slideInLeft' },
{ name: '向左滑动展开', value: 'slideInRight' },
{ name: '向上滑动展开', value: 'slideInUp' },
{ name: '向下滑动展开', value: 'slideInDown' },
{
type: 'bounce',
name: '弹跳',
children: [
{ name: '弹入', value: 'bounceIn' },
{ name: '向右弹入', value: 'bounceInLeft' },
{ name: '向左弹入', value: 'bounceInRight' },
{ name: '向上弹入', value: 'bounceInUp' },
{ name: '向下弹入', value: 'bounceInDown' },
],
},
{
type: 'fade',
name: '浮现',
children: [
{ name: '浮入', value: 'fadeIn' },
{ name: '向下浮入', value: 'fadeInDown' },
{ name: '向下长距浮入', value: 'fadeInDownBig' },
{ name: '向右浮入', value: 'fadeInLeft' },
{ name: '向右长距浮入', value: 'fadeInLeftBig' },
{ name: '向左浮入', value: 'fadeInRight' },
{ name: '向左长距浮入', value: 'fadeInRightBig' },
{ name: '向上浮入', value: 'fadeInUp' },
{ name: '向上长距浮入', value: 'fadeInUpBig' },
{ name: '从左上浮入', value: 'fadeInTopLeft' },
{ name: '从右上浮入', value: 'fadeInTopRight' },
{ name: '从左下浮入', value: 'fadeInBottomLeft' },
{ name: '从右下浮入', value: 'fadeInBottomRight' },
],
},
{
type: 'rotate',
name: '旋转',
children: [
{ name: '旋转进入', value: 'rotateIn' },
{ name: '基于左下旋转进入', value: 'rotateInDownLeft' },
{ name: '基于右下旋转进入', value: 'rotateInDownRight' },
{ name: '基于左上旋转进入', value: 'rotateInUpLeft' },
{ name: '基于右上旋转进入', value: 'rotateInUpRight' },
],
},
{
type: 'zoom',
name: '缩放',
children: [
{ name: '放大进入', value: 'zoomIn' },
{ name: '向下放大进入', value: 'zoomInDown' },
{ name: '从左放大进入', value: 'zoomInLeft' },
{ name: '从右放大进入', value: 'zoomInRight' },
{ name: '向上放大进入', value: 'zoomInUp' },
],
},
{
type: 'slide',
name: '滑入',
children: [
{ name: '向下滑入', value: 'slideInDown' },
{ name: '从右滑入', value: 'slideInLeft' },
{ name: '从左滑入', value: 'slideInRight' },
{ name: '向上滑入', value: 'slideInUp' },
],
},
{
type: 'flip',
name: '翻转',
children: [
{ name: 'X轴翻转进入', value: 'flipInX' },
{ name: 'Y轴翻转进入', value: 'flipInY' },
],
},
{
type: 'back',
name: '放大滑入',
children: [
{ name: '向下放大滑入', value: 'backInDown' },
{ name: '从左放大滑入', value: 'backInLeft' },
{ name: '从右放大滑入', value: 'backInRight' },
{ name: '向上放大滑入', value: 'backInUp' },
],
},
{
type: 'lightSpeed',
name: '飞入',
children: [
{ name: '从右飞入', value: 'lightSpeedInRight' },
{ name: '从左飞入', value: 'lightSpeedInLeft' },
],
},
]

View File

@ -1,56 +0,0 @@
import { computed, Ref, ref } from 'vue'
import { useStore } from 'vuex'
import { State } from '@/store'
import { Slide } from '@/types/slides'
const prefix = 'animate__'
export default (elId: string) => {
const animationIndex = ref(-1)
const needWaitAnimation = ref(false)
const store = useStore<State>()
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
const animations = computed(() => currentSlide.value.animations || [])
const elementIndexInAnimation = animations.value.findIndex(animation => animation.elId === elId)
if(elementIndexInAnimation !== -1 && elementIndexInAnimation < animationIndex.value) {
needWaitAnimation.value = true
}
const runAnimation = () => {
const animations = currentSlide.value.animations || []
if(!animations.length) return
if(animationIndex.value >= animations.length - 1) return
animationIndex.value += 1
const animation = animations[animationIndex.value]
const elRef = document.querySelector(`#screen-el-${animation.elId}`)
if(elRef) {
const animationName = `${prefix}${animation.type}`
elRef.classList.add(`${prefix}animated`, animationName)
}
}
const revokeAnimation = () => {
const animations = currentSlide.value.animations || []
if(!animations.length) return
if(animationIndex.value <= 0) return
animationIndex.value -= 1
const animation = animations[animationIndex.value]
const elRef = document.querySelector(`#screen-el-${animation.elId}`)
if(elRef) {
const animationName = `${prefix}${animation.type}`
elRef.classList.remove(`${prefix}animated`, animationName)
}
}
return {
needWaitAnimation,
runAnimation,
revokeAnimation,
}
}

View File

@ -68,7 +68,7 @@ export const slides: Slide[] = [
},
{
elId: 'xxx2',
type: 'zoomInDown',
type: 'zoomIn',
duration: 1000,
},
],
@ -89,5 +89,12 @@ export const slides: Slide[] = [
content: '<div>😀 😐 😶 😜 🔔 ⭐ ⚡ 🔥 👍 💡 🔰 🎀 🎁 🥇 🏅 🏆 🎈 🎉 💎 🚧 ⛔ 📢 ⌛ ⏰ 🕒 🧩 🎵 📎 🔒 🔑 ⛳ 📌 📍 💬 📅 📈 📋 📜 📁 📱 💻 💾 🌏 🚚 🚡 🚢💧 🌐 🧭 💰 💳 🛒</div>',
},
],
animations: [
{
elId: 'yyx1',
type: 'flipInX',
duration: 1000,
},
],
},
]

View File

@ -10,6 +10,7 @@ export enum MutationTypes {
SET_DISABLE_HOTKEYS_STATE = 'setDisableHotkeysState',
SET_GRID_LINES_STATE = 'setGridLinesState',
SET_AVAILABLE_FONTS = 'setAvailableFonts',
SET_TOOLBAR_STATE = 'setToolbarState',
// slides
SET_SLIDES = 'setSlides',
@ -29,7 +30,7 @@ export enum MutationTypes {
SET_SHIFT_KEY_STATE = 'setShiftKeyState',
// screen
SET_SCREENING = 'SET_SCREENING'
SET_SCREENING = 'setScreening',
}
export enum ActionTypes {

View File

@ -20,6 +20,7 @@ export interface State {
disableHotkeys: boolean;
showGridLines: boolean;
availableFonts: FontName[];
toolbarState: string;
slides: Slide[];
slideIndex: number;
snapshotCursor: number;
@ -39,6 +40,7 @@ const state: State = {
disableHotkeys: false,
showGridLines: false,
availableFonts: [],
toolbarState: '',
slides: slides,
slideIndex: 0,
snapshotCursor: -1,

View File

@ -53,6 +53,10 @@ export const mutations: MutationTree<State> = {
state.availableFonts = FONT_NAMES.filter(font => isSupportFontFamily(font.en))
},
[MutationTypes.SET_TOOLBAR_STATE](state, type) {
state.toolbarState = type
},
// slides
[MutationTypes.SET_SLIDES](state, slides: Slide[]) {

View File

@ -1,35 +0,0 @@
<template>
<div class="animation-index">
{{animationIndex}}
</div>
</template>
<script lang="ts">
export default {
name: 'animation-index',
props: {
animationIndex: {
type: Number,
required: true,
},
},
}
</script>
<style lang="scss" scoped>
.animation-index {
position: absolute;
top: 0;
left: -22px;
font-size: 12px;
width: 20px;
height: 20px;
background-color: #fff;
color: #333;
border: 1px solid #666;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 3px;
}
</style>

View File

@ -42,8 +42,6 @@
@mousedown.stop="rotateElement(elementInfo)"
/>
</template>
<AnimationIndex v-if="animationIndex !== -1" :animationIndex="animationIndex" />
</div>
</template>
@ -59,7 +57,6 @@ import useCommonOperate from '../hooks/useCommonOperate'
import RotateHandler from './RotateHandler.vue'
import ResizeHandler from './ResizeHandler.vue'
import BorderLine from './BorderLine.vue'
import AnimationIndex from './AnimationIndex.vue'
import ImageClipHandler from './ImageClipHandler.vue'
export default defineComponent({
@ -68,7 +65,6 @@ export default defineComponent({
RotateHandler,
ResizeHandler,
BorderLine,
AnimationIndex,
ImageClipHandler,
},
props: {
@ -92,10 +88,6 @@ export default defineComponent({
type: Boolean,
required: true,
},
animationIndex: {
type: Number,
required: true,
},
rotateElement: {
type: Function as PropType<(element: PPTImageElement) => void>,
required: true,

View File

@ -29,8 +29,6 @@
@mousedown.stop="rotateElement(elementInfo)"
/>
</template>
<AnimationIndex v-if="animationIndex !== -1" :animationIndex="animationIndex" />
</div>
</template>
@ -46,7 +44,6 @@ import useCommonOperate from '../hooks/useCommonOperate'
import RotateHandler from './RotateHandler.vue'
import ResizeHandler from './ResizeHandler.vue'
import BorderLine from './BorderLine.vue'
import AnimationIndex from './AnimationIndex.vue'
export default defineComponent({
name: 'text-element-operate',
@ -54,7 +51,6 @@ export default defineComponent({
RotateHandler,
ResizeHandler,
BorderLine,
AnimationIndex,
},
props: {
elementInfo: {
@ -77,10 +73,6 @@ export default defineComponent({
type: Boolean,
required: true,
},
animationIndex: {
type: Number,
required: true,
},
rotateElement: {
type: Function as PropType<(element: PPTTextElement) => void>,
required: true,

View File

@ -15,22 +15,29 @@
:isActive="isActive"
:isActiveGroupElement="isActiveGroupElement"
:isMultiSelect="isMultiSelect"
:animationIndex="animationIndex"
:animationIndex="elementIndexInAnimation"
:rotateElement="rotateElement"
:scaleElement="scaleElement"
></component>
<div
class="animation-index"
v-if="toolbarState === 'animation' && elementIndexInAnimation !== -1"
>
{{elementIndexInAnimation + 1}}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from 'vue'
import { PPTElement } from '@/types/slides'
import { defineComponent, PropType, computed, Ref } from 'vue'
import { useStore } from 'vuex'
import { State } from '@/store'
import { PPTElement, Slide } from '@/types/slides'
import { OperateResizeHandler } from '@/types/edit'
import ImageElementOperate from './ImageElementOperate.vue'
import TextElementOperate from './TextElementOperate.vue'
import { useStore } from 'vuex'
import { State } from '@/store'
export default defineComponent({
name: 'operate',
@ -55,10 +62,6 @@ export default defineComponent({
type: Boolean,
required: true,
},
animationIndex: {
type: Number,
default: -1,
},
rotateElement: {
type: Function as PropType<(element: PPTElement) => void>,
required: true,
@ -71,6 +74,8 @@ export default defineComponent({
setup(props) {
const store = useStore<State>()
const canvasScale = computed(() => store.state.canvasScale)
const toolbarState = computed(() => store.state.toolbarState)
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
const currentOperateComponent = computed(() => {
const elementTypeMap = {
@ -80,9 +85,16 @@ export default defineComponent({
return elementTypeMap[props.elementInfo.type] || null
})
const elementIndexInAnimation = computed(() => {
const animations = currentSlide.value.animations || []
return animations.findIndex(animation => animation.elId === props.elementInfo.id)
})
return {
currentOperateComponent,
canvasScale,
toolbarState,
elementIndexInAnimation,
}
},
})
@ -94,4 +106,19 @@ export default defineComponent({
z-index: 100;
user-select: none;
}
.animation-index {
position: absolute;
top: 0;
left: -24px;
font-size: 12px;
width: 18px;
height: 18px;
background-color: #fff;
color: $themeColor;
border: 1px solid $themeColor;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 3px;
}
</style>

View File

@ -58,7 +58,9 @@
:height="mouseSelectionState.height"
:quadrant="mouseSelectionState.quadrant"
/>
<SlideBackground />
<EditableElement
v-for="(element, index) in elementList"
:key="element.id"

View File

@ -8,11 +8,13 @@
}"
>
<div class="background" :style="{ ...backgroundStyle }"></div>
<BaseElement
<ScreenElement
:id="`screen-element-${element.id}`"
v-for="(element, index) in slide.elements"
:key="element.id"
:elementInfo="element"
:elementIndex="index + 1"
:animationIndex="animationIndex"
/>
</div>
</template>
@ -23,12 +25,12 @@ import { Slide } from '@/types/slides'
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
import BaseElement from '@/views/_element/BaseElement.vue'
import ScreenElement from '@/views/_element/ScreenElement.vue'
export default defineComponent({
name: 'screen-slide',
components: {
BaseElement,
ScreenElement,
},
props: {
slide: {
@ -39,6 +41,10 @@ export default defineComponent({
type: Number,
required: true,
},
animationIndex: {
type: Number,
default: -1,
},
},
setup(props) {
const background = computed(() => props.slide.background)

View File

@ -24,7 +24,11 @@
height: slideHeight + 'px',
}"
>
<ScreenSlide :scale="scale" :slide="slide" />
<ScreenSlide
:scale="scale"
:slide="slide"
:animationIndex="animationIndex"
/>
</div>
</div>
</div>
@ -32,7 +36,7 @@
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, onUnmounted, ref } from 'vue'
import { computed, defineComponent, onMounted, onUnmounted, Ref, ref } from 'vue'
import { useStore } from 'vuex'
import throttle from 'lodash/throttle'
import { MutationTypes, State } from '@/store'
@ -42,6 +46,7 @@ import { KEYS } from '@/configs/hotkey'
import { ContextmenuItem } from '@/components/Contextmenu/types'
import ScreenSlide from './ScreenSlide.vue'
import { Slide } from '@/types/slides'
export default defineComponent({
name: 'screen',
@ -52,6 +57,7 @@ export default defineComponent({
const store = useStore<State>()
const slides = computed(() => store.state.slides)
const slideIndex = computed(() => store.state.slideIndex)
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
const slideWidth = ref(0)
const slideHeight = ref(0)
@ -83,24 +89,55 @@ export default defineComponent({
if(!isFullscreen()) store.commit(MutationTypes.SET_SCREENING, false)
}
const turnPrevSlide = () => {
if(slideIndex.value <= 0) return
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, slideIndex.value - 1)
const prefix = 'animate__'
const animationIndex = ref(0)
const animations = computed(() => currentSlide.value.animations || [])
const runAnimation = () => {
const animation = animations.value[animationIndex.value]
animationIndex.value += 1
const elRef = document.querySelector(`#screen-element-${animation.elId} [class^=base-element-]`)
if(elRef) {
const animationName = `${prefix}${animation.type}`
elRef.classList.add(`${prefix}animated`, animationName)
const handleAnimationEnd = () => {
elRef.classList.remove(`${prefix}animated`, animationName)
}
elRef.addEventListener('animationend', handleAnimationEnd, { once: true })
}
}
const turnNextSlide = () => {
if(slideIndex.value >= slides.value.length - 1) return
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, slideIndex.value + 1)
const execPrev = () => {
if(animations.value.length && animationIndex.value > 0) {
animationIndex.value -= 1
}
else if(slideIndex.value > 0) {
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, slideIndex.value - 1)
const lastIndex = animations.value ? animations.value.length : 0
animationIndex.value = lastIndex
}
}
const execNext = () => {
if(animations.value.length && animationIndex.value < animations.value.length) {
runAnimation()
}
else if(slideIndex.value < slides.value.length - 1) {
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, slideIndex.value + 1)
animationIndex.value = 0
}
}
const keydownListener = (e: KeyboardEvent) => {
const key = e.key.toUpperCase()
if(key === KEYS.UP || key === KEYS.LEFT) turnPrevSlide()
else if(key === KEYS.DOWN || key === KEYS.RIGHT) turnNextSlide()
if(key === KEYS.UP || key === KEYS.LEFT) execPrev()
else if(key === KEYS.DOWN || key === KEYS.RIGHT) execNext()
}
const mousewheelListener = throttle(function(e: WheelEvent) {
if(e.deltaY > 0) turnNextSlide()
else if(e.deltaY < 0) turnPrevSlide()
if(e.deltaY < 0) execPrev()
else if(e.deltaY > 0) execNext()
}, 500, { leading: true, trailing: false })
onMounted(() => {
@ -112,6 +149,15 @@ export default defineComponent({
document.removeEventListener('keydown', keydownListener)
})
const turnPrevSlide = () => {
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, slideIndex.value - 1)
animationIndex.value = 0
}
const turnNextSlide = () => {
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, slideIndex.value + 1)
animationIndex.value = 0
}
const contextmenus = (): ContextmenuItem[] => {
return [
{
@ -140,6 +186,7 @@ export default defineComponent({
slideHeight,
scale,
mousewheelListener,
animationIndex,
contextmenus,
}
},

View File

@ -18,7 +18,7 @@ import BaseImageElement from './ImageElement/BaseImageElement.vue'
import BaseTextElement from './TextElement/BaseTextElement.vue'
export default defineComponent({
name: 'editable-element',
name: 'base-element',
props: {
elementInfo: {
type: Object as PropType<PPTElement>,

View File

@ -0,0 +1,66 @@
<template>
<div
class="screen-element"
:style="{
zIndex: elementIndex,
visibility: needWaitAnimation ? 'hidden' : 'visible',
}"
>
<component
:is="currentElementComponent"
:elementInfo="elementInfo"
></component>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, Ref } from 'vue'
import { useStore } from 'vuex'
import { State } from '@/store'
import { PPTElement, Slide } from '@/types/slides'
import BaseImageElement from './ImageElement/BaseImageElement.vue'
import BaseTextElement from './TextElement/BaseTextElement.vue'
export default defineComponent({
name: 'screen-element',
props: {
elementInfo: {
type: Object as PropType<PPTElement>,
required: true,
},
elementIndex: {
type: Number,
required: true,
},
animationIndex: {
type: Number,
default: -1,
},
},
setup(props) {
const currentElementComponent = computed(() => {
const elementTypeMap = {
'image': BaseImageElement,
'text': BaseTextElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
const store = useStore<State>()
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
const needWaitAnimation = computed(() => {
const animations = currentSlide.value.animations || []
const elementIndexInAnimation = animations.findIndex(animation => animation.elId === props.elementInfo.id)
if(elementIndexInAnimation !== -1 && elementIndexInAnimation >= props.animationIndex) return true
return false
})
return {
currentElementComponent,
needWaitAnimation,
}
},
})
</script>