From 5db39b5855b5ce0a3dcf54bf3ca69d36fabd55d7 Mon Sep 17 00:00:00 2001 From: wangjiahao <1522128093@qq.com> Date: Thu, 25 Mar 2021 19:16:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=8D=A2=E7=94=BB=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/panel/api/ViewApi.java | 6 + frontend/package.json | 16 +- frontend/src/assets/iconfont/demo.css | 539 +++ frontend/src/assets/iconfont/demo_index.html | 307 ++ frontend/src/assets/iconfont/iconfont.css | 40 + frontend/src/assets/iconfont/iconfont.eot | Bin 0 -> 3120 bytes frontend/src/assets/iconfont/iconfont.js | 1 + frontend/src/assets/iconfont/iconfont.json | 51 + frontend/src/assets/iconfont/iconfont.svg | 44 + frontend/src/assets/iconfont/iconfont.ttf | Bin 0 -> 2952 bytes frontend/src/assets/iconfont/iconfont.woff | Bin 0 -> 2096 bytes frontend/src/assets/iconfont/iconfont.woff2 | Bin 0 -> 1656 bytes frontend/src/assets/title.jpg | Bin 0 -> 72368 bytes frontend/src/components/AnimationList.vue | 118 + frontend/src/components/AttrList.vue | 81 + frontend/src/components/ComponentList.vue | 67 + frontend/src/components/Editor/Area.vue | 31 + .../components/Editor/ComponentWrapper.vue | 46 + .../src/components/Editor/ContextMenu.vue | 126 + frontend/src/components/Editor/Grid.vue | 32 + frontend/src/components/Editor/MarkLine.vue | 249 ++ frontend/src/components/Editor/Preview.vue | 88 + frontend/src/components/Editor/Shape.vue | 388 ++ frontend/src/components/Editor/index.vue | 307 ++ frontend/src/components/EventList.vue | 80 + frontend/src/components/Modal.vue | 49 + frontend/src/components/Toolbar.vue | 254 ++ frontend/src/custom-component/Group.vue | 67 + frontend/src/custom-component/Picture.vue | 23 + frontend/src/custom-component/RectShape.vue | 23 + frontend/src/custom-component/UserView.vue | 57 + frontend/src/custom-component/VButton.vue | 47 + frontend/src/custom-component/VText.vue | 118 + .../src/custom-component/component-list.js | 104 + frontend/src/custom-component/index.js | 16 + frontend/src/main.js | 8 + frontend/src/router/index.js | 12 + frontend/src/store/animation.js | 11 + frontend/src/store/compose.js | 100 + frontend/src/store/contextmenu.js | 18 + frontend/src/store/copy.js | 67 + frontend/src/store/event.js | 11 + frontend/src/store/index.js | 98 +- frontend/src/store/layer.js | 42 + frontend/src/store/lock.js | 11 + frontend/src/store/snapshot.js | 33 + frontend/src/styles/animate.css | 3614 +++++++++++++++++ frontend/src/styles/reset.css | 46 + frontend/src/utils/animationClassData.js | 94 + .../utils/calculateComponentPositonAndSize.js | 273 ++ frontend/src/utils/decomposeComponent.js | 20 + frontend/src/utils/eventBus.js | 3 + frontend/src/utils/events.js | 39 + frontend/src/utils/generateID.js | 5 + frontend/src/utils/runAnimation.js | 18 + frontend/src/utils/shortcutKey.js | 143 + frontend/src/utils/style.js | 55 + frontend/src/utils/toast.js | 9 + frontend/src/utils/translate.js | 127 + frontend/src/utils/utils.js | 26 + .../views/chart/components/ChartComponent.vue | 9 +- frontend/src/views/panel/ViewSelect/index.vue | 56 +- frontend/src/views/panel/canvas/index.vue | 180 + frontend/src/views/panel/edit/index.vue | 117 +- .../src/views/panel/list/PanelPreview.vue | 393 -- 65 files changed, 8586 insertions(+), 427 deletions(-) create mode 100644 frontend/src/assets/iconfont/demo.css create mode 100644 frontend/src/assets/iconfont/demo_index.html create mode 100644 frontend/src/assets/iconfont/iconfont.css create mode 100644 frontend/src/assets/iconfont/iconfont.eot create mode 100644 frontend/src/assets/iconfont/iconfont.js create mode 100644 frontend/src/assets/iconfont/iconfont.json create mode 100644 frontend/src/assets/iconfont/iconfont.svg create mode 100644 frontend/src/assets/iconfont/iconfont.ttf create mode 100644 frontend/src/assets/iconfont/iconfont.woff create mode 100644 frontend/src/assets/iconfont/iconfont.woff2 create mode 100644 frontend/src/assets/title.jpg create mode 100644 frontend/src/components/AnimationList.vue create mode 100644 frontend/src/components/AttrList.vue create mode 100644 frontend/src/components/ComponentList.vue create mode 100644 frontend/src/components/Editor/Area.vue create mode 100644 frontend/src/components/Editor/ComponentWrapper.vue create mode 100644 frontend/src/components/Editor/ContextMenu.vue create mode 100644 frontend/src/components/Editor/Grid.vue create mode 100644 frontend/src/components/Editor/MarkLine.vue create mode 100644 frontend/src/components/Editor/Preview.vue create mode 100644 frontend/src/components/Editor/Shape.vue create mode 100644 frontend/src/components/Editor/index.vue create mode 100644 frontend/src/components/EventList.vue create mode 100644 frontend/src/components/Modal.vue create mode 100644 frontend/src/components/Toolbar.vue create mode 100644 frontend/src/custom-component/Group.vue create mode 100644 frontend/src/custom-component/Picture.vue create mode 100644 frontend/src/custom-component/RectShape.vue create mode 100644 frontend/src/custom-component/UserView.vue create mode 100644 frontend/src/custom-component/VButton.vue create mode 100644 frontend/src/custom-component/VText.vue create mode 100644 frontend/src/custom-component/component-list.js create mode 100644 frontend/src/custom-component/index.js create mode 100644 frontend/src/store/animation.js create mode 100644 frontend/src/store/compose.js create mode 100644 frontend/src/store/contextmenu.js create mode 100644 frontend/src/store/copy.js create mode 100644 frontend/src/store/event.js create mode 100644 frontend/src/store/layer.js create mode 100644 frontend/src/store/lock.js create mode 100644 frontend/src/store/snapshot.js create mode 100644 frontend/src/styles/animate.css create mode 100644 frontend/src/styles/reset.css create mode 100644 frontend/src/utils/animationClassData.js create mode 100644 frontend/src/utils/calculateComponentPositonAndSize.js create mode 100644 frontend/src/utils/decomposeComponent.js create mode 100644 frontend/src/utils/eventBus.js create mode 100644 frontend/src/utils/events.js create mode 100644 frontend/src/utils/generateID.js create mode 100644 frontend/src/utils/runAnimation.js create mode 100644 frontend/src/utils/shortcutKey.js create mode 100644 frontend/src/utils/style.js create mode 100644 frontend/src/utils/toast.js create mode 100644 frontend/src/utils/translate.js create mode 100644 frontend/src/utils/utils.js create mode 100644 frontend/src/views/panel/canvas/index.vue delete mode 100644 frontend/src/views/panel/list/PanelPreview.vue 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 + + + + + + + + + + + +
+

