diff --git a/backend/src/main/java/io/dataease/controller/panel/api/ViewApi.java b/backend/src/main/java/io/dataease/controller/panel/api/ViewApi.java index 59d1edede9..61016f3dab 100644 --- a/backend/src/main/java/io/dataease/controller/panel/api/ViewApi.java +++ b/backend/src/main/java/io/dataease/controller/panel/api/ViewApi.java @@ -18,4 +18,10 @@ public interface ViewApi { @ApiOperation("视图树") @PostMapping("/tree") List tree(BaseGridRequest request); + + + + + + } diff --git a/frontend/package.json b/frontend/package.json index 0de68084eb..224186a09c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" }, "dependencies": { + "core-js": "^3.6.5", "@riophae/vue-treeselect": "0.4.0", "axios": "^0.21.1", "echarts": "^5.0.2", @@ -41,14 +42,17 @@ "devDependencies": { "@babel/core": "^7.4.0-0", "@babel/register": "7.0.0", - "@vue/cli-plugin-babel": "3.6.0", - "@vue/cli-plugin-eslint": "^3.9.1", - "@vue/cli-service": "3.6.0", - "babel-eslint": "10.0.1", + "@vue/cli-plugin-babel": "~4.5.0", + "@vue/cli-plugin-eslint": "~4.5.0", + "@vue/cli-plugin-router": "~4.5.0", + "@vue/cli-plugin-vuex": "~4.5.0", + "@vue/cli-service": "~4.5.0", + "babel-eslint": "10.1.0", "chalk": "2.4.2", "connect": "3.6.6", - "eslint": "5.15.3", - "eslint-plugin-vue": "5.2.2", + "eslint": "^6.7.2", + "eslint-plugin-vue": "^6.2.2", + "eslint-plugin-import": "^2.20.2", "html-webpack-plugin": "3.2.0", "less": "^4.1.1", "less-loader": "^8.0.0", diff --git a/frontend/src/assets/iconfont/demo.css b/frontend/src/assets/iconfont/demo.css new file mode 100644 index 0000000000..a67054a0a0 --- /dev/null +++ b/frontend/src/assets/iconfont/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/frontend/src/assets/iconfont/demo_index.html b/frontend/src/assets/iconfont/demo_index.html new file mode 100644 index 0000000000..70918ca7c4 --- /dev/null +++ b/frontend/src/assets/iconfont/demo_index.html @@ -0,0 +1,307 @@ + + + + + IconFont Demo + + + + + + + + + + + +
+

+ +
+
+
    + +
  • + +
    向右旋转
    +
    
    +
  • + +
  • + +
    图片
    +
    
    +
  • + +
  • + +
    +
    
    +
  • + +
  • + +
    矩形
    +
    
    +
  • + +
  • + +
    文本
    +
    
    +
  • + +
  • + +
    按钮
    +
    
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 兼容性最好,支持 IE6+,及所有现代浏览器。
  • +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
  • +
+
+

