Merge branch 'scroll_animate' into 'master'

Scroll animate

See merge request crimata-ai/crimata-electron!3
This commit is contained in:
Enrique Hernandez 2020-11-01 21:29:26 +00:00
commit b02243873c
21 changed files with 1038 additions and 299 deletions

2
.gitignore vendored
View file

@ -2,7 +2,7 @@
node_modules
/dist
/crate/target/*
/.log
# local env files
.env.local

View file

@ -15,11 +15,13 @@
},
"main": "background.js",
"dependencies": {
"@types/animejs": "^3.1.2",
"@types/bindings": "^1.3.0",
"@types/uuid": "^8.3.0",
"@types/ws": "^7.2.7",
"animejs": "^3.2.0",
"core-js": "^3.6.5",
"mitt": "^2.1.0",
"speech-recorder": "^1.2.1",
"vue": "^3.0.0-0",
"vue-router": "^4.0.0-0",
@ -27,7 +29,6 @@
"ws": "^7.3.1"
},
"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

@ -8,26 +8,26 @@ import * as path from "path";
import { SpeechRecorder } from "speech-recorder";
import WebSocket from "ws";
const ws = new WebSocket("ws://localhost:8080");
ws.on("open", function open() {
ws.send("hello fro client");
});
ws.on("message", (message: any) => {
console.log("received message", message);
});
const recorder = new SpeechRecorder();
recorder.start({
onAudio: (audio: object, speech: boolean) => {
if (speech) {
console.log(audio);
ws.send(audio);
}
console.log("recording");
// writeStream.write(audio);
}
});
// const ws = new WebSocket("ws://localhost:8080");
// ws.on("open", function open() {
// ws.send("hello fro client");
// });
// ws.on("message", (message: any) => {
// console.log("received message", message);
// });
//
// const recorder = new SpeechRecorder();
//
// recorder.start({
// onAudio: (audio: object, speech: boolean) => {
// if (speech) {
// console.log(audio);
// ws.send(audio);
// }
// console.log("recording");
// // writeStream.write(audio);
// }
// });
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.

View file

@ -0,0 +1,48 @@
<template>
<foreignObject x="10" y="10" width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml">
<div contenteditable
v-if="input.length > 0"
>
{{ input }}
</div>
</div>
</foreignObject>
</template>
<script lang="ts">
import useKeyDownHandler from "@/composables/keyDownHandler/useKeyDownHandler";
import { defineComponent, onUnmounted } from "vue";
export default defineComponent({
name: "InputItem",
setup() {
const {keyDownHandler, input} = useKeyDownHandler();
// add window event listener
window.addEventListener("keydown", keyDownHandler);
// remove Event Listener on component unMount
onUnmounted(() => {
window.removeEventListener("keydown", keyDownHandler);
});
return {
input
};
}
})
</script>
<style lang="scss" scoped>
div[contenteditable] {
width: auto;
max-width: 200px;
display: table;
padding: 7px 15px 7px 15px;
border: 0;
border-radius: 25px;
color: #fff;
background-color: #61b4f4;
font-size: 14;
text-align: left;
}
</style>

View file

@ -5,8 +5,7 @@
:transform = "startingOffset"
>
<rect
ref="bubbleTag"
id="#bubble"
ref="messageRect"
x="30"
y="10"
width="196"
@ -17,20 +16,19 @@
<text
font-size="14"
font-family="SF Pro"
id="#message"
ref="contentTag"
ref="messageText"
:fill="textFill"
>
<tspan
dy="16"
x="0"
v-for="(item, index) in textArr"
:key="index"
dy="16"
x="0"
v-for="(item, index) in textArr"
:key="index"
>
{{ item }}
</tspan>
</text>
<g :transform="signature">
<g :transform="signatureTranslate">
<image
xlink:href="profile.png"
width="29"
@ -54,8 +52,11 @@
</template>
<script lang="ts">
import { defineComponent, computed, onMounted, ref, reactive } from 'vue';
export default defineComponent({
import { defineComponent, onMounted, ref, reactive } from 'vue';
import useMessageItemComp from "@/composables/computed/useMessageItemComp";
import useMessageItemAnims from "@/composables/anims/useMessageItemAnims";
import useTextWrap from "@/composables/textWrap/useTextWrap";
export default defineComponent({
name: "MessageItem",
props: {
item: {
@ -64,99 +65,38 @@ import { defineComponent, computed, onMounted, ref, reactive } from 'vue';
},
},
setup(props) {
let textArr: string[] = reactive([]);
let textArr: string[] = reactive([]);
const signature = ref("");
const msgID = ref(props.item.id);
const bubbleTag = ref(`bubble${msgID.value}`);
const contentTag = ref(`content${msgID.value}`);
let bubble: any;
let content: any;
const msgID = ref(props.item.id);
const messageRect = ref(`message${msgID.value}`);
const messageText = ref(`text${msgID.value}`);
const textFill = computed(() => {
switch (props.item.modifier) {
case "ai":
return "#fff";
default:
return "#000";
}
});
const { textFill, bubbleFill, startingOffset } = useMessageItemComp(props.item.modifier);
const { textWrap } = useTextWrap();
const { drawMessage, signatureTranslate } = useMessageItemAnims({
mr: messageRect,
tr: messageText,
})
const bubbleFill = computed(() => {
switch (props.item.modifier) {
case "sf":
return "#61b4f4";
case "ai":
return "#585858";
default:
return "#fff";
}
});
// wraps message text before render
textArr = textWrap({
s: props.item.content,
w: 32
});
const startingOffset = computed(() => {
switch (props.item.modifier) {
case "sf":
return "translate(10, 600)";
case "ai":
return "translate(135, 600)";
default:
return "translate(20, 600)";
}
})
function signaturePosition(): void {
if (bubble.getAttribute("height") !== null) {
const height = Number(bubble.getAttribute("height"));
const p = -10;
signature.value = `translate(-10 ${height + p})`;
}
}
function createTextBox(s: string, w: number): void {
// return tspan elements for text with correct size
const wrap = (s: string, w: number) =>
s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, "g"),
"$1\n"
);
textArr = wrap(s, w).split("\n");
}
function adjustRect(): void {
// must get dimensions of text box to fit bubble around
const padding = 20;
const bbox = content.getBBox();
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);
}
// make textbox before mount
createTextBox(props.item.content, 32);
onMounted(async () => {
// update bubble and content for current message
bubble = bubbleTag.value;
content = contentTag.value;
await adjustRect();
signaturePosition();
})
return {
textFill,
bubbleFill,
startingOffset,
signaturePosition,
textArr,
signature,
contentTag,
msgID,
bubbleTag
}
onMounted(() => {
drawMessage();
})
return {
textFill,
bubbleFill,
startingOffset,
textArr,
signatureTranslate,
messageText,
messageRect
}
},
});
});
</script>
<style></style>

View file

@ -1,11 +1,5 @@
interface Message {
content: string;
signature: string;
outgoing: boolean;
modifier: string;
imgURL: string;
id: string;
}
import { Message } from "@/types/message/index";
const Messages: Message[] = [
{
content:

View file

@ -4,6 +4,7 @@
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
@click="renderMessage"
>
<defs>
<linearGradient
@ -37,24 +38,24 @@
</clipPath>
</defs>
<g id="cardsDiv">
<g
v-for="(item, index) in renderArr"
:key="index"
id="cards"
transform="translate(0, 0)"
<g
v-for="(item, index) in renderArr"
:key="index"
:id="index"
class="test"
transform="translate(0, 0)"
>
<transition
appear
v-on:before-appear="beforeEnter"
v-on:appear="enter"
>
<transition
appear
v-on:before-appear="beforeEnter"
v-on:appear="enter"
v-on:appear-cancelled="leave"
>
<MessageItem
:item="item"
:key="index"
/>
</transition>
</g>
<MessageItem
:item="item"
:key="index"
/>
</transition>
</g>
</g>
<g id="titlebar" transform="translate(0, 0)">
<g data-name="Group 73" transform="translate(20 20)">
@ -83,118 +84,37 @@
</template>
<script lang="ts">
import { defineComponent, inject, onMounted, reactive } from "vue";
import MessageItem from './UIDetails/messageItem.vue';
import Messages from "./UIDetails/messages";
export default defineComponent({
name: 'UIindex',
components: { MessageItem },
setup() {
import { defineComponent, ref, reactive } from "vue";
import MessageItem from './UIDetails/messageItem.vue';
import InputItem from "./UIDetails/inputItem.vue";
import Messages from "./UIDetails/messages";
import useUIindexAnims from "@/composables/anims/useUIindexAnims";
export default defineComponent({
name: 'UIindex',
components: { MessageItem },
setup() {
const renderArr: Message[] = reactive([])
const messages = Messages;
const count = ref(0);
interface ProtectedRef {
readonly el: SVGElement | HTMLElement;
reference: any;
height: number | null;
}
const anime: object | any = inject('animejs');
const timeline: typeof anime = anime.timeline({ loop: true });
const renderArr: Message[] = reactive([])
const messages = Messages;
const msgOffset = {
x: 0 ,
y: 0
}
let messenger: any;
let winW: number;
let modifier: string;
let msg: any;
let cardsDiv: any;
let prevCardH = 0;
const { beforeEnter, enter } = useUIindexAnims();
function renderMsg(i: number): void {
setTimeout(() => {
renderArr.push(messages[i]);
}, 2000);
}
const renderMessage = () => {
if (count.value < messages.length) {
renderArr.push(messages[count.value])
count.value++;
return;
}
}
function runAnim(): void {
for (let i = 0; i < messages.length; i++) {
renderMsg(i);
}
}
async function beforeEnter(el: any) {
modifier = el.className.baseVal.substring(0, 2);
}
async function enter(el: any, done: any) {
const paddingLeft = 40;
msg = await document.getElementById(el.id);
const rect = msg.getBoundingClientRect();
const msgW = rect.width;
const msgH = rect.height;
cardsDiv = await document.getElementById("cardsDiv");
const cdBox = cardsDiv.getBBox();
if (prevCardH === 0) {
msgOffset.y += cdBox.height;
} else if (prevCardH > msgH) {
// cardsDiv is offset by 17.5
msgOffset.y += prevCardH + msgH / 2 + 17.5;
} else {
msgOffset.y += cdBox.height - 20;
}
prevCardH = msgH;
switch (modifier) {
case "sf":
msgOffset.x = winW - msgW;
break;
case "ai":
msgOffset.x = (winW - msgW) / 2;
break;
default:
msgOffset.x = paddingLeft;
}
const translateUp = `translate(${msgOffset.x} ${msgOffset.y})`;
const translateDown = `translate(${msgOffset.x} ${msgOffset.y + 5})`;
timeline
.add({
targets: el,
transform: translateUp,
easing: "easeInOutQuad",
duration: 1000
})
.add({
targets: el,
transform: translateDown,
easing: "easeInOutQuad",
duration: 500,
offset: 1000
});
done();
}
function leave() {
}
onMounted(() => {
messenger = document.getElementById('messenger');
winW = messenger.clientWidth;
runAnim();
});
return {
// timeline,
renderArr,
msgOffset,
beforeEnter,
enter,
leave
}
},
});
return {
renderMessage,
renderArr,
beforeEnter,
enter,
}
},
});
</script>
<style lang="scss" scoped>

View file

@ -0,0 +1,49 @@
import { ref, onMounted } from "vue";
interface Ref<A> {
value: A;
}
export default function useMessageItemAnims({
mr,
tr,
}: {
mr: Ref<string>;
tr: Ref<string>;
}) {
const signatureTranslate = ref("");
let messageRect: SVGSVGElement & SVGRectElement;
let messageText: SVGSVGElement & SVGTextElement;
onMounted(async () => {
messageRect = (mr.value as unknown) as SVGSVGElement & SVGRectElement;
messageText = (tr.value as unknown) as SVGSVGElement & SVGTextElement;
});
function adjustMessageRect(): void {
// must get dimensions of text box to fit bubble around
const padding = 20;
const textBox = messageText.getBBox();
messageRect.setAttribute("x", String(textBox.x - padding));
messageRect.setAttribute("y", String(textBox.y - padding));
messageRect.setAttribute("width", String(textBox.width + 2 * padding));
messageRect.setAttribute("height", String(textBox.height + 2 * padding));
}
function translateMsgSignature(): void {
if (messageRect.getAttribute("height") !== null) {
const height = Number(messageRect.getAttribute("height"));
const p = -10;
signatureTranslate.value = `translate(-10 ${height + p})`;
}
}
async function drawMessage() {
// ignore lint error: await needed for proper render
await adjustMessageRect();
translateMsgSignature();
}
return {
drawMessage,
signatureTranslate,
};
}

View file

@ -0,0 +1,148 @@
import { inject, onMounted } from "vue";
import AnimeFunc from "@/types/animejs/index";
import { MessageAnimeFunc } from '@/types/message/index';
export default function useUIindexAnims() {
// imports animejs safely
let anime: AnimeFunc;
const animeInject: AnimeFunc | undefined = inject("animejs");
if (animeInject) anime = animeInject;
const msgOffset = {
x: 0,
y: 0
};
let winW: number;
let modifier: string;
let msg: HTMLElement | null;
let prevCardH = 0;
let totalHeight = 0;
let messenger: HTMLElement | null;
let renderList: NodeList;
let scrollAnim = false;
let scroll = 0;
onMounted(() => {
messenger = document.getElementById("messenger");
if (messenger) winW = messenger.clientWidth;
});
function calculateAllMessageHeight(): void {
const messages = document.querySelectorAll(".test");
if (messages.length) {
renderList = messages;
for (const message of messages) {
const el = message as SVGGElement;
totalHeight += el.getBBox().height;
}
scrollAnim = totalHeight > 500 ? true : false;
}
}
function beforeEnter(el: SVGGElement): void {
modifier = el.className.baseVal.substring(0, 2);
if (!scrollAnim) calculateAllMessageHeight();
}
function scrollOffset(l: NodeList, height: number): void {
for (const card of l) {
const c = card.parentNode as HTMLElement;
anime({
targets: c,
transform: `translate(0 ${height})`,
easing: "easeInOutQuad",
duration: 500,
});
}
}
const scrollAnimate: MessageAnimeFunc = (el, transform) => {
anime({
targets: el, transform: transform.initialTranslate,
easing: "easeInOutQuad",
duration: 500,
begin: function() {
scrollOffset(renderList, scroll);
},
complete: function() {
anime({
targets: el,
transform: transform.finalTranslate,
offset: 1000
});
}
});
}
const stackAnimate: MessageAnimeFunc = (el, transform) => {
anime({
targets: el,
transform: transform.initialTranslate,
easing: "easeInOutQuad",
duration: 1000,
complete: function() {
anime({
targets: el,
transform: transform.finalTranslate,
easing: "easeInOutQuad",
duration: 500,
offset: 1000
});
}
});
}
function addMessage(el: SVGGElement): void {
const transform = {
initialTranslate: `translate(${msgOffset.x} ${msgOffset.y})`,
finalTranslate: `translate(${msgOffset.x} ${msgOffset.y + 5})`
}
scrollAnim ? scrollAnimate(el, transform) : stackAnimate(el, transform)
}
function setXoffset(messageWidth: number): void {
const paddingLeft = 40;
switch (modifier) {
case "sf":
msgOffset.x = winW - messageWidth;
break;
case "ai":
msgOffset.x = (winW - messageWidth) / 2;
break;
default:
msgOffset.x = paddingLeft;
}
}
function setYoffset(messageHeight: number): void {
if (prevCardH === 0) {
msgOffset.y += messageHeight;
} else {
msgOffset.y += prevCardH + messageHeight / 2 + 17.5;
}
prevCardH = messageHeight;
}
function calculateMessageOffsets(msg: HTMLElement): void {
const msgRect = msg.getBoundingClientRect();
if (scrollAnim) {
// update scroll
scroll -= msgRect.height + msgRect.height / 2;
}
setXoffset(msgRect.width)
setYoffset(msgRect.height)
}
async function enter(el: SVGGElement): Promise<void> {
msg = await document.getElementById(el.id);
if (msg) calculateMessageOffsets(msg);
addMessage(el);
}
return {
beforeEnter,
enter,
msgOffset,
addMessage
};
}

View file

@ -1,17 +1,17 @@
import { computed } from "vue";
export default function useMessageItemComp(modifier: string): string {
// takes in message modifier as param
export default function useMessageItemComp(m: string) {
const textFill = computed(() => {
switch (modifier) {
case "ai":
return "#fff";
default:
switch (m) {
case "fr":
return "#000";
default:
return "#fff";
}
});
const bubbleFill = computed(() => {
switch (modifier) {
switch (m) {
case "sf":
return "#61b4f4";
case "ai":
@ -22,15 +22,16 @@ export default function useMessageItemComp(modifier: string): string {
});
const startingOffset = computed(() => {
switch (modifier) {
switch (m) {
case "sf":
return "translate(10, 600)";
return "translate(115, 600)";
case "ai":
return "translate(135, 600)";
return "translate(200, 600)";
default:
return "translate(20, 600)";
}
});
return {
textFill,
bubbleFill,

View file

@ -0,0 +1,263 @@
// This has the UnShifted and Shifted characters that each key maps to
// Ones that are to be ignored for character input are empty.
const keyboardCharMap = [
["", ""], // [0]
["", ""], // [1]
["", ""], // [2]
["", ""], // [3]
["", ""], // [4]
["", ""], // [5]
["", ""], // [6]
["", ""], // [7]
["", ""], // [8]
["", ""], // [9]
["", ""], // [10]
["", ""], // [11]
["", ""], // [12]
["\r", "\r"], // [13] - MOST control characters are ignored. This one (Carriage Return, or "Enter") is significant!
["", ""], // [14]
["", ""], // [15]
["", ""], // [16]
["", ""], // [17]
["", ""], // [18]
["", ""], // [19]
["", ""], // [20]
["", ""], // [21]
["", ""], // [22]
["", ""], // [23]
["", ""], // [24]
["", ""], // [25]
["", ""], // [26]
["", ""], // [27]
["", ""], // [28]
["", ""], // [29]
["", ""], // [30]
["", ""], // [31]
[" ", " "], // [32] // SPACE! Don't "clean it up" and remove the space!
["", ""], // [33]
["", ""], // [34]
["", ""], // [35]
["", ""], // [36]
["", ""], // [37]
["", ""], // [38]
["", ""], // [39]
["", ""], // [40]
["", ""], // [41]
["", ""], // [42]
["", ""], // [43]
["", ""], // [44]
["", ""], // [45]
["", ""], // [46]
["", ""], // [47]
["0", ")"], // [48]
["1", "!"], // [49]
["2", "@"], // [50]
["3", "#"], // [51]
["4", "$"], // [52]
["5", "%"], // [53]
["6", "^"], // [54]
["7", "&"], // [55]
["8", "*"], // [56]
["9", "("], // [57]
["", ""], // [58]
[";", ":"], // [59]
["<", ""], // [60]
["=", ""], // [61]
[">", ""], // [62]
["?", ""], // [63] shifted; else "/"
["", ""], // [64]
["a", "A"], // [65]
["b", "B"], // [66]
["c", "C"], // [67]
["d", "D"], // [68]
["e", "E"], // [69]
["f", "F"], // [70]
["g", "G"], // [71]
["h", "H"], // [72]
["i", "I"], // [73]
["j", "J"], // [74]
["k", "K"], // [75]
["l", "L"], // [76]
["m", "M"], // [77]
["n", "N"], // [78]
["o", "O"], // [79]
["p", "P"], // [80]
["q", "Q"], // [81]
["r", "R"], // [82]
["s", "S"], // [83]
["t", "T"], // [84]
["u", "U"], // [85]
["v", "V"], // [86]
["w", "W"], // [87]
["x", "X"], // [88]
["y", "Y"], // [89]
["z", "Z"], // [90]
["", ""], // [91] Windows Key (Windows) or Command Key (Mac)
["", ""], // [92]
["", ""], // [93]
["", ""], // [94]
["", ""], // [95]
// Number Keypad Entries...
["0", ""], // [96]
["1", ""], // [97]
["2", ""], // [98]
["3", ""], // [99]
["4", ""], // [100]
["5", ""], // [101]
["6", ""], // [102]
["7", ""], // [103]
["8", ""], // [104]
["9", ""], // [105]
["*", ""], // [106]
["+", ""], // [107]
["", ""], // [108]
["-", ""], // [109]
[".", ""], // [110]
["/", ""], // [111]
["", ""], // [112]
["", ""], // [113]
["", ""], // [114]
["", ""], // [115]
["", ""], // [116]
["", ""], // [117]
["", ""], // [118]
["", ""], // [119]
["", ""], // [120]
["", ""], // [121]
["", ""], // [122]
["", ""], // [123]
["", ""], // [124]
["", ""], // [125]
["", ""], // [126]
["", ""], // [127]
["", ""], // [128]
["", ""], // [129]
["", ""], // [130]
["", ""], // [131]
["", ""], // [132]
["", ""], // [133]
["", ""], // [134]
["", ""], // [135]
["", ""], // [136]
["", ""], // [137]
["", ""], // [138]
["", ""], // [139]
["", ""], // [140]
["", ""], // [141]
["", ""], // [142]
["", ""], // [143]
["", ""], // [144]
["", ""], // [145]
["", ""], // [146]
["", ""], // [147]
["", ""], // [148]
["", ""], // [149]
["", ""], // [150]
["", ""], // [151]
["", ""], // [152]
["", ""], // [153]
["", ""], // [154]
["", ""], // [155]
["", ""], // [156]
["", ""], // [157]
["", ""], // [158]
["", ""], // [159]
["", ""], // [160]
["", ""], // [161]
["", ""], // [162]
["", ""], // [163]
["", ""], // [164]
["", ""], // [165]
["", ""], // [166]
["", ""], // [167]
["", ""], // [168]
["", ""], // [169]
["", ""], // [170]
["", ""], // [171]
["", ""], // [172]
["", ""], // [173]
["", ""], // [174]
["", ""], // [175]
["", ""], // [176]
["", ""], // [177]
["", ""], // [178]
["", ""], // [179]
["", ""], // [180]
["", ""], // [181]
["", ""], // [182]
["", ""], // [183]
["", ""], // [184]
["", ""], // [185]
[";", ":"], // [186]
["=", "+"], // [187]
[",", "<"], // [188]
["-", "_"], // [189]
[".", ">"], // [190]
["/", "?"], // [191]
["`", "~"], // [192]
["", ""], // [193]
["", ""], // [194]
["", ""], // [195]
["", ""], // [196]
["", ""], // [197]
["", ""], // [198]
["", ""], // [199]
["", ""], // [200]
["", ""], // [201]
["", ""], // [202]
["", ""], // [203]
["", ""], // [204]
["", ""], // [205]
["", ""], // [206]
["", ""], // [207]
["", ""], // [208]
["", ""], // [209]
["", ""], // [210]
["", ""], // [211]
["", ""], // [212]
["", ""], // [213]
["", ""], // [214]
["", ""], // [215]
["", ""], // [216]
["", ""], // [217]
["", ""], // [218]
["[", "{"], // [219]
["\\", "|"], // [220]
["]", "}"], // [221]
["'", '"'], // [222]
["", ""], // [223]
["", ""], // [224]
["", ""], // [225]
["", ""], // [226]
["", ""], // [227]
["", ""], // [228]
["", ""], // [229]
["", ""], // [230]
["", ""], // [231]
["", ""], // [232]
["", ""], // [233]
["", ""], // [234]
["", ""], // [235]
["", ""], // [236]
["", ""], // [237]
["", ""], // [238]
["", ""], // [239]
["", ""], // [240]
["", ""], // [241]
["", ""], // [242]
["", ""], // [243]
["", ""], // [244]
["", ""], // [245]
["", ""], // [246]
["", ""], // [247]
["", ""], // [248]
["", ""], // [249]
["", ""], // [250]
["", ""], // [251]
["", ""], // [252]
["", ""], // [253]
["", ""], // [254]
["", ""] // [255]
];
export default keyboardCharMap;

View file

@ -0,0 +1,261 @@
// names of known key codes (0-255)
const keyboardNameMap = [
"", // [0]
"", // [1]
"", // [2]
"CANCEL", // [3]
"", // [4]
"", // [5]
"HELP", // [6]
"", // [7]
"BACK_SPACE", // [8]
"TAB", // [9]
"", // [10]
"", // [11]
"CLEAR", // [12]
"ENTER", // [13]
"ENTER_SPECIAL", // [14]
"", // [15]
"SHIFT", // [16]
"CONTROL", // [17]
"ALT", // [18]
"PAUSE", // [19]
"CAPS_LOCK", // [20]
"KANA", // [21]
"EISU", // [22]
"JUNJA", // [23]
"FINAL", // [24]
"HANJA", // [25]
"", // [26]
"ESCAPE", // [27]
"CONVERT", // [28]
"NONCONVERT", // [29]
"ACCEPT", // [30]
"MODECHANGE", // [31]
"SPACE", // [32]
"PAGE_UP", // [33]
"PAGE_DOWN", // [34]
"END", // [35]
"HOME", // [36]
"LEFT", // [37]
"UP", // [38]
"RIGHT", // [39]
"DOWN", // [40]
"SELECT", // [41]
"PRINT", // [42]
"EXECUTE", // [43]
"PRINTSCREEN", // [44]
"INSERT", // [45]
"DELETE", // [46]
"", // [47]
"0", // [48]
"1", // [49]
"2", // [50]
"3", // [51]
"4", // [52]
"5", // [53]
"6", // [54]
"7", // [55]
"8", // [56]
"9", // [57]
"COLON", // [58]
"SEMICOLON", // [59]
"LESS_THAN", // [60]
"EQUALS", // [61]
"GREATER_THAN", // [62]
"QUESTION_MARK", // [63]
"AT", // [64]
"A", // [65]
"B", // [66]
"C", // [67]
"D", // [68]
"E", // [69]
"F", // [70]
"G", // [71]
"H", // [72]
"I", // [73]
"J", // [74]
"K", // [75]
"L", // [76]
"M", // [77]
"N", // [78]
"O", // [79]
"P", // [80]
"Q", // [81]
"R", // [82]
"S", // [83]
"T", // [84]
"U", // [85]
"V", // [86]
"W", // [87]
"X", // [88]
"Y", // [89]
"Z", // [90]
"OS_KEY", // [91] Windows Key (Windows) or Command Key (Mac)
"", // [92]
"CONTEXT_MENU", // [93]
"", // [94]
"SLEEP", // [95]
"NUMPAD0", // [96]
"NUMPAD1", // [97]
"NUMPAD2", // [98]
"NUMPAD3", // [99]
"NUMPAD4", // [100]
"NUMPAD5", // [101]
"NUMPAD6", // [102]
"NUMPAD7", // [103]
"NUMPAD8", // [104]
"NUMPAD9", // [105]
"MULTIPLY", // [106]
"ADD", // [107]
"SEPARATOR", // [108]
"SUBTRACT", // [109]
"DECIMAL", // [110]
"DIVIDE", // [111]
"F1", // [112]
"F2", // [113]
"F3", // [114]
"F4", // [115]
"F5", // [116]
"F6", // [117]
"F7", // [118]
"F8", // [119]
"F9", // [120]
"F10", // [121]
"F11", // [122]
"F12", // [123]
"F13", // [124]
"F14", // [125]
"F15", // [126]
"F16", // [127]
"F17", // [128]
"F18", // [129]
"F19", // [130]
"F20", // [131]
"F21", // [132]
"F22", // [133]
"F23", // [134]
"F24", // [135]
"", // [136]
"", // [137]
"", // [138]
"", // [139]
"", // [140]
"", // [141]
"", // [142]
"", // [143]
"NUM_LOCK", // [144]
"SCROLL_LOCK", // [145]
"WIN_OEM_FJ_JISHO", // [146]
"WIN_OEM_FJ_MASSHOU", // [147]
"WIN_OEM_FJ_TOUROKU", // [148]
"WIN_OEM_FJ_LOYA", // [149]
"WIN_OEM_FJ_ROYA", // [150]
"", // [151]
"", // [152]
"", // [153]
"", // [154]
"", // [155]
"", // [156]
"", // [157]
"", // [158]
"", // [159]
"CIRCUMFLEX", // [160]
"EXCLAMATION", // [161]
"DOUBLE_QUOTE", // [162]
"HASH", // [163]
"DOLLAR", // [164]
"PERCENT", // [165]
"AMPERSAND", // [166]
"UNDERSCORE", // [167]
"OPEN_PAREN", // [168]
"CLOSE_PAREN", // [169]
"ASTERISK", // [170]
"PLUS", // [171]
"PIPE", // [172]
"HYPHEN_MINUS", // [173]
"OPEN_CURLY_BRACKET", // [174]
"CLOSE_CURLY_BRACKET", // [175]
"TILDE", // [176]
"", // [177]
"", // [178]
"", // [179]
"", // [180]
"VOLUME_MUTE", // [181]
"VOLUME_DOWN", // [182]
"VOLUME_UP", // [183]
"", // [184]
"", // [185]
"SEMICOLON", // [186]
"EQUALS", // [187]
"COMMA", // [188]
"MINUS", // [189]
"PERIOD", // [190]
"SLASH", // [191]
"BACK_QUOTE", // [192]
"", // [193]
"", // [194]
"", // [195]
"", // [196]
"", // [197]
"", // [198]
"", // [199]
"", // [200]
"", // [201]
"", // [202]
"", // [203]
"", // [204]
"", // [205]
"", // [206]
"", // [207]
"", // [208]
"", // [209]
"", // [210]
"", // [211]
"", // [212]
"", // [213]
"", // [214]
"", // [215]
"", // [216]
"", // [217]
"", // [218]
"OPEN_BRACKET", // [219]
"BACK_SLASH", // [220]
"CLOSE_BRACKET", // [221]
"QUOTE", // [222]
"", // [223]
"META", // [224]
"ALTGR", // [225]
"", // [226]
"WIN_ICO_HELP", // [227]
"WIN_ICO_00", // [228]
"", // [229]
"WIN_ICO_CLEAR", // [230]
"", // [231]
"", // [232]
"WIN_OEM_RESET", // [233]
"WIN_OEM_JUMP", // [234]
"WIN_OEM_PA1", // [235]
"WIN_OEM_PA2", // [236]
"WIN_OEM_PA3", // [237]
"WIN_OEM_WSCTRL", // [238]
"WIN_OEM_CUSEL", // [239]
"WIN_OEM_ATTN", // [240]
"WIN_OEM_FINISH", // [241]
"WIN_OEM_COPY", // [242]
"WIN_OEM_AUTO", // [243]
"WIN_OEM_ENLW", // [244]
"WIN_OEM_BACKTAB", // [245]
"ATTN", // [246]
"CRSEL", // [247]
"EXSEL", // [248]
"EREOF", // [249]
"PLAY", // [250]
"ZOOM", // [251]
"", // [252]
"PA1", // [253]
"WIN_OEM_CLEAR", // [254]
"" // [255]
];
export default keyboardNameMap;

View file

@ -0,0 +1,69 @@
import { ref, inject } from "vue";
import keyboardNameMap from "./keyboardMaps/keyboardNameMap";
import keyboardCharMap from "./keyboardMaps/keyboardCharMap";
// This catches keys from a physical keyboard, a soft keyboard on a tablet, or a
// scanner attached via USB
export default function useKeyDownHandler() {
interface KeyDownEvent {
keyCode: number;
shiftKey: boolean;
}
const emitter: any = inject("mitt");
const input = ref("");
// submits newSelfMessage event
function handleEnterKey() {
// does nothing if input value is empty
if (input.value === "") {
return;
}
const message = {
content: input.value,
signature: "EnriqueH",
outgoing: true,
modifier: "sf",
imgURL: "/test/path",
id: null
};
// fire event with message payload
emitter.emit("newSelfMessage", message);
}
function keyDownHandler(e: KeyDownEvent) {
// keyboardCharMap is an array of arrays, with each inner
// array having 2 columns - un-shifted, and shifted values.
// iCol = 0 is the un-shifted value, while iCol=1 is the shifted value.
// See the "keyboardCharMap", below, for printable characters.
// MODIFY keyboardCharMap to suit your needs if you want
// more/less/different characters to be considered "printable".
let iCol = 0;
if (e.shiftKey) {
iCol = 1;
}
console.log(e.shiftKey);
const ch = keyboardCharMap[e.keyCode][iCol];
// Optionally do things with non-printables,
// like CR, ESC, Backspace, LF, Tab, Arrows, F1-F24, etc.
// See the arrary "keyboardNameMap", below, for possible keys.
const cmd = keyboardNameMap[e.keyCode];
switch (cmd) {
case "ENTER":
handleEnterKey();
break;
case "BACK_SPACE":
input.value = input.value.slice(0, -1);
break;
case "ESCAPE":
input.value = "";
break;
default:
input.value += ch;
}
}
return {
keyDownHandler,
input
};
}

View file

@ -0,0 +1,17 @@
export default function useTextWrap() {
// text wraps a string to a given width
function textWrap({ s, w }: { s: string; w: number }): string[] {
// return tspan elements for text with correct size
const wrap = (s: string, w: number) =>
s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, "g"),
"$1\n"
);
const result = wrap(s, w).split("\n");
return result;
}
return {
textWrap
};
}

View file

@ -2,10 +2,17 @@ import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// animation library
import anime from "animejs";
// event handler library
import mitt from "mitt";
const emitter = mitt();
createApp(App)
.use(store)
.use(router)
.provide("animejs", anime)
.provide("mitt", emitter)
.mount("#app");

View file

@ -1,16 +0,0 @@
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;
}

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

@ -4,11 +4,13 @@ declare module "*.vue" {
export default component;
}
// declare module "anime-js" {
// import * as anime from "animejs/lib/anime.es.js";
// function anime(): void;
// export = anime;
// }
declare module "anime-js" {
namespace anime {
import * as anime from "animejs/lib/anime.es.js";
function anime(): void;
export = anime;
}
}
interface Message {
content: string;

View file

@ -0,0 +1,13 @@
interface AnimeFuncParams {
targets: HTMLElement | SVGGElement | SVGElement;
transform: string;
easing?: string;
duration?: number;
offset?: number;
begin?: () => void;
complete?: () => void;
}
export default interface AnimeFunc {
(params: AnimeFuncParams): void | undefined;
}

View file

@ -0,0 +1,18 @@
export interface Message {
content: string;
signature: string;
outgoing: boolean;
modifier: string;
imgURL: string;
id: string;
}
interface MessageTransform {
initialTranslate: string;
finalTranslate: string;
}
export interface MessageAnimeFunc {
(message: SVGElement, transform: MessageTransform): void;
}

View file

@ -1,19 +1,18 @@
<template>
<UIindex />
<Microphone />
</template>
<script lang="ts">
import { defineComponent, onMounted } from 'vue';
import UIindex from "@/components/UI/index.vue";
import Microphone from "@/components/microphone/index.vue";
export default defineComponent({
name: "HomeIndex",
components: { UIindex, Microphone },
});
import { defineComponent, inject } from 'vue';
import UIindex from "@/components/UI/index.vue";
// import Microphone from "@/components/microphone/index.vue";
export default defineComponent({
name: "HomeIndex",
components: { UIindex },
setup() {
const emitter: any = inject("mitt");
emitter.on('newSelfMessage', (payload: any) => console.log('foo', payload) )
}
});
</script>
<style></style>

View file

@ -8569,6 +8569,11 @@ mississippi@^3.0.0:
stream-each "^1.1.0"
through2 "^2.0.0"
mitt@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230"
integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==
mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"