add animejs + wasm to vue

This commit is contained in:
Enrique Hernandez 2020-10-08 23:17:47 -05:00
commit c3562172c4
14 changed files with 483 additions and 131 deletions

View file

@ -14,12 +14,14 @@
},
"main": "background.js",
"dependencies": {
"animejs": "^3.2.0",
"core-js": "^3.6.5",
"vue": "^3.0.0-0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@types/animejs": "^3.1.2",
"@types/jest": "^24.0.19",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",

View file

@ -1,30 +1,27 @@
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<div id="app">
<router-view/>
</div>
<router-view/>
</template>
<style lang="scss">
html,
body {
margin: 0;
padding: 0;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
width: 100vw;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>

View file

@ -1,64 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest" target="_blank" rel="noopener">unit-jest</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'HelloWorld',
props: {
msg: String,
},
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View file

@ -0,0 +1,136 @@
<template>
<g
:id="item.id"
:class="item.modifier"
:transform="startingOffset"
>
<rect
ref="bubble"
id="#bubble"
x="30"
y="10"
width="196"
height="63"
rx="20"
:fill="bubbleFill"
/>
<text
font-size="14"
font-family="SF Pro"
id="#message"
ref="content"
:fill="textFill"
>
<tspan
dy="16"
x="0"
v-for="(item, index) in textArr"
:key="index"
>
{{ item }}
</tspan>
</text>
<g :transform="signature">
<image
xlink:href="profile.png"
width="29"
height="29"
fill="#b5b5b5"
clip-path="url(#clip)"
/>
<text
transform="translate(36, 17)"
fill="#888"
font-size="10"
font-family="SFCompactDisplay-Bold, SF Compact Display"
font-weight="700"
>
<tspan v-if="item.outgoing">You to</tspan>
<tspan v-else>From</tspan>
<tspan xml:space="preserve" fill="#000"> {{ item.signature }}</tspan>
</text>
</g>
</g>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: "MessageItem",
props: {
item: {
type: Object,
required: true
},
},
data() {
return {
textArr: [],
signature: ""
};
},
computed: {
textFill() {
switch (this.item.modifier) {
case "ai":
return "#fff";
default:
return "#000";
}
},
bubbleFill() {
switch (this.item.modifier) {
case "sf":
return "#61b4f4";
case "ai":
return "#585858";
default:
return "#fff";
}
},
startingOffset() {
switch (this.item.modifier) {
case "sf":
return "translate(10, 600)";
case "ai":
return "translate(135, 600)";
default:
return "translate(20, 600)";
}
}
},
async mounted() {
await this.createTextBox(this.item.content, 32);
await this.adjustRect();
this.signaturePosition();
},
methods: {
signaturePosition() {
const height = Number(this.$refs.bubble.getAttribute("height"));
const p = -10;
this.signature = `translate(-10 ${height + p})`;
},
createTextBox(s, w) {
// return tspan elements for text with correct size
const wrap = (s, w) =>
s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, "g"),
"$1\n"
);
this.textArr = wrap(s, w).split("\n");
},
adjustRect() {
// must get dimensions of text box to fit bubble around
const padding = 20;
const bbox = this.$refs.content.getBBox();
const bubble = this.$refs.bubble;
bubble.setAttribute("x", bbox.x - padding);
bubble.setAttribute("y", bbox.y - padding);
bubble.setAttribute("width", bbox.width + 2 * padding);
bubble.setAttribute("height", bbox.height + 2 * padding);
}
}
});
</script>
<style></style>

View file

@ -0,0 +1,52 @@
const Messages = [
{
content:
"I can be there at 5 pm tomorrow to greet you at a Chick file with my almond and chive salad.",
signature: "Tommy McConville",
outgoing: true,
modifier: "sf",
imgURL: "../assets/logo.png",
id: "1"
},
{
content: "Sounds good, see you Thursday.",
signature: "Josie Wright",
outgoing: false,
modifier: "fr",
imgURL: "@/assets/logo.png",
id: "2"
},
{
content: "Hey Anna, did you get the paper done?",
signature: "Tommy McConville",
outgoing: false,
modifier: "fr",
imgURL: "@/assets/logo.png",
id: "3"
},
{
content: "Yes posting it online soon.",
signature: "Tommy McConville",
outgoing: true,
modifier: "sf",
imgURL: "../../assets/logo.png",
id: "4"
},
{
content: "Sweet, thanks!",
signature: "Tommy McConville",
outgoing: false,
modifier: "fr",
imgURL: "../../assets/logo.png",
id: "5"
},
{
content: "Hey do you know Enrique Hernandez?",
signature: "Crimata AI",
outgoing: false,
modifier: "ai",
imgURL: "../../assets/logo.png",
id: "6"
}
];
export default Messages;

