mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
update
This commit is contained in:
parent
59cfc3dc6f
commit
dc388ac6ab
348
package-lock.json
generated
348
package-lock.json
generated
@ -2258,6 +2258,122 @@
|
||||
"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=1607912103019&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": {
|
||||
@ -2421,6 +2537,17 @@
|
||||
"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",
|
||||
@ -2483,6 +2610,18 @@
|
||||
"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",
|
||||
@ -2531,6 +2670,18 @@
|
||||
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=",
|
||||
"dev": true
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.1.1",
|
||||
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.1.tgz?cache=0&sync_timestamp=1607093677581&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.1.tgz",
|
||||
"integrity": "sha1-9bKG1grGiGaExjoXoYQ5HMngGZo=",
|
||||
"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",
|
||||
@ -7317,122 +7468,6 @@
|
||||
"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?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"
|
||||
},
|
||||
"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?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
|
||||
}
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz",
|
||||
@ -15868,87 +15903,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.1.1",
|
||||
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.1.tgz?cache=0&sync_timestamp=1607093677581&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.1.tgz",
|
||||
"integrity": "sha1-9bKG1grGiGaExjoXoYQ5HMngGZo=",
|
||||
"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?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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-4.0.1.tgz?cache=0&sync_timestamp=1607347245114&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-4.0.1.tgz",
|
||||
|
139
src/assets/animate/bounce.scss
Normal file
139
src/assets/animate/bounce.scss
Normal file
@ -0,0 +1,139 @@
|
||||
@keyframes bounceIn {
|
||||
from,
|
||||
20%,
|
||||
40%,
|
||||
60%,
|
||||
80%,
|
||||
to {
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale3d(0.3, 0.3, 0.3);
|
||||
}
|
||||
20% {
|
||||
transform: scale3d(1.1, 1.1, 1.1);
|
||||
}
|
||||
40% {
|
||||
transform: scale3d(0.9, 0.9, 0.9);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: scale3d(1.03, 1.03, 1.03);
|
||||
}
|
||||
80% {
|
||||
transform: scale3d(0.97, 0.97, 0.97);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceInDown {
|
||||
from,
|
||||
60%,
|
||||
75%,
|
||||
90%,
|
||||
to {
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, -3000px, 0);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 25px, 0);
|
||||
}
|
||||
75% {
|
||||
transform: translate3d(0, -10px, 0);
|
||||
}
|
||||
90% {
|
||||
transform: translate3d(0, 5px, 0);
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceInLeft {
|
||||
from,
|
||||
60%,
|
||||
75%,
|
||||
90%,
|
||||
to {
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(-3000px, 0, 0);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: translate3d(25px, 0, 0);
|
||||
}
|
||||
75% {
|
||||
transform: translate3d(-10px, 0, 0);
|
||||
}
|
||||
90% {
|
||||
transform: translate3d(5px, 0, 0);
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceInRight {
|
||||
from,
|
||||
60%,
|
||||
75%,
|
||||
90%,
|
||||
to {
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(3000px, 0, 0);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-25px, 0, 0);
|
||||
}
|
||||
75% {
|
||||
transform: translate3d(10px, 0, 0);
|
||||
}
|
||||
90% {
|
||||
transform: translate3d(-5px, 0, 0);
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceInUp {
|
||||
from,
|
||||
60%,
|
||||
75%,
|
||||
90%,
|
||||
to {
|
||||
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 3000px, 0);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
75% {
|
||||
transform: translate3d(0, 10px, 0);
|
||||
}
|
||||
90% {
|
||||
transform: translate3d(0, -5px, 0);
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
52
src/assets/animate/fade.scss
Normal file
52
src/assets/animate/fade.scss
Normal file
@ -0,0 +1,52 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
43
src/assets/animate/flip.scss
Normal file
43
src/assets/animate/flip.scss
Normal file
@ -0,0 +1,43 @@
|
||||
@keyframes flipInX {
|
||||
from {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
|
||||
animation-timing-function: ease-in;
|
||||
opacity: 0;
|
||||
}
|
||||
40% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
60% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
|
||||
}
|
||||
to {
|
||||
transform: perspective(400px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flipInY {
|
||||
from {
|
||||
transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
|
||||
animation-timing-function: ease-in;
|
||||
opacity: 0;
|
||||
}
|
||||
40% {
|
||||
transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
60% {
|
||||
transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
|
||||
}
|
||||
to {
|
||||
transform: perspective(400px);
|
||||
}
|
||||
}
|
103
src/assets/animate/main.scss
Normal file
103
src/assets/animate/main.scss
Normal file
@ -0,0 +1,103 @@
|
||||
@import './bounce.scss';
|
||||
@import './fade.scss';
|
||||
@import './flip.scss';
|
||||
@import './rotate.scss';
|
||||
@import './slide.scss';
|
||||
@import './zoom.scss';
|
||||
|
||||
.animate {
|
||||
animation-duration: 0ms;
|
||||
|
||||
&.duration-500 {
|
||||
animation-duration: 500ms;
|
||||
}
|
||||
&.duration-1000 {
|
||||
animation-duration: 1000ms;
|
||||
}
|
||||
&.duration-1500 {
|
||||
animation-duration: 1500ms;
|
||||
}
|
||||
&.duration-2000 {
|
||||
animation-duration: 2000ms;
|
||||
}
|
||||
|
||||
&.bounceIn {
|
||||
animation-name: bounceIn;
|
||||
}
|
||||
&.bounceInDown {
|
||||
animation-name: bounceInDown;
|
||||
}
|
||||
&.bounceInLeft {
|
||||
animation-name: bounceInLeft;
|
||||
}
|
||||
&.bounceInRight {
|
||||
animation-name: bounceInRight;
|
||||
}
|
||||
&.bounceInUp {
|
||||
animation-name: bounceInUp;
|
||||
}
|
||||
&.fadeIn {
|
||||
animation-name: fadeIn;
|
||||
}
|
||||
&.fadeInDown {
|
||||
animation-name: fadeInDown;
|
||||
}
|
||||
&.fadeInLeft {
|
||||
animation-name: fadeInLeft;
|
||||
}
|
||||
&.fadeInRight {
|
||||
animation-name: fadeInRight;
|
||||
}
|
||||
&.fadeInUp {
|
||||
animation-name: fadeInUp;
|
||||
}
|
||||
&.flipInX {
|
||||
animation-name: flipInX;
|
||||
}
|
||||
&.flipInY {
|
||||
animation-name: flipInY;
|
||||
}
|
||||
&.rotateIn {
|
||||
animation-name: rotateIn;
|
||||
}
|
||||
&.rotateInDownLeft {
|
||||
animation-name: rotateInDownLeft;
|
||||
}
|
||||
&.rotateInDownRight {
|
||||
animation-name: rotateInDownRight;
|
||||
}
|
||||
&.rotateInUpLeft {
|
||||
animation-name: rotateInUpLeft;
|
||||
}
|
||||
&.rotateInUpRight {
|
||||
animation-name: rotateInUpRight;
|
||||
}
|
||||
&.slideInDown {
|
||||
animation-name: slideInDown;
|
||||
}
|
||||
&.slideInLeft {
|
||||
animation-name: slideInLeft;
|
||||
}
|
||||
&.slideInRight {
|
||||
animation-name: slideInRight;
|
||||
}
|
||||
&.slideInUp {
|
||||
animation-name: slideInUp;
|
||||
}
|
||||
&.zoomIn {
|
||||
animation-name: zoomIn;
|
||||
}
|
||||
&.zoomInDown {
|
||||
animation-name: zoomInDown;
|
||||
}
|
||||
&.zoomInLeft {
|
||||
animation-name: zoomInLeft;
|
||||
}
|
||||
&.zoomInRight {
|
||||
animation-name: zoomInRight;
|
||||
}
|
||||
&.zoomInUp {
|
||||
animation-name: zoomInUp;
|
||||
}
|
||||
}
|
||||
|
64
src/assets/animate/rotate.scss
Normal file
64
src/assets/animate/rotate.scss
Normal file
@ -0,0 +1,64 @@
|
||||
@keyframes rotateIn {
|
||||
from {
|
||||
transform-origin: center;
|
||||
transform: rotate3d(0, 0, 1, -200deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform-origin: center;
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateInDownLeft {
|
||||
from {
|
||||
transform-origin: left bottom;
|
||||
transform: rotate3d(0, 0, 1, -45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform-origin: left bottom;
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateInDownRight {
|
||||
from {
|
||||
transform-origin: right bottom;
|
||||
transform: rotate3d(0, 0, 1, 45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform-origin: right bottom;
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateInUpLeft {
|
||||
from {
|
||||
transform-origin: left bottom;
|
||||
transform: rotate3d(0, 0, 1, 45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform-origin: left bottom;
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateInUpRight {
|
||||
from {
|
||||
transform-origin: right bottom;
|
||||
transform: rotate3d(0, 0, 1, -90deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform-origin: right bottom;
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
39
src/assets/animate/slide.scss
Normal file
39
src/assets/animate/slide.scss
Normal file
@ -0,0 +1,39 @@
|
||||
@keyframes slideInDown {
|
||||
from {
|
||||
transform: translate3d(0, -100%, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translate3d(100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInUp {
|
||||
from {
|
||||
transform: translate3d(0, 100%, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
61
src/assets/animate/zoom.scss
Normal file
61
src/assets/animate/zoom.scss
Normal file
@ -0,0 +1,61 @@
|
||||
@keyframes zoomIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale3d(0.3, 0.3, 0.3);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoomInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
|
||||
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
|
||||
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoomInLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
|
||||
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
|
||||
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoomInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
|
||||
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
|
||||
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoomInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
|
||||
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
|
||||
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
|
||||
}
|
||||
}
|
192
src/views/_common/_element/EditableElement.vue
Normal file
192
src/views/_common/_element/EditableElement.vue
Normal file
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div
|
||||
class="editable-element"
|
||||
:id="'editable-element-' + elementInfo.elId"
|
||||
:style="{ zIndex: elementIndex }"
|
||||
>
|
||||
<component
|
||||
:is="currentElementComponent"
|
||||
:elementInfo="elementInfo"
|
||||
:canvasScale="canvasScale"
|
||||
:isActive="isActive"
|
||||
:isHandleEl="isHandleEl"
|
||||
:isMultiSelect="isMultiSelect"
|
||||
:selectElement="selectElement"
|
||||
:rotateElement="rotateElement"
|
||||
:scaleElement="scaleElement"
|
||||
:contextmenus="contextmenus"
|
||||
></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
|
||||
import ImageElement from './ImageElement.index.vue'
|
||||
import TextElement from './TextElement.index.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editable-element',
|
||||
props: {
|
||||
elementInfo: {
|
||||
type: Object as PropType<PPTElement>,
|
||||
required: true,
|
||||
},
|
||||
elementIndex: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
canvasScale: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isHandleEl: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isMultiSelect: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
selectElement: {
|
||||
type: Function as PropType<(e: MouseEvent, element: PPTElement, canMove: boolean) => void>,
|
||||
required: true,
|
||||
},
|
||||
rotateElement: {
|
||||
type: Function as PropType<(element: PPTElement) => void>,
|
||||
required: true,
|
||||
},
|
||||
scaleElement: {
|
||||
type: Function as PropType<(e: MouseEvent, element: PPTElement, direction: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) => void>,
|
||||
required: true,
|
||||
},
|
||||
updateZIndex: {
|
||||
type: Function as PropType<(element: PPTElement, operation: 'up' | 'down' | 'top' | 'bottom') => void>,
|
||||
required: true,
|
||||
},
|
||||
combineElements: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
uncombineElements: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
alignElement: {
|
||||
type: Function as PropType<(direction: 'top' | 'verticalCenter' | 'bottom' | 'left' | 'horizontalCenter' | 'right') => void>,
|
||||
required: true,
|
||||
},
|
||||
deleteElement: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
lockElement: {
|
||||
type: Function as PropType<(element: PPTElement, handle: 'lock' | 'unlock') => void>,
|
||||
required: true,
|
||||
},
|
||||
copyElement: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
cutElement: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const currentElementComponent = computed(() => {
|
||||
const elementTypeMap = {
|
||||
'image': ImageElement,
|
||||
'text': TextElement,
|
||||
}
|
||||
return elementTypeMap[props.elementInfo.type] || null
|
||||
})
|
||||
|
||||
const contextmenus = () => {
|
||||
if(props.elementInfo.isLock) {
|
||||
return [{
|
||||
text: '解锁',
|
||||
icon: 'icon-unlock',
|
||||
action: () => props.lockElement(props.elementInfo, 'unlock'),
|
||||
}]
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
text: '剪切',
|
||||
subText: 'Ctrl + X',
|
||||
icon: 'icon-scissor',
|
||||
action: props.cutElement,
|
||||
},
|
||||
{
|
||||
text: '复制',
|
||||
subText: 'Ctrl + C',
|
||||
icon: 'icon-copy',
|
||||
action: props.copyElement,
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
text: '层级',
|
||||
icon: 'icon-top-layer',
|
||||
disable: props.isMultiSelect && !props.elementInfo.groupId,
|
||||
children: [
|
||||
{ text: '置顶层', action: () => props.updateZIndex(props.elementInfo, 'top') },
|
||||
{ text: '置底层', action: () => props.updateZIndex(props.elementInfo, 'bottom') },
|
||||
{ divider: true },
|
||||
{ text: '上移一层', action: () => props.updateZIndex(props.elementInfo, 'up') },
|
||||
{ text: '下移一层', action: () => props.updateZIndex(props.elementInfo, 'down') },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '水平对齐',
|
||||
icon: 'icon-align-left',
|
||||
children: [
|
||||
{ text: '水平居中', action: () => props.alignElement('horizontalCenter') },
|
||||
{ text: '左对齐', action: () => props.alignElement('left') },
|
||||
{ text: '右对齐', action: () => props.alignElement('right') },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '垂直对齐',
|
||||
icon: 'icon-align-bottom',
|
||||
children: [
|
||||
{ text: '垂直居中', action: () => props.alignElement('verticalCenter') },
|
||||
{ text: '上对齐', action: () => props.alignElement('top') },
|
||||
{ text: '下对齐', action: () => props.alignElement('bottom') },
|
||||
],
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
text: props.elementInfo.groupId ? '取消组合' : '组合',
|
||||
subText: 'Ctrl + G',
|
||||
icon: 'icon-block',
|
||||
action: props.elementInfo.groupId ? props.uncombineElements : props.combineElements,
|
||||
hide: !props.isMultiSelect,
|
||||
},
|
||||
{
|
||||
text: '锁定',
|
||||
subText: 'Ctrl + L',
|
||||
icon: 'icon-lock',
|
||||
action: () => props.lockElement(props.elementInfo, 'lock'),
|
||||
},
|
||||
{
|
||||
text: '删除',
|
||||
subText: 'Delete',
|
||||
icon: 'icon-delete',
|
||||
action: () => props.deleteElement(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
currentElementComponent,
|
||||
contextmenus,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
54
src/views/_common/_element/ElementBorder.vue
Normal file
54
src/views/_common/_element/ElementBorder.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<SvgWrapper
|
||||
class="element-border"
|
||||
overflow="visible"
|
||||
:width="width"
|
||||
:height="height"
|
||||
>
|
||||
<path
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-linecap="butt"
|
||||
stroke-miterlimit="8"
|
||||
stroke-linejoin
|
||||
fill="transparent"
|
||||
:d="`M0,0 L${width},0 L${width},${height} L0,${height} Z`"
|
||||
:stroke="borderColor"
|
||||
:stroke-width="borderWidth"
|
||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
||||
></path>
|
||||
</SvgWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'element-border',
|
||||
props: {
|
||||
width: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
},
|
||||
borderWidth: {
|
||||
type: Number,
|
||||
},
|
||||
borderStyle: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
svg {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
499
src/views/_common/_element/ImageElement/ImageClipHandler.vue
Normal file
499
src/views/_common/_element/ImageElement/ImageClipHandler.vue
Normal file
@ -0,0 +1,499 @@
|
||||
<template>
|
||||
<div
|
||||
class="image-clip-handler"
|
||||
:style="clipWrapperPositionStyle"
|
||||
v-click-outside="clip"
|
||||
>
|
||||
<img
|
||||
class="bottom-img"
|
||||
:src="imgUrl"
|
||||
:draggable="false"
|
||||
alt=""
|
||||
:style="bottomImgPositionStyle"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="top-img-wrapper"
|
||||
:style="{
|
||||
...topImgWrapperPositionStyle,
|
||||
clipPath: clipPath,
|
||||
}"
|
||||
>
|
||||
<img
|
||||
class="top-img"
|
||||
:src="imgUrl"
|
||||
:draggable="false"
|
||||
alt=""
|
||||
:style="topImgPositionStyle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="operate"
|
||||
:style="topImgWrapperPositionStyle"
|
||||
@mousedown.stop="$event => moveClipRange($event)"
|
||||
>
|
||||
<div
|
||||
:class="['clip-point', point]"
|
||||
v-for="point in ['t-l', 't-r', 'b-l', 'b-r']"
|
||||
:key="point"
|
||||
@mousedown.stop="$event => scaleClipRange($event, point)"
|
||||
>
|
||||
<SvgWrapper width="12" height="12" fill="#fff" stroke="#666">
|
||||
<path d="M 12 0 L 0 0 L 0 12 L 3 12 L 3 3 L 12 3 L 12 0 Z"></path>
|
||||
</SvgWrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
|
||||
import { KEYCODE } from '@/configs/keyCode'
|
||||
|
||||
type ClipDataRange = [[number, number], [number, number]]
|
||||
|
||||
interface ClipData {
|
||||
range: ClipDataRange;
|
||||
path: string;
|
||||
}
|
||||
|
||||
type ScaleClipRangeType = 't-l' | 't-r' | 'b-l' | 'b-r'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'image-clip-handler',
|
||||
props: {
|
||||
imgUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
clipData: {
|
||||
type: Object as PropType<ClipData>,
|
||||
required: true,
|
||||
},
|
||||
clipPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
canvasScale: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
top: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
left: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const topImgWrapperPosition = reactive({
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
})
|
||||
const clipWrapperPositionStyle = reactive({
|
||||
top: '0',
|
||||
left: '0',
|
||||
})
|
||||
const isSettingClipRange = ref(false)
|
||||
const currentRange = ref<ClipDataRange | null>(null)
|
||||
|
||||
const getClipDataTransformInfo = () => {
|
||||
const [start, end] = props.clipData ? props.clipData.range : [[0, 0], [100, 100]]
|
||||
|
||||
const widthScale = (end[0] - start[0]) / 100
|
||||
const heightScale = (end[1] - start[1]) / 100
|
||||
const left = start[0] / widthScale
|
||||
const top = start[1] / heightScale
|
||||
|
||||
return { widthScale, heightScale, left, top }
|
||||
}
|
||||
|
||||
const imgPosition = computed(() => {
|
||||
const { widthScale, heightScale, left, top } = getClipDataTransformInfo()
|
||||
return {
|
||||
left: -left,
|
||||
top: -top,
|
||||
width: 100 / widthScale,
|
||||
height: 100 / heightScale,
|
||||
}
|
||||
})
|
||||
|
||||
const bottomImgPositionStyle = computed(() => {
|
||||
return {
|
||||
top: imgPosition.value.top + '%',
|
||||
left: imgPosition.value.left + '%',
|
||||
width: imgPosition.value.width + '%',
|
||||
height: imgPosition.value.height + '%',
|
||||
}
|
||||
})
|
||||
|
||||
const topImgWrapperPositionStyle = computed(() => {
|
||||
return {
|
||||
top: topImgWrapperPosition.top + '%',
|
||||
left: topImgWrapperPosition.left + '%',
|
||||
width: topImgWrapperPosition.width + '%',
|
||||
height: topImgWrapperPosition.height + '%',
|
||||
}
|
||||
})
|
||||
|
||||
const topImgPositionStyle = computed(() => {
|
||||
const bottomWidth = imgPosition.value.width
|
||||
const bottomHeight = imgPosition.value.height
|
||||
|
||||
const topLeft = topImgWrapperPosition.left
|
||||
const topTop = topImgWrapperPosition.top
|
||||
const topWidth = topImgWrapperPosition.width
|
||||
const topHeight = topImgWrapperPosition.height
|
||||
|
||||
return {
|
||||
left: -topLeft * (100 / topWidth) + '%',
|
||||
top: -topTop * (100 / topHeight) + '%',
|
||||
width: bottomWidth / topWidth * 100 + '%',
|
||||
height: bottomHeight / topHeight * 100 + '%',
|
||||
}
|
||||
})
|
||||
|
||||
const initClipPosition = () => {
|
||||
const { left, top } = getClipDataTransformInfo()
|
||||
topImgWrapperPosition.left = left
|
||||
topImgWrapperPosition.top = top
|
||||
topImgWrapperPosition.width = 100
|
||||
topImgWrapperPosition.height = 100
|
||||
|
||||
clipWrapperPositionStyle.top = -top + '%'
|
||||
clipWrapperPositionStyle.left = -left + '%'
|
||||
}
|
||||
|
||||
const clip = () => {
|
||||
if(isSettingClipRange.value) return
|
||||
|
||||
if(!currentRange.value) {
|
||||
emit('clip', null)
|
||||
return
|
||||
}
|
||||
|
||||
const { left, top } = getClipDataTransformInfo()
|
||||
|
||||
const position = {
|
||||
left: (topImgWrapperPosition.left - left) / 100 * props.width,
|
||||
top: (topImgWrapperPosition.top - top) / 100 * props.height,
|
||||
width: (topImgWrapperPosition.width - 100) / 100 * props.width,
|
||||
height: (topImgWrapperPosition.height - 100) / 100 * props.height,
|
||||
}
|
||||
emit('clip', {
|
||||
range: currentRange.value,
|
||||
position,
|
||||
})
|
||||
}
|
||||
|
||||
const keyboardClip = (e: KeyboardEvent) => {
|
||||
if(e.keyCode === KEYCODE.ENTER) clip()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initClipPosition()
|
||||
document.addEventListener('keydown', keyboardClip)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', keyboardClip)
|
||||
})
|
||||
|
||||
const getRange = () => {
|
||||
const retPosition = {
|
||||
left: parseInt(topImgPositionStyle.value.left),
|
||||
top: parseInt(topImgPositionStyle.value.top),
|
||||
width: parseInt(topImgPositionStyle.value.width),
|
||||
height: parseInt(topImgPositionStyle.value.height),
|
||||
}
|
||||
|
||||
const widthScale = 100 / retPosition.width
|
||||
const heightScale = 100 / retPosition.height
|
||||
|
||||
const start: [number, number] = [
|
||||
-retPosition.left * widthScale,
|
||||
-retPosition.top * heightScale,
|
||||
]
|
||||
const end: [number, number] = [
|
||||
widthScale * 100 + start[0],
|
||||
heightScale * 100 + start[1],
|
||||
]
|
||||
|
||||
currentRange.value = [start, end]
|
||||
}
|
||||
|
||||
const moveClipRange = (e: MouseEvent) => {
|
||||
isSettingClipRange.value = true
|
||||
let isMouseDown = true
|
||||
|
||||
const startPageX = e.pageX
|
||||
const startPageY = e.pageY
|
||||
const bottomPosition = imgPosition.value
|
||||
const originPositopn = {
|
||||
left: topImgWrapperPosition.left,
|
||||
top: topImgWrapperPosition.top,
|
||||
width: topImgWrapperPosition.width,
|
||||
height: topImgWrapperPosition.height,
|
||||
}
|
||||
|
||||
document.onmousemove = e => {
|
||||
if(!isMouseDown) return
|
||||
|
||||
const currentPageX = e.pageX
|
||||
const currentPageY = e.pageY
|
||||
|
||||
const moveX = (currentPageX - startPageX) / props.canvasScale / props.width * 100
|
||||
const moveY = (currentPageY - startPageY) / props.canvasScale / props.height * 100
|
||||
|
||||
let targetLeft = originPositopn.left + moveX
|
||||
let targetTop = originPositopn.top + moveY
|
||||
|
||||
// 范围限制
|
||||
if(targetLeft < 0) targetLeft = 0
|
||||
else if(targetLeft + originPositopn.width > bottomPosition.width) {
|
||||
targetLeft = bottomPosition.width - originPositopn.width
|
||||
}
|
||||
if(targetTop < 0) targetTop = 0
|
||||
else if(targetTop + originPositopn.height > bottomPosition.height) {
|
||||
targetTop = bottomPosition.height - originPositopn.height
|
||||
}
|
||||
|
||||
topImgWrapperPosition.left = targetLeft
|
||||
topImgWrapperPosition.top = targetTop
|
||||
}
|
||||
|
||||
document.onmouseup = () => {
|
||||
isMouseDown = false
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
|
||||
getRange()
|
||||
|
||||
setTimeout(() => {
|
||||
isSettingClipRange.value = false
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
const scaleClipRange = (e: MouseEvent, type: ScaleClipRangeType) => {
|
||||
isSettingClipRange.value = true
|
||||
let isMouseDown = true
|
||||
|
||||
const minWidth = 32 / props.width * 100
|
||||
const minHeight = 32 / props.height * 100
|
||||
|
||||
const startPageX = e.pageX
|
||||
const startPageY = e.pageY
|
||||
const bottomPosition = imgPosition.value
|
||||
const originPositopn = {
|
||||
left: topImgWrapperPosition.left,
|
||||
top: topImgWrapperPosition.top,
|
||||
width: topImgWrapperPosition.width,
|
||||
height: topImgWrapperPosition.height,
|
||||
}
|
||||
|
||||
document.onmousemove = e => {
|
||||
if(!isMouseDown) return
|
||||
|
||||
const currentPageX = e.pageX
|
||||
const currentPageY = e.pageY
|
||||
|
||||
let moveX = (currentPageX - startPageX) / props.canvasScale / props.width * 100
|
||||
let moveY = (currentPageY - startPageY) / props.canvasScale / props.height * 100
|
||||
|
||||
let targetLeft, targetTop, targetWidth, targetHeight
|
||||
|
||||
// 根据不同缩放点,计算目标大小和位置,同时做大小和范围的限制
|
||||
if(type === 't-l') {
|
||||
if(originPositopn.left + moveX < 0) {
|
||||
moveX = -originPositopn.left
|
||||
}
|
||||
if(originPositopn.top + moveY < 0) {
|
||||
moveY = -originPositopn.top
|
||||
}
|
||||
if(originPositopn.width - moveX < minWidth) {
|
||||
moveX = originPositopn.width - minWidth
|
||||
}
|
||||
if(originPositopn.height - moveY < minHeight) {
|
||||
moveY = originPositopn.height - minHeight
|
||||
}
|
||||
targetWidth = originPositopn.width - moveX
|
||||
targetHeight = originPositopn.height - moveY
|
||||
targetLeft = originPositopn.left + moveX
|
||||
targetTop = originPositopn.top + moveY
|
||||
}
|
||||
else if(type === 't-r') {
|
||||
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
|
||||
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
|
||||
}
|
||||
if(originPositopn.top + moveY < 0) {
|
||||
moveY = -originPositopn.top
|
||||
}
|
||||
if(originPositopn.width + moveX < minWidth) {
|
||||
moveX = minWidth - originPositopn.width
|
||||
}
|
||||
if(originPositopn.height - moveY < minHeight) {
|
||||
moveY = originPositopn.height - minHeight
|
||||
}
|
||||
targetWidth = originPositopn.width + moveX
|
||||
targetHeight = originPositopn.height - moveY
|
||||
targetLeft = originPositopn.left
|
||||
targetTop = originPositopn.top + moveY
|
||||
}
|
||||
else if(type === 'b-l') {
|
||||
if(originPositopn.left + moveX < 0) {
|
||||
moveX = -originPositopn.left
|
||||
}
|
||||
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
|
||||
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
|
||||
}
|
||||
if(originPositopn.width - moveX < minWidth) {
|
||||
moveX = originPositopn.width - minWidth
|
||||
}
|
||||
if(originPositopn.height + moveY < minHeight) {
|
||||
moveY = minHeight - originPositopn.height
|
||||
}
|
||||
targetWidth = originPositopn.width - moveX
|
||||
targetHeight = originPositopn.height + moveY
|
||||
targetLeft = originPositopn.left + moveX
|
||||
targetTop = originPositopn.top
|
||||
}
|
||||
else {
|
||||
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
|
||||
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
|
||||
}
|
||||
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
|
||||
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
|
||||
}
|
||||
if(originPositopn.width + moveX < minWidth) {
|
||||
moveX = minWidth - originPositopn.width
|
||||
}
|
||||
if(originPositopn.height + moveY < minHeight) {
|
||||
moveY = minHeight - originPositopn.height
|
||||
}
|
||||
targetWidth = originPositopn.width + moveX
|
||||
targetHeight = originPositopn.height + moveY
|
||||
targetLeft = originPositopn.left
|
||||
targetTop = originPositopn.top
|
||||
}
|
||||
|
||||
topImgWrapperPosition.left = targetLeft
|
||||
topImgWrapperPosition.top = targetTop
|
||||
topImgWrapperPosition.width = targetWidth
|
||||
topImgWrapperPosition.height = targetHeight
|
||||
}
|
||||
|
||||
document.onmouseup = () => {
|
||||
isMouseDown = false
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
|
||||
getRange()
|
||||
|
||||
setTimeout(() => isSettingClipRange.value = false, 0)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
clipWrapperPositionStyle,
|
||||
bottomImgPositionStyle,
|
||||
topImgWrapperPositionStyle,
|
||||
topImgPositionStyle,
|
||||
clip,
|
||||
moveClipRange,
|
||||
scaleClipRange,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.image-clip-handler {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.bottom-img {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.top-img-wrapper {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operate {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.clip-point {
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform-origin: 0 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&.t-l {
|
||||
cursor: nwse-resize;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
&.t-r {
|
||||
cursor: nesw-resize;
|
||||
left: 100%;
|
||||
top: 0;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
&.b-l {
|
||||
cursor: nesw-resize;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
&.b-r {
|
||||
cursor: nwse-resize;
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<SvgWrapper
|
||||
class="image-ellipse-border"
|
||||
overflow="visible"
|
||||
:width="width"
|
||||
:height="height"
|
||||
>
|
||||
<ellipse
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-linecap="butt"
|
||||
stroke-miterlimit="8"
|
||||
stroke-linejoin
|
||||
fill="transparent"
|
||||
:cx="width / 2"
|
||||
:cy="height / 2"
|
||||
:rx="width / 2"
|
||||
:ry="height / 2"
|
||||
:stroke="borderColor"
|
||||
:stroke-width="borderWidth"
|
||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
||||
></ellipse>
|
||||
</SvgWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'image-ellipse-border',
|
||||
props: {
|
||||
width: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
borderWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
borderStyle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
svg {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<SvgWrapper
|
||||
class="image-polygon-border"
|
||||
overflow="visible"
|
||||
:width="width"
|
||||
:height="height"
|
||||
>
|
||||
<path
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-linecap="butt"
|
||||
stroke-miterlimit="8"
|
||||
stroke-linejoin
|
||||
fill="transparent"
|
||||
:d="createPath(width, height)"
|
||||
:stroke="borderColor"
|
||||
:stroke-width="borderWidth"
|
||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
||||
></path>
|
||||
</SvgWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'image-polygon-border',
|
||||
props: {
|
||||
width: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
borderWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
borderStyle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
createPath: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
svg {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
65
src/views/_common/_element/ImageElement/ImageRectBorder.vue
Normal file
65
src/views/_common/_element/ImageElement/ImageRectBorder.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<SvgWrapper
|
||||
class="image-rect-border"
|
||||
overflow="visible"
|
||||
:width="width"
|
||||
:height="height"
|
||||
>
|
||||
<rect
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-linecap="butt"
|
||||
stroke-miterlimit="8"
|
||||
stroke-linejoin
|
||||
fill="transparent"
|
||||
:rx="radius"
|
||||
:ry="radius"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:stroke="borderColor"
|
||||
:stroke-width="borderWidth"
|
||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
||||
></rect>
|
||||
</SvgWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'image-rect-border',
|
||||
props: {
|
||||
width: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
borderWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
borderStyle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
radius: {
|
||||
type: String,
|
||||
default: '0',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
svg {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user