+ +
+
+
    + +
  • + +
    向右旋转
    +
    &#xe66a;
    +
  • + +
  • + +
    图片
    +
    &#xe616;
    +
  • + +
  • + +
    +
    &#xe672;
    +
  • + +
  • + +
    矩形
    +
    &#xe790;
    +
  • + +
  • + +
    文本
    +
    &#xe652;
    +
  • + +
  • + +
    按钮
    +
    &#xe648;
    +
  • + +
+
+

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,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') 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 0000000000000000000000000000000000000000..1c944e6357cdba3d6ca06cec3eecf927a54f38d0 GIT binary patch literal 3120 zcmd^B|8HAY6+h=a`@Psn>^$4gU*>$V{c6WFiIduK+_YKRbX~GlX;-q4(rKb|6FXn( zZ*uIClw}HSw{8Qe`4x#@x(dNU1RIEI5{1IpmPxGB1p5JLv>KDTYkb|^ZmPlcNF-XBQlpzC2C=%m@31W&cPu9%V2fH8wJ*2sxKhdnydRPV?dPW}q z+JRSUKfgodZekBS_IP)6;Jc5WCHZNRCnmGWLgM_Xuao=<<(t!)#q{`R(N_WZ4*?AC z&7_i3-m4qO$d+X2n;}BH$v;o{D&-wB+0sHY0rGvHisnpyGRZCTx5@uKlAE&0g#rfP z52SaK?8_yysloBu5HM(+9Aj%9`E`tx~>6jLYK@fD7WCP#|7@>}PfqRG8i&~9;gD8^k)|nyQ7t+!6 z!&9)o;=Hw@&7WU}Wirrb&${|wnN&u~>@0R_X84@$w@fT+p_6KwDe(pvC)uK|icoP@ z9;u8~<|@ToD;u;5+hwxcOh`UkADf6ky&|Wlqa%mnwAGS!O$VYemcIrn3fn;Vj%n4xi#?nC-@|`PcaaKvxgrVgyAC zih|8GhzZ4l?GbDb*%Gbj68Yagwg0v3m47#I8*e=G`+?4V?=D?jw>90ln)>0M6zBX) z^5~N&e0t^0+UG`GOrJtguL$)4t>Nc+N=lLP9LK{Tk zAYB4K4Md`m`CCzzoB|4t1TQ#a{%BuZ>J2GuoEDTFPM7T1i_r&ZoCzhalA^4{NuufS zgVpDngNVUq?u<8N2v*ZZqkx=|J7eHD!zb9&>?I0y2ES{HM}>jv&6ea)UMK%~`{{8G>_`T2AA{>2a7JI_DOy;fb}3IkR8m9qK&k-x(4r$4bSWP8%*S9VSgj_|I5F@y-R!N0w2WSVR#IV(Q|i-9=UJ9Ik*VlgCD>z z;7tH~Eb5dUf=GMn5QPp*$gyZ2+fAwy47ilIY9j6uM5l{vMWLOk;=Q46ObjB`9jF;O z$cA*}MeV3=|7F^DF)k)6bP`yTq?Hvh7LUahIhKgI2+2x2Xl+j&yc9 zdvwV9*x6%?P3spY0>&jf;Y$>nl8f-}m1Tb5l94#|y8ZV#W$?g*Pjv(?o2 zfb)e(#qN}4si{c{_$E&bk9fT{>)3$9L*|L010Hun!&ehdTb;?d`=zU+%HeX?;kQN= z&*;w%Kf9xhO5il7yuP#0)sgQCdOfCcv&ZzOYHOgYE5Lme=tAtW2b-&By`hkodxp;V zXKh~l1A@`(4m4TbJ}4XJ8V%>$xTM{%+BvnR6Jdj!>N`acWXkN6u(FOM4RdrOT6D}4zeC3c!t`FN_C~r3 zy*d_%-=||Ubijy?>%mT;6upWD6Z!0fXLXDeO#M{H9KCecbj%ZfUB?E(H*{>IuH2gtO*{im7BNHRU_G=$oC)=hFFH$-D{1Qq$#3vbZU3))T4X{A@nw z+ZE~Aq()M?RB^MN`P0+UQYr0A7xP))Fg2LUWb(d3F+Z1@EJbEYrNW->Zq^awBa`_o zJ^l~Tv{Q7&N>onKr8^0WRBn$y&0k`^7>#bX-!9)jQr&JfLSx8LE`Dh|^KhDC-zbf; zL@}|Ca*<-*ETLgNLQYX!oFVLkf~uRNb|%R>0yC;t0ro&QeVY;WrhP<>w6pA>sQ+22 zS?qVeB+@&}qXCU5ph!`hv@n~@O)uul3*}_)%nTJmsaznApD*Wyx$?qnZd&+eDt9uK N6Hb;(rF>3p^*`Yz_Rs(T literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1ac8c5bdec1dd0fbb23dc9718e25a6227e21e9ac GIT binary patch literal 2952 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 literal 0 HcmV?d00001 diff --git a/frontend/src/assets/iconfont/iconfont.woff2 b/frontend/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d57cf5038a029c86b26425f97f5128433e0cc2e3 GIT binary patch literal 1656 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 O=x9g|`;g@Se-i*9SCT;h literal 0 HcmV?d00001 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 @@ - - - - -