205
src/components/UI/index.vue Normal file
View file

@ -0,0 +1,205 @@
<template>
<svg
id="messenger"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<linearGradient
id="linear-gradient"
x1="0.5"
y1="-1.438"
x2="0.5"
y2="0.633"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#fff33b" stop-opacity="0" />
<stop offset="0.152" stop-color="#ffde46" stop-opacity="0.141" />
<stop offset="0.467" stop-color="#ffa764" stop-opacity="0.435" />
<stop offset="0.581" stop-color="#ff916f" stop-opacity="0.541" />
<stop offset="1" stop-color="#ff5782" />
</linearGradient>
<linearGradient
id="linear-gradient-2"
x1="0.498"
y1="0.728"
x2="0.498"
y2="-0.433"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#f7cf83" />
<stop offset="1" stop-color="#fff" />
</linearGradient>
<rect id="rect" width="29" height="29" rx="10" />
<clipPath id="clip">
<use xlink:href="#rect" />
</clipPath>
</defs>
<g id="cardsDiv">
<g
v-for="(item, index) in renderArr"
:key="index"
id="cards"
transform="translate(0, 0)"
>
<transition
appear
v-on:before-appear="beforeEnter"
v-on:appear="enter"
v-on:appear-cancelled="leave"
>
<MessageItem
:v-if="item"
:item="item"
:key="index"
/>
</transition>
</g>
</g>
<g id="titlebar" transform="translate(0, 0)">
<g data-name="Group 73" transform="translate(20 20)">
<ellipse
id="Oval"
cx="8.134"
cy="8.134"
rx="8.134"
ry="8.134"
transform="translate(0 0)"
fill="url(#linear-gradient)"
/>
<ellipse
id="Oval-2"
data-name="Oval"
cx="8.134"
cy="8.134"
rx="8.134"
ry="8.134"
transform="translate(27 0)"
fill="url(#linear-gradient-2)"
/>
</g>
</g>
</svg>
</template>
<script lang="ts">
import { defineComponent, inject } from "vue";
import MessageItem from './UIDetails/messageItem.vue';
// @ts-ignore
import {Messages} from "./UIDetails/messages.js";
export default defineComponent({
name: 'UIindex',
components: { MessageItem },
setup() {
const anime = inject('animejs');
let timeline;
console.log(Messages);
const setTimeLine = () => {
timeline = anime.timeline({ loop: true });
}
setTimeLine();
return {
timeline
}
},
// data() {
// return {
// renderArr: [],
// messages: [],
// timeline: null,
// msgOffset: {
// x: null,
// y: null
// },
// prevCardH: 0
// };
// },
// mounted() {
// this.setTimeLine();
// this.runAnim();
// },
// methods: {
// setTimeLine() {
// this.timeline = this.anime.timeline({ loop: true });
// },
// runAnim() {
// this.messages = Messages;
// for (let i = 0; i < this.messages.length; i++) {
// this.renderMsg(i);
// }
// },
// renderMsg(i) {
// setTimeout(() => {
// this.renderArr.push(this.messages[i]);
// }, 2000);
// },
// async beforeEnter(el) {},
// async enter(el, done) {
// const winW = document.getElementById("messenger").clientWidth;
// const modifier = el.className.baseVal.substring(0, 2);
// const msg = await document.getElementById(el.id);
// const rect = msg.getBoundingClientRect();
// const msgW = rect.width;
// const msgH = rect.height;
// const paddingLeft = 40;
// const cardsDiv = await document.getElementById("cardsDiv");
// const cdBox = cardsDiv.getBBox();
//
// if (this.prevCardH === 0) {
// this.msgOffset.y += cdBox.height;
// } else if (this.prevCardH > msgH) {
// // cardsDiv is offset by 17.5
// this.msgOffset.y += this.prevCardH + msgH / 2 + 17.5;
// } else {
// this.msgOffset.y += cdBox.height - 20;
// }
// this.prevCardH = msgH;
//
// switch (modifier) {
// case "sf":
// this.msgOffset.x = winW - msgW;
// break;
// case "ai":
// this.msgOffset.x = (winW - msgW) / 2;
// break;
// default:
// this.msgOffset.x = paddingLeft;
// }
//
// const translateUp = `translate(${this.msgOffset.x} ${this.msgOffset.y})`;
// const translateDown = `translate(${this.msgOffset.x} ${this.msgOffset.y +
// 5})`;
// this.timeline
// .add({
// targets: el,
// transform: translateUp,
// easing: "easeInOutQuad",
// duration: 1000
// })
// .add({
// targets: el,
// transform: translateDown,
// easing: "easeInOutQuad",
// duration: 500,
// offset: 1000
// });
// done();
// },
// leave(el, done) {
// console.log("leaving");
// }
// }
});
</script>
<style lang="scss" scoped>
#messenger {
width: 350px;
height: 500px;
background-color: #E6E6E6;
box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.3);
border-radius: 20px;
}
</style>

