font-size: 14;
text-align: left;
}
-
</style>
:transform = "startingOffset"
>
<rect
- ref="bubbleTag"
- id="#bubble"
+ ref="messageRect"
x="30"
y="10"
width="196"
<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"
</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: {
},
},
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;
- console.log(props)
+ 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" || "sf":
- 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>
<script lang="ts">
import { defineComponent, inject, onMounted, reactive } from "vue";
import MessageItem from './UIDetails/messageItem.vue';
-import InputItem from "@/components/UI/UIDetails/inputItem.vue";
+import InputItem from "./UIDetails/inputItem.vue";
import Messages from "./UIDetails/messages";
export default defineComponent({
name: 'UIindex',
components: { MessageItem, InputItem },
setup() {
- 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;
-
- function renderMsg(i: number): void {
- setTimeout(() => {
- renderArr.push(messages[i]);
- }, 2000);
- }
+ 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;
- function runAnim(): void {
- for (let i = 0; i < messages.length; i++) {
- renderMsg(i);
- }
- }
+ function renderMsg(i: number): void {
+ setTimeout(() => {
+ renderArr.push(messages[i]);
+ }, 2000);
+ }
- async function beforeEnter(el: any) {
- modifier = el.className.baseVal.substring(0, 2);
+ function runAnim(): void {
+ for (let i = 0; i < messages.length; i++) {
+ renderMsg(i);
}
+ }
- 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();
+ async function beforeEnter(el: any) {
+ modifier = el.className.baseVal.substring(0, 2);
+ }
- 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;
+ 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();
- switch (modifier) {
- case "sf":
- msgOffset.x = winW - msgW;
- break;
- case "ai":
- msgOffset.x = (winW - msgW) / 2;
- break;
- default:
- msgOffset.x = paddingLeft;
- }
+ 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;
- 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();
+ 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() {
}
--- /dev/null
+import { ref, onMounted } from "vue";
+interface Ref {
+ value: string;
+}
+export default function useMessageItemAnims({ mr, tr }: { mr: Ref; tr: Ref }) {
+ const signatureTranslate = ref("");
+ let messageRect: string | SVGRectElement | SVGSVGElement | any;
+ let messageText: string | SVGTextElement | SVGSVGElement | any;
+
+ onMounted(() => {
+ messageRect = mr.value;
+ messageText = tr.value;
+ });
+
+ function adjustMessageRect(): void {
+ // must get dimensions of text box to fit bubble around
+ const padding = 20;
+ const textBox = messageText.getBBox();
+ messageRect.setAttribute("x", textBox.x - padding);
+ messageRect.setAttribute("y", textBox.y - padding);
+ messageRect.setAttribute("width", textBox.width + 2 * padding);
+ messageRect.setAttribute("height", 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
+ };
+}
--- /dev/null
+import { computed } from "vue";
+// takes in message modifier as param
+export default function useMessageItemComp(m: string) {
+ const textFill = computed(() => {
+ switch (m) {
+ case "fr":
+ return "#000";
+ default:
+ return "#fff";
+ }
+ });
+
+ const bubbleFill = computed(() => {
+ switch (m) {
+ case "sf":
+ return "#61b4f4";
+ case "ai":
+ return "#585858";
+ default:
+ return "#fff";
+ }
+ });
+
+ const startingOffset = computed(() => {
+ switch (m) {
+ case "sf":
+ return "translate(10, 600)";
+ case "ai":
+ return "translate(135, 600)";
+ default:
+ return "translate(20, 600)";
+ }
+ });
+
+ return {
+ textFill,
+ bubbleFill,
+ startingOffset
+ };
+}
--- /dev/null
+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
+ };
+}
+++ /dev/null
-import { reactive, onMounted } from "vue";
-export default function useTextBox() {
- const textArr: string[] = reactive([]);
-
- function createTextBox(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 textWrap = wrap(s, w).split("\n");
- return textWrap;
- }
-
- function adjustRect(rect: any, text: any): void {
- // must get dimensions of text box to fit bubble around
- const padding = 20;
- const bbox = text.getBBox();
- rect.setAttribute("x", bbox.x - padding);
- rect.setAttribute("y", bbox.y - padding);
- rect.setAttribute("width", bbox.width + 2 * padding);
- rect.setAttribute("height", bbox.height + 2 * padding);
- }
-
- return {
- textArr,
- adjustRect,
- createTextBox
- };
-}
+++ /dev/null
-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;
-}