--- /dev/null
+<template>
+ <g id="cardsDiv">
+ <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"
+ v-on:after-enter="afterEnter"
+ >
+ <MessageItem :item="item" :key="index" />
+ </transition>
+ </g>
+ </g>
+</template>
+
+<script lang="ts">
+import useMessageRenderer from "@/composables/messageRenderer/useMessageRenderer";
+import { defineComponent, reactive, ref, inject } from "vue";
+import MessageItem from "./messageItem.vue";
+import Messages from "./messages";
+import { Message } from "@/types/message/index";
+export default defineComponent({
+ name: "MessageRenderer",
+ components: { MessageItem },
+ setup() {
+ const emitter: any = inject("mitt");
+ const renderArr: Message[] = reactive([]);
+ const messages = Messages;
+ const count = ref(0);
+
+ const { beforeEnter, enter, afterEnter } = useMessageRenderer();
+
+ emitter.on("renderMessage", (payload: any) => {
+ renderArr.push(payload);
+ });
+ function renderMessage() {
+ if (count.value < messages.length) {
+ renderArr.push(messages[count.value]);
+ count.value++;
+ return;
+ }
+ }
+
+ return {
+ renderMessage,
+ renderArr,
+ beforeEnter,
+ enter,
+ afterEnter,
+ };
+ },
+});
+</script>
@click="renderMessage"
>
<AI />
-
- <g id="cardsDiv">
- <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"
- v-on:after-enter="afterEnter"
- >
- <MessageItem :item="item" :key="index" />
- </transition>
- </g>
- </g>
-
+ <MessageRenderer />
<InputItem />
<TitleBar />
</svg>
</template>
<script lang="ts">
-import { defineComponent, ref, reactive } from "vue";
+import { defineComponent, ref, reactive, inject } from "vue";
import MessageItem from "./UIDetails/messageItem.vue";
import AI from "../AI/index.vue";
import InputItem from "./UIDetails/inputItem.vue";
import TitleBar from "./UIDetails/titleBar.vue";
import Messages from "./UIDetails/messages";
-import useUIindexAnims from "@/composables/anims/useUIindexAnims";
+import MessageRenderer from "./UIDetails/messageRenderer.vue";
export default defineComponent({
name: "UIindex",
- components: { AI, MessageItem, InputItem, TitleBar },
+ components: { AI, MessageItem, InputItem, TitleBar, MessageRenderer },
setup() {
- const renderArr: Message[] = reactive([]);
const messages = Messages;
const count = ref(0);
-
- const { beforeEnter, enter, afterEnter } = useUIindexAnims();
+ const emitter: any = inject("mitt");
const renderMessage = () => {
if (count.value < messages.length) {
- renderArr.push(messages[count.value]);
+ emitter.emit("renderMessage", messages[count.value])
count.value++;
return;
}
return {
renderMessage,
- renderArr,
- beforeEnter,
- enter,
- afterEnter,
};
},
});
--- /dev/null
+import useMessageOffsets from "./useOffsets";
+import useRendererAnims from "./useRendererAnims";
+export default function useMessageRenderer() {
+
+ const { calculateMessageOffsets } = useMessageOffsets();
+ const { scrollAnimate, stackAnimate } = useRendererAnims();
+
+ let messageOffset = {
+ x: 0,
+ y: 0
+ };
+ let modifier: string;
+ let msg: HTMLElement | null;
+ let renderList: NodeList;
+ let scrollAnim = false;
+ let scroll = 0;
+
+ function setRenderList(): void {
+ const messages = document.querySelectorAll(".test");
+ if (messages.length) renderList = messages;
+ }
+
+ function renderMessage(el: SVGGElement): void {
+ const transform = {
+ initialTranslate: `translate(${messageOffset.x} ${messageOffset.y})`,
+ finalTranslate: `translate(${messageOffset.x} ${messageOffset.y + 5})`
+ }
+ scrollAnim ? scrollAnimate(el, transform, scroll, renderList) : stackAnimate(el, transform);
+ }
+
+ function beforeEnter(el: SVGGElement): void {
+ setRenderList();
+ modifier = el.className.baseVal.substring(0, 2);
+ }
+
+ function enter(el: SVGGElement): void {
+ msg = document.getElementById(el.id);
+ }
+
+ function afterEnter(el: SVGGElement) {
+ const messagePadding = 10;
+ if (msg) {
+ messageOffset = calculateMessageOffsets(msg, modifier);
+ scrollAnim = messageOffset.y > 450 ? true : false;
+ }
+ if (scrollAnim) {
+ scroll -= el.getBBox().height + messagePadding;
+ }
+ renderMessage(el);
+ }
+
+ return {
+ beforeEnter,
+ enter,
+ afterEnter
+ };
+}
--- /dev/null
+import { ref, onMounted } from "vue";
+export default function useMessageOffsets() {
+ let xOffset = ref(0);
+ let yOffset = ref(0);
+ let messenger: HTMLElement | null;
+ let winW: number;
+ let previousMessageHeight = 0;
+ const messagePadding = 10;
+
+ onMounted(() => {
+ messenger = document.getElementById("messenger");
+ if (messenger) winW = messenger.clientWidth;
+ })
+
+ function setXoffset(messageWidth: number, modifier: string): void {
+ const paddingLeft = 40;
+ switch (modifier) {
+ case "sf":
+ xOffset.value = winW - messageWidth;
+ break;
+ case "ai":
+ xOffset.value = (winW - messageWidth) / 2;
+ break;
+ default:
+ xOffset.value = paddingLeft;
+ }
+ }
+
+ function setYoffset(messageHeight: number): void {
+ if (previousMessageHeight === 0) {
+ yOffset.value = 40;
+ } else {
+ yOffset.value += previousMessageHeight + messagePadding;
+ }
+ previousMessageHeight = messageHeight;
+ }
+
+ const calculateMessageOffsets = (msg: HTMLElement, modifier: string) => {
+ const msgRect = msg.getBoundingClientRect();
+ setXoffset(msgRect.width, modifier)
+ setYoffset(msgRect.height)
+ const offsets = {
+ x: xOffset.value as number,
+ y: yOffset.value as number
+ };
+ return offsets;
+ }
+
+ return {
+ calculateMessageOffsets,
+ }
+}
--- /dev/null
+import { inject } from "vue";
+import AnimeFunc from "@/types/animejs/index";
+import { MessageAnimeFunc } from '@/types/message/index';
+export default function useRendererAnims() {
+
+ // imports animejs safely
+ let anime: AnimeFunc;
+ const animeInject: AnimeFunc | undefined = inject("animejs");
+ if (animeInject) anime = animeInject;
+
+ function scrollOffset(targetList: NodeList | undefined, height: number | undefined): void {
+ if (targetList) {
+ for (const message of targetList) {
+ const m = message.parentNode as HTMLElement;
+ anime({
+ targets: m,
+ transform: `translate(0 ${height})`,
+ easing: "easeInOutQuad",
+ duration: 500,
+ });
+ }
+ }
+ }
+
+ const scrollAnimate: MessageAnimeFunc = (el, transform, scrollHeight, targetList) => {
+ anime({
+ targets: el, transform: transform.initialTranslate,
+ easing: "easeInOutQuad",
+ duration: 500,
+ begin: function() {
+ scrollOffset(targetList, scrollHeight);
+ },
+ 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
+ });
+ }
+ });
+ }
+
+ return {
+ scrollAnimate,
+ stackAnimate
+ };
+}
}
export interface MessageAnimeFunc {
- (message: SVGElement, transform: MessageTransform): void;
+ (message: SVGElement, transform: MessageTransform, scrollHeight?: number | undefined, targetList?: NodeList | undefined): void;
}