View file

@ -1,9 +1,12 @@
import { createApp } from "vue";
import { createApp, provide, inject } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import anime from "animejs/lib/anime.es.js";
createApp(App)
.use(store)
.use(router)
.provide("animejs", anime)
.mount("#app");

16
src/plugins/anime.ts Normal file
View file

@ -0,0 +1,16 @@
import { provide, inject } from "vue";
const animeSymbol = Symbol();
export function provideAnime(anime: any) {
provide(animeSymbol, anime);
}
export function useAnime() {
const anime = inject(animeSymbol);
if (!anime) {
console.log("Error! Failed to provide AnimeJS");
// throw error, no store provided
}
return anime;
}

View file

@ -1,25 +1,17 @@
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Home from '../views/Home.vue'
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import HomeIndex from "../views/home/index.vue";
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
path: "/",
name: "Home",
component: HomeIndex
}
]
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
});
export default router
export default router;

10
src/shims-vue.d.ts vendored
View file

@ -1,5 +1,7 @@
declare module '*.vue' {
import { defineComponent } from 'vue'
const component: ReturnType<typeof defineComponent>
export default component
declare module "*.vue" {
import { defineComponent } from "vue";
const component: ReturnType<typeof defineComponent>;
export default component;
}
declare module "animejs/lib/anime.es.js";

View file

@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View file

@ -1,24 +0,0 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
export default defineComponent({
name: 'Home',
components: {
HelloWorld,
},
async mounted(){
await import("../../crate/pkg").then(async module => {
const result = await module.add(1, 2);
console.log('testing rust', result)
})
}
});
</script>

30
src/views/home/index.vue Normal file
View file

@ -0,0 +1,30 @@
<template>
Hello from vue home view
<!-- <UIindex /> -->
</template>
<script lang="ts">
import { defineComponent, onMounted } from 'vue';
import UIindex from "@/components/UI/index.vue";
export default defineComponent({
name: "HomeIndex",
components: { UIindex },
setup() {
let result;
const getCwasm = async () => {
await import('../../../crate/pkg').then(async module => {
result = await module.add(1, 2);
console.log(result);
});
}
onMounted(() => {
getCwasm();
})
},
});
</script>
<style></style>

View file

@ -1173,6 +1173,11 @@
dependencies:
defer-to-connect "^2.0.0"
"@types/animejs@^3.1.2":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@types/animejs/-/animejs-3.1.2.tgz#93289a902b0b9bfac3020d29413139dcca6acdbc"
integrity sha512-GyyuoztWbZMErx9brJQChvvfeMd5vplWU/H6BYVl7JUcLoMaDOVxlqF3d4ypB7TzXnbgnMSykGmjAVVRsvhHhQ==
"@types/anymatch@*":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
@ -2259,6 +2264,11 @@ amdefine@>=0.0.4:
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
animejs@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/animejs/-/animejs-3.2.0.tgz#2daea7365983249022cc454fa726acdb4a84345d"
integrity sha512-zhtGl5cS0G2f5DfciMO8uwWpnx06nfFnHlXYYXHBazHep1Lyd6kEtBQP+9hpYKE0dBZjIigHp9VpMO95ZfXQJQ==
ansi-align@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"