注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.eot');
+  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
+      url('iconfont.woff2') format('woff2'),
+      url('iconfont.woff') format('woff'),
+      url('iconfont.ttf') format('truetype');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + 向右旋转 +
    +
    .icon-xiangyouxuanzhuan +
    +
  • + +
  • + +
    + 图片 +
    +
    .icon-tupian +
    +
  • + +
  • + +
    + 锁 +
    +
    .icon-suo +
    +
  • + +
  • + +
    + 矩形 +
    +
    .icon-juxing +
    +
  • + +
  • + +
    + 文本 +
    +
    .icon-wenben +
    +
  • + +
  • + +
    + 按钮 +
    +
    .icon-button +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 兼容性良好,支持 IE8+,及所有现代浏览器。
  • +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    向右旋转
    +
    #icon-xiangyouxuanzhuan
    +
  • + +
  • + +
    图片
    +
    #icon-tupian
    +
  • + +
  • + +
    +
    #icon-suo
    +
  • + +
  • + +
    矩形
    +
    #icon-juxing
    +
  • + +
  • + +
    文本
    +
    #icon-wenben
    +
  • + +
  • + +
    按钮
    +
    #icon-button
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/frontend/src/assets/iconfont/iconfont.css b/frontend/src/assets/iconfont/iconfont.css new file mode 100644 index 0000000000..1e730fb6d0 --- /dev/null +++ b/frontend/src/assets/iconfont/iconfont.css @@ -0,0 +1,40 @@ +@font-face {font-family: "iconfont"; + src: url('iconfont.eot?t=1613282476380'); /* IE9 */ + src: url('iconfont.eot?t=1613282476380#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAZ4AAsAAAAAC4gAAAYrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDXgqJUIgMATYCJAMcCxAABCAFhG0HZRvpCVGUT1KO7AtsG/ZEEGZ3PDfaDAJAgQcmFqBIigUWiiI8AiAe/tvvf/vMzJVn2j7mifU6urpJNelEkiW80mmEJhYiyZo3Qvl/6nxfgFi/BKGxwByQHcd2lJPljzDSVgCceEMegddu3XAIT7tLO+RSaJSLPez0oYkqnu0TgIkcaAHSAf1bbX7eTf//38/VidWFpretrReT9//MLIkBIXFIGhIhmXgjQrPSqJVFPJNdqjAo343DCXRtMIXm6KIVG8CsYMsF4pbAF8CcMSoNzNBq6opNi3jMoE0P03Pgkf39+Af7w0xSZWzVY1cWlmHOz2Ajuu51EnZoAun27BhuRcY+oBD3Kp136bxqn0Xdqr+pdwoYaiX1z6Al1qmijfz/T7aZjQ5DrqdjkUrI+/rn1Xqoj9RzqZqfQckn8XOJpApbh3oVYlXUC8Qo8sn8GpE9XajRmwyxEcQLEP8xfGWld2GXSTFZc9tdY3xevb4/GOs2W4LNau4KIZu93+2PadM8AP/LtQ1XrmDOzY3Xr29G3rbp2jX8LLc3X726kb+FNSjRV62SerBvyI3Uqs8tPX614diVcMW1USevTz3YXq41paRFQ7Cg4noIddOwWaZLqZBbMZukQFsmWV9IaqUXFhHMjBJIERcj/kuZZaMEdF+2AKSNyICwpUeE4G5Eu2UCBEU7W2sLlWXYMqaym4raylp5tK3bOrRE8TQaGNna2K5JEXfMybhooK+8gJwHLxkaai4Tsssr5FHJIo1dMLRUZtOcQmSMge5z5k1tMPeYRoREiIwOcTPi29vRKhG3I0EXEhlS0O2HhbwijSLQPaFRSGRYtUBxu6VlaG8HmMZRi3gbaQ4Lq1Ut65pQXIHKs2vC4u0W9AACCBJFoxhA/B7keQgQop7A2wESNigFWmWqFAhRD6GiaO6fNByWXZG15F7MIakBVikRUsQyS8aleedaUmrBbJH7c4JfZ8wMShqZshQMGX7dNgSP/TMf1Xc4tdFvnn3grrPzttKyOQqFZ5gpf6NyhzpX80afmLlzge/AZJHxLejV3kW7WF0YR4N2rk6JiODxFkYmB0uRfauIgwq6sqGyF684cuKgohVwL8FBD4Hbg5DNvSGMs9aFrzfXCvTOBs4t4wZTwqpLOo9hc2Hrz+Jq74/D72sfZqDVP28JhxMSs0rmc7vu+G+FCVfmxKicNOcKjDmfrsHm3ds7D5ub9O6xebxdb65UXblizWh/sRV8an8QSud9feWC2TW+fIMLIfQ6pvnrFAYsFX7xiHWzWpvaxl3tdr626Hb8dPgSkuR2wq5waS5497VimqjHnWiYe0PbtCvAIvPBlOH+2nPMkrT898SGaCh+TFilgy25/X6Z+SoDjcOgUvT0wCudsSPT9+NzttrvMtJcEoZ9zcQOx1FpujKSH6XX4KALz1Nwf/+gWaJWgBSSGKqzymYM3FyDewUTKRXsDXofN5EYxivNHiWsPe2gXsNaYb3CVKKjjOf18xJ2a5uzNthu9tRU7cA6zQ5TJ5H6Bof3drXaOgfzH3rvdS7ga1/pmzkv4dfVR6znpt9MnrN6q4BH8Lm3e5t+eO/2Wr4+7IFfu703XDqdAOsy3V3dPGC36iFfuJpc15/nApDfpF+sX9Zdz3MAyB+1L/8qi/kx7U27XTMyE5+GcV9X7VIi347k+44hS/8YUuKQspAzU1NmnaWEyG18hskXErq6+Dt4pz5tD1+Qkf8evoiQNNYga20jFuo+qPSsoNY6AF17Ld66Z4bSidLBHgMAYewqJENvIBt7QSzUj1BZ+gm1ceig60z4dtmzIwTrfIoLDJdRsQUJJVLjhIVZXeU6zKtSgabllrUJU0WIoFw6m88sxjVMt7GG0sjnGeMQR4mMFsHRsCQRVKekikssXWGsPi2T4aoeKF0iMlgdQWEFDFaGFLVABCVEDafHZqyFn18H46kkBbSh7VxwE4xSCDNHctKyBOJiVCO1PZZ6RSNeHkviHIQblhMyZFFixySeJJB69UJVWAmTVukQr5smk4zEUVF6/7j8OE+DLnbWJlLkKFFFHU20JneEQo1vIWqzWqi1VpTfyNR6cVWdohJjVW0WaryxCdeKotFYVBkjNQAA') format('woff2'), + url('iconfont.woff?t=1613282476380') format('woff'), + url('iconfont.ttf?t=1613282476380') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-xiangyouxuanzhuan:before { + content: "\e66a"; +} + +.icon-tupian:before { + content: "\e616"; +} + +.icon-suo:before { + content: "\e672"; +} + +.icon-juxing:before { + content: "\e790"; +} + +.icon-wenben:before { + content: "\e652"; +} + +.icon-button:before { + content: "\e648"; +} + diff --git a/frontend/src/assets/iconfont/iconfont.eot b/frontend/src/assets/iconfont/iconfont.eot new file mode 100644 index 0000000000..1c944e6357 Binary files /dev/null and b/frontend/src/assets/iconfont/iconfont.eot differ diff --git a/frontend/src/assets/iconfont/iconfont.js b/frontend/src/assets/iconfont/iconfont.js new file mode 100644 index 0000000000..a496b1bad0 --- /dev/null +++ b/frontend/src/assets/iconfont/iconfont.js @@ -0,0 +1 @@ +!function(t){var e,h,c,l,n,o,a='',i=(i=document.getElementsByTagName("script"))[i.length-1].getAttribute("data-injectcss");if(i&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}function d(){n||(n=!0,c())}e=function(){var t,e,h,c;(c=document.createElement("div")).innerHTML=a,a=null,(h=c.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",t=h,(e=document.body).firstChild?(c=t,(h=e.firstChild).parentNode.insertBefore(c,h)):e.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),e()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(c=e,l=t.document,n=!1,(o=function(){try{l.documentElement.doScroll("left")}catch(t){return void setTimeout(o,50)}d()})(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,d())})}(window); \ No newline at end of file diff --git a/frontend/src/assets/iconfont/iconfont.json b/frontend/src/assets/iconfont/iconfont.json new file mode 100644 index 0000000000..59642108de --- /dev/null +++ b/frontend/src/assets/iconfont/iconfont.json @@ -0,0 +1,51 @@ +{ + "id": "2373406", + "name": "visual-drag-demo", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "8229196", + "name": "向右旋转", + "font_class": "xiangyouxuanzhuan", + "unicode": "e66a", + "unicode_decimal": 58986 + }, + { + "icon_id": "2187794", + "name": "图片", + "font_class": "tupian", + "unicode": "e616", + "unicode_decimal": 58902 + }, + { + "icon_id": "6056165", + "name": "锁", + "font_class": "suo", + "unicode": "e672", + "unicode_decimal": 58994 + }, + { + "icon_id": "6266248", + "name": "矩形", + "font_class": "juxing", + "unicode": "e790", + "unicode_decimal": 59280 + }, + { + "icon_id": "14220841", + "name": "文本", + "font_class": "wenben", + "unicode": "e652", + "unicode_decimal": 58962 + }, + { + "icon_id": "16859933", + "name": "按钮", + "font_class": "button", + "unicode": "e648", + "unicode_decimal": 58952 + } + ] +} diff --git a/frontend/src/assets/iconfont/iconfont.svg b/frontend/src/assets/iconfont/iconfont.svg new file mode 100644 index 0000000000..bd5dff3525 --- /dev/null +++ b/frontend/src/assets/iconfont/iconfont.svg @@ -0,0 +1,44 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/iconfont/iconfont.ttf b/frontend/src/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000000..1ac8c5bdec Binary files /dev/null and b/frontend/src/assets/iconfont/iconfont.ttf differ diff --git a/frontend/src/assets/iconfont/iconfont.woff b/frontend/src/assets/iconfont/iconfont.woff new file mode 100644 index 0000000000..cdab07906b Binary files /dev/null and b/frontend/src/assets/iconfont/iconfont.woff differ diff --git a/frontend/src/assets/iconfont/iconfont.woff2 b/frontend/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 0000000000..d57cf5038a Binary files /dev/null and b/frontend/src/assets/iconfont/iconfont.woff2 differ diff --git a/frontend/src/assets/title.jpg b/frontend/src/assets/title.jpg new file mode 100644 index 0000000000..52ebfe1397 Binary files /dev/null and b/frontend/src/assets/title.jpg differ diff --git a/frontend/src/components/AnimationList.vue b/frontend/src/components/AnimationList.vue new file mode 100644 index 0000000000..9eb2605728 --- /dev/null +++ b/frontend/src/components/AnimationList.vue @@ -0,0 +1,118 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/AttrList.vue b/frontend/src/components/AttrList.vue new file mode 100644 index 0000000000..559c747e19 --- /dev/null +++ b/frontend/src/components/AttrList.vue @@ -0,0 +1,81 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/ComponentList.vue b/frontend/src/components/ComponentList.vue new file mode 100644 index 0000000000..54e040df7a --- /dev/null +++ b/frontend/src/components/ComponentList.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/frontend/src/components/Editor/Area.vue b/frontend/src/components/Editor/Area.vue new file mode 100644 index 0000000000..a9e6d30e61 --- /dev/null +++ b/frontend/src/components/Editor/Area.vue @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Editor/ComponentWrapper.vue b/frontend/src/components/Editor/ComponentWrapper.vue new file mode 100644 index 0000000000..eafcdaa4b9 --- /dev/null +++ b/frontend/src/components/Editor/ComponentWrapper.vue @@ -0,0 +1,46 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Editor/ContextMenu.vue b/frontend/src/components/Editor/ContextMenu.vue new file mode 100644 index 0000000000..fc875e497e --- /dev/null +++ b/frontend/src/components/Editor/ContextMenu.vue @@ -0,0 +1,126 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Editor/Grid.vue b/frontend/src/components/Editor/Grid.vue new file mode 100644 index 0000000000..a3ecd76aa1 --- /dev/null +++ b/frontend/src/components/Editor/Grid.vue @@ -0,0 +1,32 @@ + + + \ No newline at end of file diff --git a/frontend/src/components/Editor/MarkLine.vue b/frontend/src/components/Editor/MarkLine.vue new file mode 100644 index 0000000000..075a738852 --- /dev/null +++ b/frontend/src/components/Editor/MarkLine.vue @@ -0,0 +1,249 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Editor/Preview.vue b/frontend/src/components/Editor/Preview.vue new file mode 100644 index 0000000000..9ae0fbdb15 --- /dev/null +++ b/frontend/src/components/Editor/Preview.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/frontend/src/components/Editor/Shape.vue b/frontend/src/components/Editor/Shape.vue new file mode 100644 index 0000000000..3378efb979 --- /dev/null +++ b/frontend/src/components/Editor/Shape.vue @@ -0,0 +1,388 @@ + + + + + diff --git a/frontend/src/components/Editor/index.vue b/frontend/src/components/Editor/index.vue new file mode 100644 index 0000000000..bb5c216118 --- /dev/null +++ b/frontend/src/components/Editor/index.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/frontend/src/components/EventList.vue b/frontend/src/components/EventList.vue new file mode 100644 index 0000000000..fbe3f51dd1 --- /dev/null +++ b/frontend/src/components/EventList.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Modal.vue b/frontend/src/components/Modal.vue new file mode 100644 index 0000000000..21b853c337 --- /dev/null +++ b/frontend/src/components/Modal.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Toolbar.vue b/frontend/src/components/Toolbar.vue new file mode 100644 index 0000000000..8170b5e3d8 --- /dev/null +++ b/frontend/src/components/Toolbar.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/frontend/src/custom-component/Group.vue b/frontend/src/custom-component/Group.vue new file mode 100644 index 0000000000..cdecc01a84 --- /dev/null +++ b/frontend/src/custom-component/Group.vue @@ -0,0 +1,67 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/custom-component/Picture.vue b/frontend/src/custom-component/Picture.vue new file mode 100644 index 0000000000..bc7cf13107 --- /dev/null +++ b/frontend/src/custom-component/Picture.vue @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/custom-component/RectShape.vue b/frontend/src/custom-component/RectShape.vue new file mode 100644 index 0000000000..1c42bfe1d0 --- /dev/null +++ b/frontend/src/custom-component/RectShape.vue @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/custom-component/UserView.vue b/frontend/src/custom-component/UserView.vue new file mode 100644 index 0000000000..0dae19e444 --- /dev/null +++ b/frontend/src/custom-component/UserView.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/frontend/src/custom-component/VButton.vue b/frontend/src/custom-component/VButton.vue new file mode 100644 index 0000000000..ef168ceeb8 --- /dev/null +++ b/frontend/src/custom-component/VButton.vue @@ -0,0 +1,47 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/custom-component/VText.vue b/frontend/src/custom-component/VText.vue new file mode 100644 index 0000000000..48868c6c3d --- /dev/null +++ b/frontend/src/custom-component/VText.vue @@ -0,0 +1,118 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/custom-component/component-list.js b/frontend/src/custom-component/component-list.js new file mode 100644 index 0000000000..3420ce62c3 --- /dev/null +++ b/frontend/src/custom-component/component-list.js @@ -0,0 +1,104 @@ +// 公共样式 +export const commonStyle = { + rotate: 0, + opacity: 1 +} + +export const commonAttr = { + animations: [], + events: {}, + groupStyle: {}, // 当一个组件成为 Group 的子组件时使用 + isLock: false // 是否锁定组件 +} + +// 编辑器左侧组件列表 +const list = [ + { + component: 'v-text', + label: '文字', + propValue: '双击编辑文字', + icon: 'wenben', + style: { + width: 200, + height: 22, + fontSize: 14, + fontWeight: 500, + lineHeight: '', + letterSpacing: 0, + textAlign: '', + color: '' + } + }, + { + component: 'v-button', + label: '按钮', + propValue: '按钮', + icon: 'button', + style: { + width: 100, + height: 34, + borderWidth: '', + borderColor: '', + borderRadius: '', + fontSize: 14, + fontWeight: 500, + lineHeight: '', + letterSpacing: 0, + textAlign: '', + color: '', + backgroundColor: '' + } + }, + { + component: 'Picture', + label: '图片', + icon: 'tupian', + propValue: require('@/assets/title.jpg'), + style: { + width: 300, + height: 200, + borderRadius: '' + } + }, + { + component: 'rect-shape', + label: '矩形', + propValue: ' ', + icon: 'juxing', + style: { + width: 200, + height: 200, + fontSize: 14, + fontWeight: 500, + lineHeight: '', + letterSpacing: 0, + textAlign: 'center', + color: '', + borderColor: '#000', + borderWidth: 1, + backgroundColor: '', + borderStyle: 'solid', + verticalAlign: 'middle' + } + }, + { + component: 'user-view', + label: '用户视图', + propValue: '', + icon: 'juxing', + type: 'view', + style: { + width: 200, + height: 300, + borderWidth: 1 + } + } +] + +for (let i = 0, len = list.length; i < len; i++) { + const item = list[i] + item.style = { ...commonStyle, ...item.style } + list[i] = { ...commonAttr, ...item } +} + +export default list diff --git a/frontend/src/custom-component/index.js b/frontend/src/custom-component/index.js new file mode 100644 index 0000000000..ab9bde6bcd --- /dev/null +++ b/frontend/src/custom-component/index.js @@ -0,0 +1,16 @@ +import Vue from 'vue' + +import Picture from '@/custom-component/Picture' +import VText from '@/custom-component/VText' +import VButton from '@/custom-component/VButton' +import Group from '@/custom-component/Group' +import RectShape from '@/custom-component/RectShape' +import UserView from '@/custom-component/UserView' + +Vue.component('Picture', Picture) +Vue.component('VText', VText) +Vue.component('VButton', VButton) +Vue.component('Group', Group) +Vue.component('RectShape', RectShape) +Vue.component('UserView', UserView) + diff --git a/frontend/src/main.js b/frontend/src/main.js index cff3b561bc..baecaf8a98 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -15,6 +15,14 @@ import api from '@/api/index.js' import filter from '@/filter/filter' import directives from './directive' +import '@/custom-component' // 注册自定义组件 +import '@/assets/iconfont/iconfont.css' +import '@/styles/animate.css' +import 'element-ui/lib/theme-chalk/index.css' +import '@/styles/reset.css' +Vue.config.productionTip = false + + Vue.prototype.$api = api import * as echarts from 'echarts' diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index cc572f9541..4f2297c090 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -71,6 +71,18 @@ export const constantRoutes = [ } ] }, + { + path: '/panelCanvas', + component: Layout, + redirect: '/panelCanvas/canvas', + hidden: true, + children: [ + { + path: 'canvas', + component: () => import('@/views/panel/canvas') + } + ] + }, { path: '/preview', diff --git a/frontend/src/store/animation.js b/frontend/src/store/animation.js new file mode 100644 index 0000000000..0db25da68a --- /dev/null +++ b/frontend/src/store/animation.js @@ -0,0 +1,11 @@ +export default { + mutations: { + addAnimation({ curComponent }, animation) { + curComponent.animations.push(animation) + }, + + removeAnimation({ curComponent }, index) { + curComponent.animations.splice(index, 1) + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/compose.js b/frontend/src/store/compose.js new file mode 100644 index 0000000000..e68afa2c3e --- /dev/null +++ b/frontend/src/store/compose.js @@ -0,0 +1,100 @@ +import store from './index' +import generateID from '@/utils/generateID' +import eventBus from '@/utils/eventBus' +import decomposeComponent from '@/utils/decomposeComponent' +import { $ } from '@/utils/utils' +import { commonStyle, commonAttr } from '@/custom-component/component-list' + +export default { + state: { + areaData: { // 选中区域包含的组件以及区域位移信息 + style: { + top: 0, + left: 0, + width: 0, + height: 0, + }, + components: [], + }, + editor: null, + }, + mutations: { + getEditor(state) { + state.editor = $('#editor') + }, + + setAreaData(state, data) { + state.areaData = data + }, + + compose({ componentData, areaData, editor }) { + const components = [] + areaData.components.forEach(component => { + if (component.component != 'Group') { + components.push(component) + } else { + // 如果要组合的组件中,已经存在组合数据,则需要提前拆分 + const parentStyle = { ...component.style } + const subComponents = component.propValue + const editorRect = editor.getBoundingClientRect() + + store.commit('deleteComponent') + subComponents.forEach(component => { + decomposeComponent(component, editorRect, parentStyle) + store.commit('addComponent', { component }) + }) + + components.push(...component.propValue) + store.commit('batchDeleteComponent', component.propValue) + } + }) + + store.commit('addComponent', { + component: { + id: generateID(), + component: 'Group', + ...commonAttr, + style: { + ...commonStyle, + ...areaData.style, + }, + propValue: components, + }, + }) + + eventBus.$emit('hideArea') + + store.commit('batchDeleteComponent', areaData.components) + store.commit('setCurComponent', { + component: componentData[componentData.length - 1], + index: componentData.length - 1, + }) + + areaData.components = [] + }, + + // 将已经放到 Group 组件数据删除,也就是在 componentData 中删除,因为它们已经放到 Group 组件中了 + batchDeleteComponent({ componentData }, deleteData) { + deleteData.forEach(component => { + for (let i = 0, len = componentData.length; i < len; i++) { + if (component.id == componentData[i].id) { + componentData.splice(i, 1) + break + } + } + }) + }, + + decompose({ curComponent, editor }) { + const parentStyle = { ...curComponent.style } + const components = curComponent.propValue + const editorRect = editor.getBoundingClientRect() + + store.commit('deleteComponent') + components.forEach(component => { + decomposeComponent(component, editorRect, parentStyle) + store.commit('addComponent', { component }) + }) + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/contextmenu.js b/frontend/src/store/contextmenu.js new file mode 100644 index 0000000000..c54729a016 --- /dev/null +++ b/frontend/src/store/contextmenu.js @@ -0,0 +1,18 @@ +export default { + state: { + menuTop: 0, // 右击菜单数据 + menuLeft: 0, + menuShow: false, + }, + mutations: { + showContextMenu(state, { top, left }) { + state.menuShow = true + state.menuTop = top + state.menuLeft = left + }, + + hideContextMenu(state) { + state.menuShow = false + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/copy.js b/frontend/src/store/copy.js new file mode 100644 index 0000000000..dedfcd6e02 --- /dev/null +++ b/frontend/src/store/copy.js @@ -0,0 +1,67 @@ +import store from './index' +import toast from '@/utils/toast' +import generateID from '@/utils/generateID' +import { deepCopy } from '@/utils/utils' + +export default { + state: { + copyData: null, // 复制粘贴剪切 + isCut: false, + }, + mutations: { + copy(state) { + if (!state.curComponent) return + state.copyData = { + data: deepCopy(state.curComponent), + index: state.curComponentIndex, + } + + state.isCut = false + }, + + paste(state, isMouse) { + if (!state.copyData) { + toast('请选择组件') + return + } + + const data = state.copyData.data + + if (isMouse) { + data.style.top = state.menuTop + data.style.left = state.menuLeft + } else { + data.style.top += 10 + data.style.left += 10 + } + + data.id = generateID() + store.commit('addComponent', { component: deepCopy(data) }) + if (state.isCut) { + state.copyData = null + } + }, + + cut(state) { + if (!state.curComponent) { + toast('请选择组件') + return + } + + if (state.copyData) { + const data = deepCopy(state.copyData.data) + const index = state.copyData.index + data.id = generateID() + store.commit('addComponent', { component: data, index }) + if (state.curComponentIndex >= index) { + // 如果当前组件索引大于等于插入索引,需要加一,因为当前组件往后移了一位 + state.curComponentIndex++ + } + } + + store.commit('copy') + store.commit('deleteComponent') + state.isCut = true + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/event.js b/frontend/src/store/event.js new file mode 100644 index 0000000000..4b838ac5e9 --- /dev/null +++ b/frontend/src/store/event.js @@ -0,0 +1,11 @@ +export default { + mutations: { + addEvent({ curComponent }, { event, param }) { + curComponent.events[event] = param + }, + + removeEvent({ curComponent }, event) { + delete curComponent.events[event] + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index a7590476c3..cb90dbcb5d 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -9,9 +9,101 @@ import dataset from './modules/dataset' import chart from './modules/chart' import request from './modules/request' import panel from './modules/panel' + +import animation from './animation' +import compose from './compose' +import contextmenu from './contextmenu' +import copy from './copy' +import event from './event' +import layer from './layer' +import snapshot from './snapshot' +import lock from './lock' + Vue.use(Vuex) -const store = new Vuex.Store({ +const data = { + state: { + ...animation.state, + ...compose.state, + ...contextmenu.state, + ...copy.state, + ...event.state, + ...layer.state, + ...snapshot.state, + ...lock.state, + + editMode: 'edit', // 编辑器模式 edit preview + canvasStyleData: { // 页面全局数据 + width: 1200, + height: 740, + scale: 100 + }, + componentData: [], // 画布组件数据 + curComponent: null, + curComponentIndex: null, + // 点击画布时是否点中组件,主要用于取消选中组件用。 + // 如果没点中组件,并且在画布空白处弹起鼠标,则取消当前组件的选中状态 + isClickComponent: false + }, + mutations: { + ...animation.mutations, + ...compose.mutations, + ...contextmenu.mutations, + ...copy.mutations, + ...event.mutations, + ...layer.mutations, + ...snapshot.mutations, + ...lock.mutations, + + setClickComponentStatus(state, status) { + state.isClickComponent = status + }, + + setEditMode(state, mode) { + state.editMode = mode + }, + + setCanvasStyle(state, style) { + state.canvasStyleData = style + }, + + setCurComponent(state, { component, index }) { + state.curComponent = component + state.curComponentIndex = index + }, + + setShapeStyle({ curComponent }, { top, left, width, height, rotate }) { + if (top) curComponent.style.top = top + if (left) curComponent.style.left = left + if (width) curComponent.style.width = width + if (height) curComponent.style.height = height + if (rotate) curComponent.style.rotate = rotate + }, + + setShapeSingleStyle({ curComponent }, { key, value }) { + curComponent.style[key] = value + }, + + setComponentData(state, componentData = []) { + Vue.set(state, 'componentData', componentData) + }, + + addComponent(state, { component, index }) { + if (index !== undefined) { + state.componentData.splice(index, 0, component) + } else { + state.componentData.push(component) + } + }, + + deleteComponent(state, index) { + if (index === undefined) { + index = state.curComponentIndex + } + + state.componentData.splice(index, 1) + } + }, modules: { app, settings, @@ -23,6 +115,6 @@ const store = new Vuex.Store({ panel }, getters -}) +} -export default store +export default new Vuex.Store(data) diff --git a/frontend/src/store/layer.js b/frontend/src/store/layer.js new file mode 100644 index 0000000000..fec1ff6fc4 --- /dev/null +++ b/frontend/src/store/layer.js @@ -0,0 +1,42 @@ +import { swap } from '@/utils/utils' +import toast from '@/utils/toast' + +export default { + mutations: { + upComponent({ componentData, curComponentIndex }) { + // 上移图层 index,表示元素在数组中越往后 + if (curComponentIndex < componentData.length - 1) { + swap(componentData, curComponentIndex, curComponentIndex + 1) + } else { + toast('已经到顶了') + } + }, + + downComponent({ componentData, curComponentIndex }) { + // 下移图层 index,表示元素在数组中越往前 + if (curComponentIndex > 0) { + swap(componentData, curComponentIndex, curComponentIndex - 1) + } else { + toast('已经到底了') + } + }, + + topComponent({ componentData, curComponentIndex }) { + // 置顶 + if (curComponentIndex < componentData.length - 1) { + swap(componentData, curComponentIndex, componentData.length - 1) + } else { + toast('已经到顶了') + } + }, + + bottomComponent({ componentData, curComponentIndex }) { + // 置底 + if (curComponentIndex > 0) { + swap(componentData, curComponentIndex, 0) + } else { + toast('已经到底了') + } + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/lock.js b/frontend/src/store/lock.js new file mode 100644 index 0000000000..ed0c17bac2 --- /dev/null +++ b/frontend/src/store/lock.js @@ -0,0 +1,11 @@ +export default { + mutations: { + lock({ curComponent }) { + curComponent.isLock = true + }, + + unlock({ curComponent }) { + curComponent.isLock = false + }, + }, +} \ No newline at end of file diff --git a/frontend/src/store/snapshot.js b/frontend/src/store/snapshot.js new file mode 100644 index 0000000000..5f4908e4f5 --- /dev/null +++ b/frontend/src/store/snapshot.js @@ -0,0 +1,33 @@ +import store from './index' +import { deepCopy } from '@/utils/utils' + +export default { + state: { + snapshotData: [], // 编辑器快照数据 + snapshotIndex: -1, // 快照索引 + }, + mutations: { + undo(state) { + if (state.snapshotIndex >= 0) { + state.snapshotIndex-- + store.commit('setComponentData', deepCopy(state.snapshotData[state.snapshotIndex])) + } + }, + + redo(state) { + if (state.snapshotIndex < state.snapshotData.length - 1) { + state.snapshotIndex++ + store.commit('setComponentData', deepCopy(state.snapshotData[state.snapshotIndex])) + } + }, + + recordSnapshot(state) { + // 添加新的快照 + state.snapshotData[++state.snapshotIndex] = deepCopy(state.componentData) + // 在 undo 过程中,添加新的快照时,要将它后面的快照清理掉 + if (state.snapshotIndex < state.snapshotData.length - 1) { + state.snapshotData = state.snapshotData.slice(0, state.snapshotIndex + 1) + } + }, + }, +} \ No newline at end of file diff --git a/frontend/src/styles/animate.css b/frontend/src/styles/animate.css new file mode 100644 index 0000000000..142dccf569 --- /dev/null +++ b/frontend/src/styles/animate.css @@ -0,0 +1,3614 @@ +@charset "UTF-8"; + +/*! + * animate.css -https://daneden.github.io/animate.css/ + * Version - 3.7.2 + * Licensed under the MIT license - http://opensource.org/licenses/MIT + * + * Copyright (c) 2019 Daniel Eden + */ + +@-webkit-keyframes bounce { + from, + 20%, + 53%, + 80%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 40%, + 43% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -4px, 0); + transform: translate3d(0, -4px, 0); + } +} + +@keyframes bounce { + from, + 20%, + 53%, + 80%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 40%, + 43% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -4px, 0); + transform: translate3d(0, -4px, 0); + } +} + +.bounce { + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + transform-origin: center bottom; +} + +@-webkit-keyframes flash { + from, + 50%, + to { + opacity: 1; + } + + 25%, + 75% { + opacity: 0; + } +} + +@keyframes flash { + from, + 50%, + to { + opacity: 1; + } + + 25%, + 75% { + opacity: 0; + } +} + +.flash { + -webkit-animation-name: flash; + animation-name: flash; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes pulse { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes pulse { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.pulse { + -webkit-animation-name: pulse; + animation-name: pulse; +} + +@-webkit-keyframes rubberBand { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(0.95, 1.05, 1); + transform: scale3d(0.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, 0.95, 1); + transform: scale3d(1.05, 0.95, 1); + } + + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes rubberBand { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(0.95, 1.05, 1); + transform: scale3d(0.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, 0.95, 1); + transform: scale3d(1.05, 0.95, 1); + } + + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.rubberBand { + -webkit-animation-name: rubberBand; + animation-name: rubberBand; +} + +@-webkit-keyframes shake { + from, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +@keyframes shake { + from, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +.shake { + -webkit-animation-name: shake; + animation-name: shake; +} + +@-webkit-keyframes headShake { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 6.5% { + -webkit-transform: translateX(-6px) rotateY(-9deg); + transform: translateX(-6px) rotateY(-9deg); + } + + 18.5% { + -webkit-transform: translateX(5px) rotateY(7deg); + transform: translateX(5px) rotateY(7deg); + } + + 31.5% { + -webkit-transform: translateX(-3px) rotateY(-5deg); + transform: translateX(-3px) rotateY(-5deg); + } + + 43.5% { + -webkit-transform: translateX(2px) rotateY(3deg); + transform: translateX(2px) rotateY(3deg); + } + + 50% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes headShake { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 6.5% { + -webkit-transform: translateX(-6px) rotateY(-9deg); + transform: translateX(-6px) rotateY(-9deg); + } + + 18.5% { + -webkit-transform: translateX(5px) rotateY(7deg); + transform: translateX(5px) rotateY(7deg); + } + + 31.5% { + -webkit-transform: translateX(-3px) rotateY(-5deg); + transform: translateX(-3px) rotateY(-5deg); + } + + 43.5% { + -webkit-transform: translateX(2px) rotateY(3deg); + transform: translateX(2px) rotateY(3deg); + } + + 50% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +.headShake { + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-name: headShake; + animation-name: headShake; +} + +@-webkit-keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + to { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +@keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + to { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +.swing { + -webkit-transform-origin: top center; + transform-origin: top center; + -webkit-animation-name: swing; + animation-name: swing; +} + +@-webkit-keyframes tada { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, + 20% { + -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + + 30%, + 50%, + 70%, + 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, + 60%, + 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes tada { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, + 20% { + -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + + 30%, + 50%, + 70%, + 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, + 60%, + 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + to { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.tada { + -webkit-animation-name: tada; + animation-name: tada; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes wobble { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes wobble { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.wobble { + -webkit-animation-name: wobble; + animation-name: wobble; +} + +@-webkit-keyframes jello { + from, + 11.1%, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 22.2% { + -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); + transform: skewX(-12.5deg) skewY(-12.5deg); + } + + 33.3% { + -webkit-transform: skewX(6.25deg) skewY(6.25deg); + transform: skewX(6.25deg) skewY(6.25deg); + } + + 44.4% { + -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); + transform: skewX(-3.125deg) skewY(-3.125deg); + } + + 55.5% { + -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); + transform: skewX(1.5625deg) skewY(1.5625deg); + } + + 66.6% { + -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); + transform: skewX(-0.78125deg) skewY(-0.78125deg); + } + + 77.7% { + -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); + transform: skewX(0.390625deg) skewY(0.390625deg); + } + + 88.8% { + -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + } +} + +@keyframes jello { + from, + 11.1%, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 22.2% { + -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); + transform: skewX(-12.5deg) skewY(-12.5deg); + } + + 33.3% { + -webkit-transform: skewX(6.25deg) skewY(6.25deg); + transform: skewX(6.25deg) skewY(6.25deg); + } + + 44.4% { + -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); + transform: skewX(-3.125deg) skewY(-3.125deg); + } + + 55.5% { + -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); + transform: skewX(1.5625deg) skewY(1.5625deg); + } + + 66.6% { + -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); + transform: skewX(-0.78125deg) skewY(-0.78125deg); + } + + 77.7% { + -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); + transform: skewX(0.390625deg) skewY(0.390625deg); + } + + 88.8% { + -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + } +} + +.jello { + -webkit-animation-name: jello; + animation-name: jello; + -webkit-transform-origin: center; + transform-origin: center; +} + +@-webkit-keyframes heartBeat { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + } + + 14% { + -webkit-transform: scale(1.3); + transform: scale(1.3); + } + + 28% { + -webkit-transform: scale(1); + transform: scale(1); + } + + 42% { + -webkit-transform: scale(1.3); + transform: scale(1.3); + } + + 70% { + -webkit-transform: scale(1); + transform: scale(1); + } +} + +@keyframes heartBeat { + 0% { + -webkit-transform: scale(1); + transform: scale(1); + } + + 14% { + -webkit-transform: scale(1.3); + transform: scale(1.3); + } + + 28% { + -webkit-transform: scale(1); + transform: scale(1); + } + + 42% { + -webkit-transform: scale(1.3); + transform: scale(1.3); + } + + 70% { + -webkit-transform: scale(1); + transform: scale(1); + } +} + +.heartBeat { + -webkit-animation-name: heartBeat; + animation-name: heartBeat; + -webkit-animation-duration: 1.3s; + animation-duration: 1.3s; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; +} + +@-webkit-keyframes bounceIn { + from, + 20%, + 40%, + 60%, + 80%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(0.9, 0.9, 0.9); + transform: scale3d(0.9, 0.9, 0.9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(0.97, 0.97, 0.97); + transform: scale3d(0.97, 0.97, 0.97); + } + + to { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes bounceIn { + from, + 20%, + 40%, + 60%, + 80%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(0.9, 0.9, 0.9); + transform: scale3d(0.9, 0.9, 0.9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(0.97, 0.97, 0.97); + transform: scale3d(0.97, 0.97, 0.97); + } + + to { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.bounceIn { + -webkit-animation-duration: 0.75s; + animation-duration: 0.75s; + -webkit-animation-name: bounceIn; + animation-name: bounceIn; +} + +@-webkit-keyframes bounceInDown { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInDown { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInDown { + -webkit-animation-name: bounceInDown; + animation-name: bounceInDown; +} + +@-webkit-keyframes bounceInLeft { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInLeft { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInLeft { + -webkit-animation-name: bounceInLeft; + animation-name: bounceInLeft; +} + +@-webkit-keyframes bounceInRight { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + from { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInRight { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + from { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInRight { + -webkit-animation-name: bounceInRight; + animation-name: bounceInRight; +} + +@-webkit-keyframes bounceInUp { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + from { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInUp { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + from { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInUp { + -webkit-animation-name: bounceInUp; + animation-name: bounceInUp; +} + +@-webkit-keyframes bounceOut { + 20% { + -webkit-transform: scale3d(0.9, 0.9, 0.9); + transform: scale3d(0.9, 0.9, 0.9); + } + + 50%, + 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + to { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } +} + +@keyframes bounceOut { + 20% { + -webkit-transform: scale3d(0.9, 0.9, 0.9); + transform: scale3d(0.9, 0.9, 0.9); + } + + 50%, + 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + to { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } +} + +.bounceOut { + -webkit-animation-duration: 0.75s; + animation-duration: 0.75s; + -webkit-animation-name: bounceOut; + animation-name: bounceOut; +} + +@-webkit-keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, + 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, + 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.bounceOutDown { + -webkit-animation-name: bounceOutDown; + animation-name: bounceOutDown; +} + +@-webkit-keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.bounceOutLeft { + -webkit-animation-name: bounceOutLeft; + animation-name: bounceOutLeft; +} + +@-webkit-keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.bounceOutRight { + -webkit-animation-name: bounceOutRight; + animation-name: bounceOutRight; +} + +@-webkit-keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, + 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, + 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.bounceOutUp { + -webkit-animation-name: bounceOutUp; + animation-name: bounceOutUp; +} + +@-webkit-keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +.fadeIn { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; +} + +@-webkit-keyframes fadeInDown { + from { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInDown { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; +} + +@-webkit-keyframes fadeInDownBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInDownBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInDownBig { + -webkit-animation-name: fadeInDownBig; + animation-name: fadeInDownBig; +} + +@-webkit-keyframes fadeInLeft { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInLeft { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInLeft { + -webkit-animation-name: fadeInLeft; + animation-name: fadeInLeft; +} + +@-webkit-keyframes fadeInLeftBig { + from { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInLeftBig { + from { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInLeftBig { + -webkit-animation-name: fadeInLeftBig; + animation-name: fadeInLeftBig; +} + +@-webkit-keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInRight { + -webkit-animation-name: fadeInRight; + animation-name: fadeInRight; +} + +@-webkit-keyframes fadeInRightBig { + from { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInRightBig { + from { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInRightBig { + -webkit-animation-name: fadeInRightBig; + animation-name: fadeInRightBig; +} + +@-webkit-keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInUp { + -webkit-animation-name: fadeInUp; + animation-name: fadeInUp; +} + +@-webkit-keyframes fadeInUpBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeInUpBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.fadeInUpBig { + -webkit-animation-name: fadeInUpBig; + animation-name: fadeInUpBig; +} + +@-webkit-keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} + +@keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} + +.fadeOut { + -webkit-animation-name: fadeOut; + animation-name: fadeOut; +} + +@-webkit-keyframes fadeOutDown { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +@keyframes fadeOutDown { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +.fadeOutDown { + -webkit-animation-name: fadeOutDown; + animation-name: fadeOutDown; +} + +@-webkit-keyframes fadeOutDownBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes fadeOutDownBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.fadeOutDownBig { + -webkit-animation-name: fadeOutDownBig; + animation-name: fadeOutDownBig; +} + +@-webkit-keyframes fadeOutLeft { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes fadeOutLeft { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +.fadeOutLeft { + -webkit-animation-name: fadeOutLeft; + animation-name: fadeOutLeft; +} + +@-webkit-keyframes fadeOutLeftBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes fadeOutLeftBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.fadeOutLeftBig { + -webkit-animation-name: fadeOutLeftBig; + animation-name: fadeOutLeftBig; +} + +@-webkit-keyframes fadeOutRight { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +@keyframes fadeOutRight { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +.fadeOutRight { + -webkit-animation-name: fadeOutRight; + animation-name: fadeOutRight; +} + +@-webkit-keyframes fadeOutRightBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes fadeOutRightBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.fadeOutRightBig { + -webkit-animation-name: fadeOutRightBig; + animation-name: fadeOutRightBig; +} + +@-webkit-keyframes fadeOutUp { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +@keyframes fadeOutUp { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +.fadeOutUp { + -webkit-animation-name: fadeOutUp; + animation-name: fadeOutUp; +} + +@-webkit-keyframes fadeOutUpBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes fadeOutUpBig { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.fadeOutUpBig { + -webkit-animation-name: fadeOutUpBig; + animation-name: fadeOutUpBig; +} + +@-webkit-keyframes flip { + from { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) + rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) + rotate3d(0, 1, 0, 0deg); + transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) + rotate3d(0, 1, 0, 0deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + to { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) + rotate3d(0, 1, 0, 0deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +@keyframes flip { + from { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) + rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) + rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) + rotate3d(0, 1, 0, 0deg); + transform: perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) + rotate3d(0, 1, 0, 0deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + to { + -webkit-transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) + rotate3d(0, 1, 0, 0deg); + transform: perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +.animated.flip { + -webkit-backface-visibility: visible; + backface-visibility: visible; + -webkit-animation-name: flip; + animation-name: flip; +} + +@-webkit-keyframes flipInX { + from { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + to { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInX { + from { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + to { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInX { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInX; + animation-name: flipInX; +} + +@-webkit-keyframes flipInY { + from { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + to { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInY { + from { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + to { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInY; + animation-name: flipInY; +} + +@-webkit-keyframes flipOutX { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + to { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutX { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + to { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +.flipOutX { + -webkit-animation-duration: 0.75s; + animation-duration: 0.75s; + -webkit-animation-name: flipOutX; + animation-name: flipOutX; + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; +} + +@-webkit-keyframes flipOutY { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + to { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutY { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + to { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +.flipOutY { + -webkit-animation-duration: 0.75s; + animation-duration: 0.75s; + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipOutY; + animation-name: flipOutY; +} + +@-webkit-keyframes lightSpeedIn { + from { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes lightSpeedIn { + from { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.lightSpeedIn { + -webkit-animation-name: lightSpeedIn; + animation-name: lightSpeedIn; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; +} + +@-webkit-keyframes lightSpeedOut { + from { + opacity: 1; + } + + to { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +@keyframes lightSpeedOut { + from { + opacity: 1; + } + + to { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +.lightSpeedOut { + -webkit-animation-name: lightSpeedOut; + animation-name: lightSpeedOut; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; +} + +@-webkit-keyframes rotateIn { + from { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + to { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +@keyframes rotateIn { + from { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + to { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +.rotateIn { + -webkit-animation-name: rotateIn; + animation-name: rotateIn; +} + +@-webkit-keyframes rotateInDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +@keyframes rotateInDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +.rotateInDownLeft { + -webkit-animation-name: rotateInDownLeft; + animation-name: rotateInDownLeft; +} + +@-webkit-keyframes rotateInDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +@keyframes rotateInDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +.rotateInDownRight { + -webkit-animation-name: rotateInDownRight; + animation-name: rotateInDownRight; +} + +@-webkit-keyframes rotateInUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +@keyframes rotateInUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +.rotateInUpLeft { + -webkit-animation-name: rotateInUpLeft; + animation-name: rotateInUpLeft; +} + +@-webkit-keyframes rotateInUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +@keyframes rotateInUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +.rotateInUpRight { + -webkit-animation-name: rotateInUpRight; + animation-name: rotateInUpRight; +} + +@-webkit-keyframes rotateOut { + from { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + to { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +@keyframes rotateOut { + from { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + to { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +.rotateOut { + -webkit-animation-name: rotateOut; + animation-name: rotateOut; +} + +@-webkit-keyframes rotateOutDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +.rotateOutDownLeft { + -webkit-animation-name: rotateOutDownLeft; + animation-name: rotateOutDownLeft; +} + +@-webkit-keyframes rotateOutDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutDownRight { + -webkit-animation-name: rotateOutDownRight; + animation-name: rotateOutDownRight; +} + +@-webkit-keyframes rotateOutUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutUpLeft { + -webkit-animation-name: rotateOutUpLeft; + animation-name: rotateOutUpLeft; +} + +@-webkit-keyframes rotateOutUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +@keyframes rotateOutUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + to { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +.rotateOutUpRight { + -webkit-animation-name: rotateOutUpRight; + animation-name: rotateOutUpRight; +} + +@-webkit-keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, + 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, + 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + to { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +@keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, + 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, + 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + to { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +.hinge { + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-name: hinge; + animation-name: hinge; +} + +@-webkit-keyframes jackInTheBox { + from { + opacity: 0; + -webkit-transform: scale(0.1) rotate(30deg); + transform: scale(0.1) rotate(30deg); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + } + + 50% { + -webkit-transform: rotate(-10deg); + transform: rotate(-10deg); + } + + 70% { + -webkit-transform: rotate(3deg); + transform: rotate(3deg); + } + + to { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); + } +} + +@keyframes jackInTheBox { + from { + opacity: 0; + -webkit-transform: scale(0.1) rotate(30deg); + transform: scale(0.1) rotate(30deg); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + } + + 50% { + -webkit-transform: rotate(-10deg); + transform: rotate(-10deg); + } + + 70% { + -webkit-transform: rotate(3deg); + transform: rotate(3deg); + } + + to { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); + } +} + +.jackInTheBox { + -webkit-animation-name: jackInTheBox; + animation-name: jackInTheBox; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollIn { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes rollIn { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.rollIn { + -webkit-animation-name: rollIn; + animation-name: rollIn; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollOut { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +@keyframes rollOut { + from { + opacity: 1; + } + + to { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +.rollOut { + -webkit-animation-name: rollOut; + animation-name: rollOut; +} + +@-webkit-keyframes zoomIn { + from { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } + + 50% { + opacity: 1; + } +} + +@keyframes zoomIn { + from { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } + + 50% { + opacity: 1; + } +} + +.zoomIn { + -webkit-animation-name: zoomIn; + animation-name: zoomIn; +} + +@-webkit-keyframes zoomInDown { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +@keyframes zoomInDown { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +.zoomInDown { + -webkit-animation-name: zoomInDown; + animation-name: zoomInDown; +} + +@-webkit-keyframes zoomInLeft { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +@keyframes zoomInLeft { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +.zoomInLeft { + -webkit-animation-name: zoomInLeft; + animation-name: zoomInLeft; +} + +@-webkit-keyframes zoomInRight { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +@keyframes zoomInRight { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +.zoomInRight { + -webkit-animation-name: zoomInRight; + animation-name: zoomInRight; +} + +@-webkit-keyframes zoomInUp { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +@keyframes zoomInUp { + from { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +.zoomInUp { + -webkit-animation-name: zoomInUp; + animation-name: zoomInUp; +} + +@-webkit-keyframes zoomOut { + from { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } + + to { + opacity: 0; + } +} + +@keyframes zoomOut { + from { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(0.3, 0.3, 0.3); + transform: scale3d(0.3, 0.3, 0.3); + } + + to { + opacity: 0; + } +} + +.zoomOut { + -webkit-animation-name: zoomOut; + animation-name: zoomOut; +} + +@-webkit-keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + to { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +@keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + to { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +.zoomOutDown { + -webkit-animation-name: zoomOutDown; + animation-name: zoomOutDown; +} + +@-webkit-keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0); + transform: scale(0.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +@keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0); + transform: scale(0.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +.zoomOutLeft { + -webkit-animation-name: zoomOutLeft; + animation-name: zoomOutLeft; +} + +@-webkit-keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: scale(0.1) translate3d(2000px, 0, 0); + transform: scale(0.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +@keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0); + } + + to { + opacity: 0; + -webkit-transform: scale(0.1) translate3d(2000px, 0, 0); + transform: scale(0.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +.zoomOutRight { + -webkit-animation-name: zoomOutRight; + animation-name: zoomOutRight; +} + +@-webkit-keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + to { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +@keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + to { + opacity: 0; + -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); + transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); + } +} + +.zoomOutUp { + -webkit-animation-name: zoomOutUp; + animation-name: zoomOutUp; +} + +@-webkit-keyframes slideInDown { + from { + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInDown { + from { + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInDown { + -webkit-animation-name: slideInDown; + animation-name: slideInDown; +} + +@-webkit-keyframes slideInLeft { + from { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInLeft { + from { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInLeft { + -webkit-animation-name: slideInLeft; + animation-name: slideInLeft; +} + +@-webkit-keyframes slideInRight { + from { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInRight { + from { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInRight { + -webkit-animation-name: slideInRight; + animation-name: slideInRight; +} + +@-webkit-keyframes slideInUp { + from { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInUp { + from { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInUp { + -webkit-animation-name: slideInUp; + animation-name: slideInUp; +} + +@-webkit-keyframes slideOutDown { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +@keyframes slideOutDown { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +.slideOutDown { + -webkit-animation-name: slideOutDown; + animation-name: slideOutDown; +} + +@-webkit-keyframes slideOutLeft { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes slideOutLeft { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +.slideOutLeft { + -webkit-animation-name: slideOutLeft; + animation-name: slideOutLeft; +} + +@-webkit-keyframes slideOutRight { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +@keyframes slideOutRight { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +.slideOutRight { + -webkit-animation-name: slideOutRight; + animation-name: slideOutRight; +} + +@-webkit-keyframes slideOutUp { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +@keyframes slideOutUp { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +.slideOutUp { + -webkit-animation-name: slideOutUp; + animation-name: slideOutUp; +} + +.animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +.animated.infinite { + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.animated.delay-1s { + -webkit-animation-delay: 1s; + animation-delay: 1s; +} + +.animated.delay-2s { + -webkit-animation-delay: 2s; + animation-delay: 2s; +} + +.animated.delay-3s { + -webkit-animation-delay: 3s; + animation-delay: 3s; +} + +.animated.delay-4s { + -webkit-animation-delay: 4s; + animation-delay: 4s; +} + +.animated.delay-5s { + -webkit-animation-delay: 5s; + animation-delay: 5s; +} + +.animated.fast { + -webkit-animation-duration: 800ms; + animation-duration: 800ms; +} + +.animated.faster { + -webkit-animation-duration: 500ms; + animation-duration: 500ms; +} + +.animated.slow { + -webkit-animation-duration: 2s; + animation-duration: 2s; +} + +.animated.slower { + -webkit-animation-duration: 3s; + animation-duration: 3s; +} \ No newline at end of file diff --git a/frontend/src/styles/reset.css b/frontend/src/styles/reset.css new file mode 100644 index 0000000000..b7d5b60c81 --- /dev/null +++ b/frontend/src/styles/reset.css @@ -0,0 +1,46 @@ +body, +button, +input, +p, +li, +ol, +ul, +div, +section, +article, +td, +th, +span, +textarea, +form, +footer, +header, +nav, +main, +address, +aside, +pre, +canvas { + margin: 0; + padding: 0; + box-sizing: border-box; +} +body { + overflow: hidden; +} +li { + list-style: none; +} +#app { + overflow: hidden; +} +.el-tabs { + height: 100%; +} +.el-tabs__content { + height: calc(100% - 55px); + overflow: auto; +} +.el-tabs__nav-scroll { + padding-left: 20px; +} \ No newline at end of file diff --git a/frontend/src/utils/animationClassData.js b/frontend/src/utils/animationClassData.js new file mode 100644 index 0000000000..531303c546 --- /dev/null +++ b/frontend/src/utils/animationClassData.js @@ -0,0 +1,94 @@ +export default [ + { + label: '进入', + children: [ + { label: '渐显', value: 'fadeIn' }, + { label: '向右进入', value: 'fadeInLeft' }, + { label: '向左进入', value: 'fadeInRight' }, + { label: '向上进入', value: 'fadeInUp' }, + { label: '向下进入', value: 'fadeInDown' }, + { label: '向右长距进入', value: 'fadeInLeftBig' }, + { label: '向左长距进入', value: 'fadeInRightBig' }, + { label: '向上长距进入', value: 'fadeInUpBig' }, + { label: '向下长距进入', value: 'fadeInDownBig' }, + { label: '旋转进入', value: 'rotateIn' }, + { label: '左顺时针旋转', value: 'rotateInDownLeft' }, + { label: '右逆时针旋转', value: 'rotateInDownRight' }, + { label: '左逆时针旋转', value: 'rotateInUpLeft' }, + { label: '右逆时针旋转', value: 'rotateInUpRight' }, + { label: '弹入', value: 'bounceIn' }, + { label: '向右弹入', value: 'bounceInLeft' }, + { label: '向左弹入', value: 'bounceInRight' }, + { label: '向上弹入', value: 'bounceInUp' }, + { label: '向下弹入', value: 'bounceInDown' }, + { label: '光速从右进入', value: 'lightSpeedInRight' }, + { label: '光速从左进入', value: 'lightSpeedInLeft' }, + { label: '光速从右退出', value: 'lightSpeedOutRight' }, + { label: '光速从左退出', value: 'lightSpeedOutLeft' }, + { label: 'Y轴旋转', value: 'flip' }, + { label: '中心X轴旋转', value: 'flipInX' }, + { label: '中心Y轴旋转', value: 'flipInY' }, + { label: '左长半径旋转', value: 'rollIn' }, + { label: '由小变大进入', value: 'zoomIn' }, + { label: '左变大进入', value: 'zoomInLeft' }, + { label: '右变大进入', value: 'zoomInRight' }, + { label: '向上变大进入', value: 'zoomInUp' }, + { label: '向下变大进入', value: 'zoomInDown' }, + { label: '向右滑动展开', value: 'slideInLeft' }, + { label: '向左滑动展开', value: 'slideInRight' }, + { label: '向上滑动展开', value: 'slideInUp' }, + { label: '向下滑动展开', value: 'slideInDown' }, + ], + }, + { + label: '强调', + children: [ + { label: '弹跳', value: 'bounce' }, + { label: '闪烁', value: 'flash' }, + { label: '放大缩小', value: 'pulse' }, + { label: '放大缩小弹簧', value: 'rubberBand' }, + { label: '左右晃动', value: 'headShake' }, + { label: '左右扇形摇摆', value: 'swing' }, + { label: '放大晃动缩小', value: 'tada' }, + { label: '扇形摇摆', value: 'wobble' }, + { label: '左右上下晃动', value: 'jello' }, + { label: 'Y轴旋转', value: 'flip' }, + ], + }, + { + label: '退出', + children: [ + { label: '渐隐', value: 'fadeOut' }, + { label: '向左退出', value: 'fadeOutLeft' }, + { label: '向右退出', value: 'fadeOutRight' }, + { label: '向上退出', value: 'fadeOutUp' }, + { label: '向下退出', value: 'fadeOutDown' }, + { label: '向左长距退出', value: 'fadeOutLeftBig' }, + { label: '向右长距退出', value: 'fadeOutRightBig' }, + { label: '向上长距退出', value: 'fadeOutUpBig' }, + { label: '向下长距退出', value: 'fadeOutDownBig' }, + { label: '旋转退出', value: 'rotateOut' }, + { label: '左顺时针旋转', value: 'rotateOutDownLeft' }, + { label: '右逆时针旋转', value: 'rotateOutDownRight' }, + { label: '左逆时针旋转', value: 'rotateOutUpLeft' }, + { label: '右逆时针旋转', value: 'rotateOutUpRight' }, + { label: '弹出', value: 'bounceOut' }, + { label: '向左弹出', value: 'bounceOutLeft' }, + { label: '向右弹出', value: 'bounceOutRight' }, + { label: '向上弹出', value: 'bounceOutUp' }, + { label: '向下弹出', value: 'bounceOutDown' }, + { label: '中心X轴旋转', value: 'flipOutX' }, + { label: '中心Y轴旋转', value: 'flipOutY' }, + { label: '左长半径旋转', value: 'rollOut' }, + { label: '由小变大退出', value: 'zoomOut' }, + { label: '左变大退出', value: 'zoomOutLeft' }, + { label: '右变大退出', value: 'zoomOutRight' }, + { label: '向上变大退出', value: 'zoomOutUp' }, + { label: '向下变大退出', value: 'zoomOutDown' }, + { label: '向左滑动收起', value: 'slideOutLeft' }, + { label: '向右滑动收起', value: 'slideOutRight' }, + { label: '向上滑动收起', value: 'slideOutUp' }, + { label: '向下滑动收起', value: 'slideOutDown' }, + ], + }, +] diff --git a/frontend/src/utils/calculateComponentPositonAndSize.js b/frontend/src/utils/calculateComponentPositonAndSize.js new file mode 100644 index 0000000000..a0541f2f7c --- /dev/null +++ b/frontend/src/utils/calculateComponentPositonAndSize.js @@ -0,0 +1,273 @@ +/* eslint-disable no-lonely-if */ +import { calculateRotatedPointCoordinate, getCenterPoint } from './translate' + +const funcs = { + lt: calculateLeftTop, + t: calculateTop, + rt: calculateRightTop, + r: calculateRight, + rb: calculateRightBottom, + b: calculateBottom, + lb: calculateLeftBottom, + l: calculateLeft, +} + +function calculateLeftTop(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint } = pointInfo + let newCenterPoint = getCenterPoint(curPositon, symmetricPoint) + let newTopLeftPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -style.rotate) + let newBottomRightPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + + let newWidth = newBottomRightPoint.x - newTopLeftPoint.x + let newHeight = newBottomRightPoint.y - newTopLeftPoint.y + + if (needLockProportion) { + if (newWidth / newHeight > proportion) { + newTopLeftPoint.x += Math.abs(newWidth - newHeight * proportion) + newWidth = newHeight * proportion + } else { + newTopLeftPoint.y += Math.abs(newHeight - newWidth / proportion) + newHeight = newWidth / proportion + } + + // 由于现在求的未旋转前的坐标是以没按比例缩减宽高前的坐标来计算的 + // 所以缩减宽高后,需要按照原来的中心点旋转回去,获得缩减宽高并旋转后对应的坐标 + // 然后以这个坐标和对称点获得新的中心点,并重新计算未旋转前的坐标 + const rotatedTopLeftPoint = calculateRotatedPointCoordinate(newTopLeftPoint, newCenterPoint, style.rotate) + newCenterPoint = getCenterPoint(rotatedTopLeftPoint, symmetricPoint) + newTopLeftPoint = calculateRotatedPointCoordinate(rotatedTopLeftPoint, newCenterPoint, -style.rotate) + newBottomRightPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + + newWidth = newBottomRightPoint.x - newTopLeftPoint.x + newHeight = newBottomRightPoint.y - newTopLeftPoint.y + } + + if (newWidth > 0 && newHeight > 0) { + style.width = Math.round(newWidth) + style.height = Math.round(newHeight) + style.left = Math.round(newTopLeftPoint.x) + style.top = Math.round(newTopLeftPoint.y) + } +} + +function calculateRightTop(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint } = pointInfo + let newCenterPoint = getCenterPoint(curPositon, symmetricPoint) + let newTopRightPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -style.rotate) + let newBottomLeftPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + + let newWidth = newTopRightPoint.x - newBottomLeftPoint.x + let newHeight = newBottomLeftPoint.y - newTopRightPoint.y + + if (needLockProportion) { + if (newWidth / newHeight > proportion) { + newTopRightPoint.x -= Math.abs(newWidth - newHeight * proportion) + newWidth = newHeight * proportion + } else { + newTopRightPoint.y += Math.abs(newHeight - newWidth / proportion) + newHeight = newWidth / proportion + } + + const rotatedTopRightPoint = calculateRotatedPointCoordinate(newTopRightPoint, newCenterPoint, style.rotate) + newCenterPoint = getCenterPoint(rotatedTopRightPoint, symmetricPoint) + newTopRightPoint = calculateRotatedPointCoordinate(rotatedTopRightPoint, newCenterPoint, -style.rotate) + newBottomLeftPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + + newWidth = newTopRightPoint.x - newBottomLeftPoint.x + newHeight = newBottomLeftPoint.y - newTopRightPoint.y + } + + if (newWidth > 0 && newHeight > 0) { + style.width = Math.round(newWidth) + style.height = Math.round(newHeight) + style.left = Math.round(newBottomLeftPoint.x) + style.top = Math.round(newTopRightPoint.y) + } +} + +function calculateRightBottom(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint } = pointInfo + let newCenterPoint = getCenterPoint(curPositon, symmetricPoint) + let newTopLeftPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + let newBottomRightPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -style.rotate) + + let newWidth = newBottomRightPoint.x - newTopLeftPoint.x + let newHeight = newBottomRightPoint.y - newTopLeftPoint.y + + if (needLockProportion) { + if (newWidth / newHeight > proportion) { + newBottomRightPoint.x -= Math.abs(newWidth - newHeight * proportion) + newWidth = newHeight * proportion + } else { + newBottomRightPoint.y -= Math.abs(newHeight - newWidth / proportion) + newHeight = newWidth / proportion + } + + const rotatedBottomRightPoint = calculateRotatedPointCoordinate(newBottomRightPoint, newCenterPoint, style.rotate) + newCenterPoint = getCenterPoint(rotatedBottomRightPoint, symmetricPoint) + newTopLeftPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + newBottomRightPoint = calculateRotatedPointCoordinate(rotatedBottomRightPoint, newCenterPoint, -style.rotate) + + newWidth = newBottomRightPoint.x - newTopLeftPoint.x + newHeight = newBottomRightPoint.y - newTopLeftPoint.y + } + + if (newWidth > 0 && newHeight > 0) { + style.width = Math.round(newWidth) + style.height = Math.round(newHeight) + style.left = Math.round(newTopLeftPoint.x) + style.top = Math.round(newTopLeftPoint.y) + } +} + +function calculateLeftBottom(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint } = pointInfo + let newCenterPoint = getCenterPoint(curPositon, symmetricPoint) + let newTopRightPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + let newBottomLeftPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -style.rotate) + + let newWidth = newTopRightPoint.x - newBottomLeftPoint.x + let newHeight = newBottomLeftPoint.y - newTopRightPoint.y + + if (needLockProportion) { + if (newWidth / newHeight > proportion) { + newBottomLeftPoint.x += Math.abs(newWidth - newHeight * proportion) + newWidth = newHeight * proportion + } else { + newBottomLeftPoint.y -= Math.abs(newHeight - newWidth / proportion) + newHeight = newWidth / proportion + } + + const rotatedBottomLeftPoint = calculateRotatedPointCoordinate(newBottomLeftPoint, newCenterPoint, style.rotate) + newCenterPoint = getCenterPoint(rotatedBottomLeftPoint, symmetricPoint) + newTopRightPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -style.rotate) + newBottomLeftPoint = calculateRotatedPointCoordinate(rotatedBottomLeftPoint, newCenterPoint, -style.rotate) + + newWidth = newTopRightPoint.x - newBottomLeftPoint.x + newHeight = newBottomLeftPoint.y - newTopRightPoint.y + } + + if (newWidth > 0 && newHeight > 0) { + style.width = Math.round(newWidth) + style.height = Math.round(newHeight) + style.left = Math.round(newBottomLeftPoint.x) + style.top = Math.round(newTopRightPoint.y) + } +} + +function calculateTop(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint, curPoint } = pointInfo + let rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -style.rotate) + let rotatedTopMiddlePoint = calculateRotatedPointCoordinate({ + x: curPoint.x, + y: rotatedcurPositon.y, + }, curPoint, style.rotate) + + // 勾股定理 + let newHeight = Math.sqrt((rotatedTopMiddlePoint.x - symmetricPoint.x) ** 2 + (rotatedTopMiddlePoint.y - symmetricPoint.y) ** 2) + + if (newHeight > 0) { + const newCenter = { + x: rotatedTopMiddlePoint.x - (rotatedTopMiddlePoint.x - symmetricPoint.x) / 2, + y: rotatedTopMiddlePoint.y + (symmetricPoint.y - rotatedTopMiddlePoint.y) / 2, + } + + let width = style.width + // 因为调整的是高度 所以只需根据锁定的比例调整宽度即可 + if (needLockProportion) { + width = newHeight * proportion + } + + style.width = width + style.height = Math.round(newHeight) + style.top = Math.round(newCenter.y - (newHeight / 2)) + style.left = Math.round(newCenter.x - (style.width / 2)) + } +} + +function calculateRight(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint, curPoint } = pointInfo + const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -style.rotate) + const rotatedRightMiddlePoint = calculateRotatedPointCoordinate({ + x: rotatedcurPositon.x, + y: curPoint.y, + }, curPoint, style.rotate) + + let newWidth = Math.sqrt((rotatedRightMiddlePoint.x - symmetricPoint.x) ** 2 + (rotatedRightMiddlePoint.y - symmetricPoint.y) ** 2) + if (newWidth > 0) { + const newCenter = { + x: rotatedRightMiddlePoint.x - (rotatedRightMiddlePoint.x - symmetricPoint.x) / 2, + y: rotatedRightMiddlePoint.y + (symmetricPoint.y - rotatedRightMiddlePoint.y) / 2, + } + + let height = style.height + // 因为调整的是宽度 所以只需根据锁定的比例调整高度即可 + if (needLockProportion) { + height = newWidth / proportion + } + + style.height = height + style.width = Math.round(newWidth) + style.top = Math.round(newCenter.y - (style.height / 2)) + style.left = Math.round(newCenter.x - (newWidth / 2)) + } +} + +function calculateBottom(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint, curPoint } = pointInfo + const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -style.rotate) + const rotatedBottomMiddlePoint = calculateRotatedPointCoordinate({ + x: curPoint.x, + y: rotatedcurPositon.y, + }, curPoint, style.rotate) + + const newHeight = Math.sqrt((rotatedBottomMiddlePoint.x - symmetricPoint.x) ** 2 + (rotatedBottomMiddlePoint.y - symmetricPoint.y) ** 2) + if (newHeight > 0) { + const newCenter = { + x: rotatedBottomMiddlePoint.x - (rotatedBottomMiddlePoint.x - symmetricPoint.x) / 2, + y: rotatedBottomMiddlePoint.y + (symmetricPoint.y - rotatedBottomMiddlePoint.y) / 2, + } + + let width = style.width + // 因为调整的是高度 所以只需根据锁定的比例调整宽度即可 + if (needLockProportion) { + width = newHeight * proportion + } + + style.width = width + style.height = Math.round(newHeight) + style.top = Math.round(newCenter.y - (newHeight / 2)) + style.left = Math.round(newCenter.x - (style.width / 2)) + } +} + +function calculateLeft(style, curPositon, proportion, needLockProportion, pointInfo) { + const { symmetricPoint, curPoint } = pointInfo + const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -style.rotate) + const rotatedLeftMiddlePoint = calculateRotatedPointCoordinate({ + x: rotatedcurPositon.x, + y: curPoint.y, + }, curPoint, style.rotate) + + const newWidth = Math.sqrt((rotatedLeftMiddlePoint.x - symmetricPoint.x) ** 2 + (rotatedLeftMiddlePoint.y - symmetricPoint.y) ** 2) + if (newWidth > 0) { + const newCenter = { + x: rotatedLeftMiddlePoint.x - (rotatedLeftMiddlePoint.x - symmetricPoint.x) / 2, + y: rotatedLeftMiddlePoint.y + (symmetricPoint.y - rotatedLeftMiddlePoint.y) / 2, + } + + let height = style.height + if (needLockProportion) { + height = newWidth / proportion + } + + style.height = height + style.width = Math.round(newWidth) + style.top = Math.round(newCenter.y - (style.height / 2)) + style.left = Math.round(newCenter.x - (newWidth / 2)) + } +} + +export default function calculateComponentPositonAndSize(name, style, curPositon, proportion, needLockProportion, pointInfo) { + funcs[name](style, curPositon, proportion, needLockProportion, pointInfo) +} \ No newline at end of file diff --git a/frontend/src/utils/decomposeComponent.js b/frontend/src/utils/decomposeComponent.js new file mode 100644 index 0000000000..e3105b14cc --- /dev/null +++ b/frontend/src/utils/decomposeComponent.js @@ -0,0 +1,20 @@ +import { $ } from './utils' +import { mod360 } from './translate' + +// 将组合中的各个子组件拆分出来,并计算它们新的 style +export default function decomposeComponent(component, editorRect, parentStyle) { + const componentRect = $(`#component${component.id}`).getBoundingClientRect() + // 获取元素的中心点坐标 + const center = { + x: componentRect.left - editorRect.left + componentRect.width / 2, + y: componentRect.top - editorRect.top + componentRect.height / 2, + } + + component.style.rotate = mod360(component.style.rotate + parentStyle.rotate) + component.style.width = parseFloat(component.groupStyle.width) / 100 * parentStyle.width + component.style.height = parseFloat(component.groupStyle.height) / 100 * parentStyle.height + // 计算出元素新的 top left 坐标 + component.style.left = center.x - component.style.width / 2 + component.style.top = center.y - component.style.height / 2 + component.groupStyle = {} +} \ No newline at end of file diff --git a/frontend/src/utils/eventBus.js b/frontend/src/utils/eventBus.js new file mode 100644 index 0000000000..58ac2c9e94 --- /dev/null +++ b/frontend/src/utils/eventBus.js @@ -0,0 +1,3 @@ +import Vue from 'vue' +// 用于监听、触发事件 +export default new Vue() \ No newline at end of file diff --git a/frontend/src/utils/events.js b/frontend/src/utils/events.js new file mode 100644 index 0000000000..88cc427323 --- /dev/null +++ b/frontend/src/utils/events.js @@ -0,0 +1,39 @@ +// 编辑器自定义事件 +const events = { + redirect(url) { + if (url) { + window.location.href = url + } + }, + + alert(msg) { + if (msg) { + alert(msg) + } + }, +} + +const mixins = { + methods: events, +} + +const eventList = [ + { + key: 'redirect', + label: '跳转事件', + event: events.redirect, + param: '', + }, + { + key: 'alert', + label: 'alert 事件', + event: events.alert, + param: '', + }, +] + +export { + mixins, + events, + eventList, +} \ No newline at end of file diff --git a/frontend/src/utils/generateID.js b/frontend/src/utils/generateID.js new file mode 100644 index 0000000000..5dce36e3bc --- /dev/null +++ b/frontend/src/utils/generateID.js @@ -0,0 +1,5 @@ +let id = 0 +// 主要用于 Vue 的 diff 算法,为每个元素创建一个独一无二的 ID +export default function generateID() { + return id++ +} \ No newline at end of file diff --git a/frontend/src/utils/runAnimation.js b/frontend/src/utils/runAnimation.js new file mode 100644 index 0000000000..b1e63ecf26 --- /dev/null +++ b/frontend/src/utils/runAnimation.js @@ -0,0 +1,18 @@ +export default async function runAnimation($el, animations = []) { + const play = (animation) => new Promise(resolve => { + $el.classList.add(animation.value, 'animated') + const removeAnimation = () => { + $el.removeEventListener('animationend', removeAnimation) + $el.removeEventListener('animationcancel', removeAnimation) + $el.classList.remove(animation.value, 'animated') + resolve() + } + + $el.addEventListener('animationend', removeAnimation) + $el.addEventListener('animationcancel', removeAnimation) + }) + + for (let i = 0, len = animations.length; i < len; i++) { + await play(animations[i]) + } +} diff --git a/frontend/src/utils/shortcutKey.js b/frontend/src/utils/shortcutKey.js new file mode 100644 index 0000000000..80b91637e9 --- /dev/null +++ b/frontend/src/utils/shortcutKey.js @@ -0,0 +1,143 @@ +import store from '@/store' +import eventBus from '@/utils/eventBus' + +const ctrlKey = 17, + vKey = 86, // 粘贴 + cKey = 67, // 复制 + xKey = 88, // 剪切 + + yKey = 89, // 重做 + zKey = 90, // 撤销 + + gKey = 71, // 组合 + bKey = 66, // 拆分 + + lKey = 76, // 锁定 + uKey = 85, // 解锁 + + sKey = 83, // 保存 + pKey = 80, // 预览 + dKey = 68, // 删除 + deleteKey = 46, // 删除 + eKey = 69 // 清空画布 + +export const keycodes = [66, 67, 68, 69, 71, 76, 80, 83, 85, 86, 88, 89, 90] + +// 与组件状态无关的操作 +const basemap = { + [vKey]: paste, + [yKey]: redo, + [zKey]: undo, + [sKey]: save, + [pKey]: preview, + [eKey]: clearCanvas, +} + +// 组件锁定状态下可以执行的操作 +const lockMap = { + ...basemap, + [uKey]: unlock, +} + +// 组件未锁定状态下可以执行的操作 +const unlockMap = { + ...basemap, + [cKey]: copy, + [xKey]: cut, + [gKey]: compose, + [bKey]: decompose, + [dKey]: deleteComponent, + [deleteKey]: deleteComponent, + [lKey]: lock, +} + +let isCtrlDown = false +// 全局监听按键操作并执行相应命令 +export function listenGlobalKeyDown() { + window.onkeydown = (e) => { + const { curComponent } = store.state + if (e.keyCode == ctrlKey) { + isCtrlDown = true + } else if (e.keyCode == deleteKey && curComponent) { + store.commit('deleteComponent') + store.commit('recordSnapshot') + } else if (isCtrlDown) { + if (!curComponent || !curComponent.isLock) { + e.preventDefault() + unlockMap[e.keyCode] && unlockMap[e.keyCode]() + } else if (curComponent && curComponent.isLock) { + e.preventDefault() + lockMap[e.keyCode] && lockMap[e.keyCode]() + } + } + } + + window.onkeyup = (e) => { + if (e.keyCode == ctrlKey) { + isCtrlDown = false + } + } +} + +function copy() { + store.commit('copy') +} + +function paste() { + store.commit('paste') + store.commit('recordSnapshot') +} + +function cut() { + store.commit('cut') +} + +function redo() { + store.commit('redo') +} + +function undo() { + store.commit('undo') +} + +function compose() { + if (store.state.areaData.components.length) { + store.commit('compose') + store.commit('recordSnapshot') + } +} + +function decompose() { + const curComponent = store.state.curComponent + if (curComponent && !curComponent.isLock && curComponent.component == 'Group') { + store.commit('decompose') + store.commit('recordSnapshot') + } +} + +function save() { + eventBus.$emit('save') +} + +function preview() { + eventBus.$emit('preview') +} + +function deleteComponent() { + if (store.state.curComponent) { + store.commit('deleteComponent') + store.commit('recordSnapshot') + } +} + +function clearCanvas() { + eventBus.$emit('clearCanvas') +} + +function lock() { + store.commit('lock') +} + +function unlock() { + store.commit('unlock') +} diff --git a/frontend/src/utils/style.js b/frontend/src/utils/style.js new file mode 100644 index 0000000000..f4eb296958 --- /dev/null +++ b/frontend/src/utils/style.js @@ -0,0 +1,55 @@ +import { sin, cos } from '@/utils/translate' + +export function getStyle(style, filter = []) { + const needUnit = [ + 'fontSize', + 'width', + 'height', + 'top', + 'left', + 'borderWidth', + 'letterSpacing', + 'borderRadius', + ] + + const result = {} + Object.keys(style).forEach(key => { + if (!filter.includes(key)) { + if (key != 'rotate') { + result[key] = style[key] + + if (needUnit.includes(key)) { + result[key] += 'px' + } + } else { + result.transform = key + '(' + style[key] + 'deg)' + } + } + }) + + return result +} + +// 获取一个组件旋转 rotate 后的样式 +export function getComponentRotatedStyle(style) { + style = { ...style } + if (style.rotate != 0) { + const newWidth = style.width * cos(style.rotate) + style.height * sin(style.rotate) + const diffX = (style.width - newWidth) / 2 // 旋转后范围变小是正值,变大是负值 + style.left += diffX + style.right = style.left + newWidth + + const newHeight = style.height * cos(style.rotate) + style.width * sin(style.rotate) + const diffY = (newHeight - style.height) / 2 // 始终是正 + style.top -= diffY + style.bottom = style.top + newHeight + + style.width = newWidth + style.height = newHeight + } else { + style.bottom = style.top + style.height + style.right = style.left + style.width + } + + return style +} \ No newline at end of file diff --git a/frontend/src/utils/toast.js b/frontend/src/utils/toast.js new file mode 100644 index 0000000000..fad84177b7 --- /dev/null +++ b/frontend/src/utils/toast.js @@ -0,0 +1,9 @@ +import { Message } from 'element-ui' + +export default function toast(message = '', type = 'error', duration = 1500) { + Message({ + message, + type, + duration, + }) +} \ No newline at end of file diff --git a/frontend/src/utils/translate.js b/frontend/src/utils/translate.js new file mode 100644 index 0000000000..00627a3184 --- /dev/null +++ b/frontend/src/utils/translate.js @@ -0,0 +1,127 @@ +import store from '@/store' + +// 角度转弧度 +// Math.PI = 180 度 +function angleToRadian(angle) { + return angle * Math.PI / 180 +} + +/** + * 计算根据圆心旋转后的点的坐标 + * @param {Object} point 旋转前的点坐标 + * @param {Object} center 旋转中心 + * @param {Number} rotate 旋转的角度 + * @return {Object} 旋转后的坐标 + * https://www.zhihu.com/question/67425734/answer/252724399 旋转矩阵公式 + */ +export function calculateRotatedPointCoordinate(point, center, rotate) { + /** + * 旋转公式: + * 点a(x, y) + * 旋转中心c(x, y) + * 旋转后点n(x, y) + * 旋转角度θ tan ?? + * nx = cosθ * (ax - cx) - sinθ * (ay - cy) + cx + * ny = sinθ * (ax - cx) + cosθ * (ay - cy) + cy + */ + + return { + x: (point.x - center.x) * Math.cos(angleToRadian(rotate)) - (point.y - center.y) * Math.sin(angleToRadian(rotate)) + center.x, + y: (point.x - center.x) * Math.sin(angleToRadian(rotate)) + (point.y - center.y) * Math.cos(angleToRadian(rotate)) + center.y, + } +} + +/** + * 获取旋转后的点坐标(八个点之一) + * @param {Object} style 样式 + * @param {Object} center 组件中心点 + * @param {String} name 点名称 + * @return {Object} 旋转后的点坐标 + */ +export function getRotatedPointCoordinate(style, center, name) { + let point // point 是未旋转前的坐标 + switch (name) { + case 't': + point = { + x: style.left + (style.width / 2), + y: style.top, + } + + break + case 'b': + point = { + x: style.left + (style.width / 2), + y: style.top + style.height, + } + + break + case 'l': + point = { + x: style.left, + y: style.top + style.height / 2, + } + + break + case 'r': + point = { + x: style.left + style.width, + y: style.top + style.height / 2, + } + + break + case 'lt': + point = { + x: style.left, + y: style.top, + } + + break + case 'rt': + point = { + x: style.left + style.width, + y: style.top, + } + + break + case 'lb': + point = { + x: style.left, + y: style.top + style.height, + } + + break + default: // rb + point = { + x: style.left + style.width, + y: style.top+ style.height, + } + + break + } + + return calculateRotatedPointCoordinate(point, center, style.rotate) +} + +// 求两点之间的中点坐标 +export function getCenterPoint(p1, p2) { + return { + x: p1.x + ((p2.x - p1.x) / 2), + y: p1.y + ((p2.y - p1.y) / 2), + } +} + +export function sin(rotate) { + return Math.abs(Math.sin(angleToRadian(rotate))) +} + +export function cos(rotate) { + return Math.abs(Math.cos(angleToRadian(rotate))) +} + +export function mod360(deg) { + return (deg + 360) % 360 +} + +export function changeStyleWithScale(value) { + return value * parseInt(store.state.canvasStyleData.scale) / 100 +} \ No newline at end of file diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js new file mode 100644 index 0000000000..4900a4b28c --- /dev/null +++ b/frontend/src/utils/utils.js @@ -0,0 +1,26 @@ +export function deepCopy(target) { + if (typeof target == 'object') { + const result = Array.isArray(target)? [] : {} + for (const key in target) { + if (typeof target[key] == 'object') { + result[key] = deepCopy(target[key]) + } else { + result[key] = target[key] + } + } + + return result + } + + return target +} + +export function swap(arr, i, j) { + const temp = arr[i] + arr[i] = arr[j] + arr[j] = temp +} + +export function $(selector) { + return document.querySelector(selector) +} \ No newline at end of file diff --git a/frontend/src/views/chart/components/ChartComponent.vue b/frontend/src/views/chart/components/ChartComponent.vue index 88230a6c14..d54552337c 100644 --- a/frontend/src/views/chart/components/ChartComponent.vue +++ b/frontend/src/views/chart/components/ChartComponent.vue @@ -1,5 +1,5 @@ @@ -11,6 +11,7 @@ import { baseLineOption, stackLineOption } from '../chart/line/line' import { basePieOption } from '../chart/pie/pie' import { baseFunnelOption } from '../chart/funnel/funnel' import { baseRadarOption } from '../chart/radar/radar' +import eventBus from '@/utils/eventBus' export default { name: 'ChartComponent', @@ -41,9 +42,15 @@ export default { // 基于准备好的dom,初始化echarts实例 this.myChart = this.$echarts.init(document.getElementById(this.chartId)) this.drawEcharts() + + // 监听元素变动事件 + eventBus.$on('resizing', (componentId) => { + this.chartResize() + }) }, methods: { drawEcharts() { + debugger const chart = this.chart let chart_option = {} // type diff --git a/frontend/src/views/panel/ViewSelect/index.vue b/frontend/src/views/panel/ViewSelect/index.vue index 327c01ed6b..5d114c128c 100644 --- a/frontend/src/views/panel/ViewSelect/index.vue +++ b/frontend/src/views/panel/ViewSelect/index.vue @@ -11,10 +11,13 @@ :data="data" :props="defaultProps" :render-content="renderNode" - default-expand-all - :filter-node-method="filterNode" + draggable + :allow-drop="allowDrop" + :allow-drag="allowDrag" + @node-drag-start="handleDragStart" /> +
@@ -82,8 +85,8 @@ export default { }, renderNode(h, { node, data, store }) { return ( -
this.detail(data)} on-dblclick={() => this.addView2Drawing(data.id)} > - {node.label} +
this.detail(data)} on-dblclick={() => this.addView2Drawing(data.id)}> + {node.label} {data.type !== 'group' && data.type !== 'scene' ? ( @@ -102,31 +105,54 @@ export default { this.detailItem = null }, addView2Drawing(viewId) { - // viewInfo(viewId).then(res => { - // const info = res.data - // this.$emit('panel-view-add', info) - // }) + // viewInfo(viewId).then(res => { + // const info = res.data + // this.$emit('panel-view-add', info) + // }) bus.$emit('panel-view-add', { id: viewId }) - // this.$emit('panel-view-add', viewId) + // this.$emit('panel-view-add', viewId) + }, + handleDragStart(node, ev) { + ev.dataTransfer.effectAllowed = 'copy' + const dataTrans = { + type: 'view', + id: node.data.id + } + ev.dataTransfer.setData('componentInfo', JSON.stringify(dataTrans)) + // bus.$emit('component-on-drag') + }, + + // 判断节点能否被拖拽 + allowDrag(draggingNode) { + if (draggingNode.data.type === 'scene') { + return false + } else { + return true + } + }, + + allowDrop(draggingNode, dropNode, type) { + return false } + } } diff --git a/frontend/src/views/panel/canvas/index.vue b/frontend/src/views/panel/canvas/index.vue new file mode 100644 index 0000000000..c5d8639249 --- /dev/null +++ b/frontend/src/views/panel/canvas/index.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/frontend/src/views/panel/edit/index.vue b/frontend/src/views/panel/edit/index.vue index ca432acad4..b3101a99fe 100644 --- a/frontend/src/views/panel/edit/index.vue +++ b/frontend/src/views/panel/edit/index.vue @@ -73,19 +73,27 @@
-
-
+
+
- - + +
- +
+ +
@@ -98,8 +106,16 @@ import DeAsideContainer from '@/components/dataease/DeAsideContainer' import { addClass, removeClass } from '@/utils' import FilterGroup from '../filter' import ViewSelect from '../ViewSelect' -import DrawingBoard from '../DrawingBoard' import bus from '@/utils/bus' +import Editor from '@/components/Editor/index' +import { deepCopy } from '@/utils/utils' +import componentList from '@/custom-component/component-list' // 左侧列表数据 +import generateID from '@/utils/generateID' +import { listenGlobalKeyDown } from '@/utils/shortcutKey' +import { mapState } from 'vuex' +import { uuid } from 'vue-uuid' + + export default { components: { DeMainContainer, @@ -107,16 +123,24 @@ export default { DeAsideContainer, FilterGroup, ViewSelect, - DrawingBoard + Editor }, data() { return { show: false, clickNotClose: false, - showIndex: -1 + showIndex: -1, + activeName: 'attr', + reSelectAnimateIndex: undefined } }, + computed: mapState([ + 'componentData', + 'curComponent', + 'isClickComponent', + 'canvasStyleData' + ]), watch: { show(value) { if (value && !this.clickNotClose) { @@ -129,8 +153,16 @@ export default { } } }, + created() { + this.restore() + // 全局监听按键事件 + listenGlobalKeyDown() + }, mounted() { this.insertToBody() + bus.$on('component-on-drag', () => { + this.show = false + }) }, beforeDestroy() { const elx = this.$refs.rightPanel @@ -171,6 +203,73 @@ export default { }, preViewShow() { bus.$emit('panel-drawing-preview') + }, + + // 画布 + restore() { + // 用保存的数据恢复画布 + if (localStorage.getItem('canvasData')) { + this.$store.commit('setComponentData', this.resetID(JSON.parse(localStorage.getItem('canvasData')))) + } + + if (localStorage.getItem('canvasStyle')) { + this.$store.commit('setCanvasStyle', JSON.parse(localStorage.getItem('canvasStyle'))) + } + }, + + resetID(data) { + data.forEach(item => { + item.id = uuid.v1() + }) + + return data + }, + handleDrop(e) { + let component + console.log('handleDrop123') + const componentInfo = JSON.parse(e.dataTransfer.getData('componentInfo')) + if (componentInfo.type === 'view') { + componentList.forEach(componentTemp => { + if (componentTemp.type === 'view') { + component = deepCopy(componentTemp) + component.style.top = e.offsetY + component.style.left = e.offsetX + component.id = uuid.v1() + const propValue = { + id: component.id, + viewId: componentInfo.id + } + component.propValue = propValue + } + }) + } + this.$store.commit('addComponent', { component }) + this.$store.commit('recordSnapshot') + }, + + handleDragOver(e) { + console.log('handleDragOver123') + e.preventDefault() + e.dataTransfer.dropEffect = 'copy' + }, + + handleMouseDown() { + console.log('handleMouseDown123') + + this.$store.commit('setClickComponentStatus', false) + }, + + deselectCurComponent(e) { + console.log('deselectCurComponent123') + + if (!this.isClickComponent) { + this.$store.commit('setCurComponent', { component: null, index: null }) + } + + // 0 左击 1 滚轮 2 右击 + if (e.button != 2) { + this.$store.commit('hideContextMenu') + } } } @@ -215,7 +314,7 @@ export default { .leftPanel { width: 100%; - max-width: 260px; + max-width: 200px; height: calc(100vh - 91px); position: fixed; top: 91px; diff --git a/frontend/src/views/panel/list/PanelPreview.vue b/frontend/src/views/panel/list/PanelPreview.vue deleted file mode 100644 index f2ff68f4d1..0000000000 --- a/frontend/src/views/panel/list/PanelPreview.vue +++ /dev/null @@ -1,393 +0,0 @@ - - - - -