feat: graph lines and more customization available for nodes
This commit is contained in:
@@ -1,27 +1,34 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {motion, frame} from "motion-v";
|
import {motion, frame} from "motion-v";
|
||||||
|
|
||||||
function getRandomArbitrary(min: number, max: number): number {
|
|
||||||
return Math.random() * (max - min) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
img = undefined,
|
img = undefined,
|
||||||
color = 'bg-white',
|
color = 'bg-white',
|
||||||
textTop = false,
|
textTop = false,
|
||||||
moveFactor = undefined
|
moveFactor = undefined,
|
||||||
|
scaleFactor = 2,
|
||||||
|
initialWidth = undefined,
|
||||||
|
initialHeight = undefined,
|
||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
name: string,
|
name: string,
|
||||||
img?: string,
|
img?: string,
|
||||||
color?: string,
|
color?: string,
|
||||||
textTop?: boolean,
|
textTop?: boolean,
|
||||||
moveFactor?: number
|
moveFactor?: number,
|
||||||
|
scaleFactor?: number,
|
||||||
|
initialWidth?: string,
|
||||||
|
initialHeight?: string,
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['positionChanged'])
|
||||||
|
|
||||||
const isHoveringParent = ref(false)
|
const isHoveringParent = ref(false)
|
||||||
const roundedDivClass = `rounded-full w-full h-full absolute ${color}`
|
const roundedDivClass = `rounded-full w-full h-full absolute ${color}`
|
||||||
|
|
||||||
|
function getRandomArbitrary(min: number, max: number): number {
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
const moveFactorValue = moveFactor ? moveFactor : getRandomArbitrary(8.0, 30.0)
|
const moveFactorValue = moveFactor ? moveFactor : getRandomArbitrary(8.0, 30.0)
|
||||||
|
|
||||||
const spring = {damping: 5, stiffness: 50, restDelta: 0.001}
|
const spring = {damping: 5, stiffness: 50, restDelta: 0.001}
|
||||||
@@ -40,6 +47,16 @@ const handlePointerMove = ({clientX, clientY}: { clientX: number; clientY: numbe
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useRafFn(() => {
|
||||||
|
const element = elementRef.value?.$el
|
||||||
|
if (!element)
|
||||||
|
return
|
||||||
|
emit(
|
||||||
|
'positionChanged',
|
||||||
|
x.get() + element.offsetLeft + element.offsetWidth/2,
|
||||||
|
y.get() + element.offsetTop + element.offsetHeight/2,)
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener("pointermove", handlePointerMove)
|
window.addEventListener("pointermove", handlePointerMove)
|
||||||
})
|
})
|
||||||
@@ -48,19 +65,20 @@ onUnmounted(() => {
|
|||||||
window.removeEventListener("pointermove", handlePointerMove)
|
window.removeEventListener("pointermove", handlePointerMove)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const baseElementClass = ref(`${initialWidth ? initialWidth : 'w-30'} ${initialHeight ? initialHeight : 'h-30'} avatar absolute`)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<motion.div
|
<motion.div
|
||||||
ref="elementRef"
|
ref="elementRef"
|
||||||
class="w-30 h-30 avatar absolute"
|
:class="baseElementClass"
|
||||||
:style="{ x, y }"
|
:style="{ x, y }"
|
||||||
:while-hover="{ scale: 2 }"
|
:while-hover="{ scale: scaleFactor }"
|
||||||
@hover-start="event => {isHoveringParent=true}"
|
@hover-start="event => {isHoveringParent=true}"
|
||||||
@hover-end="event => {isHoveringParent=false}">
|
@hover-end="event => {isHoveringParent=false}">
|
||||||
|
|
||||||
<div v-if="img !== undefined" class="mask mask-squircle w-24">
|
<div v-if="img !== undefined" class="mask mask-squircle w-full">
|
||||||
<NuxtImg :src="img"/>
|
<NuxtImg :src="img"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else :class="roundedDivClass"/>
|
<div v-else :class="roundedDivClass"/>
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
const nodes = [
|
const nodes = [
|
||||||
|
{
|
||||||
|
name: "Hi, I'm Alex",
|
||||||
|
img: "/face.jpg",
|
||||||
|
textTop: true,
|
||||||
|
moveFactor: 6,
|
||||||
|
hOffset: 'left-1/3',
|
||||||
|
vOffset: 'top-1/3',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "DevOps engineer",
|
name: "DevOps engineer",
|
||||||
color: "bg-blue-100",
|
color: "bg-blue-100",
|
||||||
hOffset: 'left-30',
|
hOffset: 'left-30',
|
||||||
vOffset: 'top-30',
|
vOffset: 'top-30',
|
||||||
|
initialWidth: 'w-2',
|
||||||
|
initialHeight: 'h-2',
|
||||||
|
scaleFactor: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Game developer",
|
name: "Game developer",
|
||||||
@@ -28,20 +39,66 @@ const nodes = [
|
|||||||
textTop: true
|
textTop: true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const divsLocations = ref(nodes.map(() => {
|
||||||
|
return {x: 0, y: 0}
|
||||||
|
}))
|
||||||
|
const linesLocations = ref(new Array<{x1: number, y1: number, x2: number, y2: number}>())
|
||||||
|
|
||||||
|
for (let i = 0; i < divsLocations.value.length - 1; i++) {
|
||||||
|
const startLocation = divsLocations.value[i]!
|
||||||
|
const endLocation = divsLocations.value[i+1]!
|
||||||
|
linesLocations.value.push({x1: startLocation.x, y1: startLocation.y, x2: endLocation.x, y2: endLocation.y})
|
||||||
|
}
|
||||||
|
linesLocations.value.push({x1: 0, y1: 0, x2: 0, y2: 0})
|
||||||
|
|
||||||
|
const nodeMoved = (nodeIndex: number, x: number, y: number) => {
|
||||||
|
divsLocations.value[nodeIndex] = {x: x, y: y}
|
||||||
|
}
|
||||||
|
|
||||||
|
useRafFn(() => {
|
||||||
|
for (let i = 0; i < divsLocations.value.length - 1; i++) {
|
||||||
|
const startLocation = divsLocations.value[i]!
|
||||||
|
const endLocation = divsLocations.value[i+1]!
|
||||||
|
linesLocations.value[i] = {x1: startLocation.x, y1: startLocation.y, x2: endLocation.x, y2: endLocation.y}
|
||||||
|
}
|
||||||
|
linesLocations.value[linesLocations.value.length - 1] = {
|
||||||
|
x1: divsLocations.value[divsLocations.value.length - 1]!.x,
|
||||||
|
y1: divsLocations.value[divsLocations.value.length - 1]!.y,
|
||||||
|
x2: divsLocations.value[0]!.x,
|
||||||
|
y2: divsLocations.value[0]!.y
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="hero bg-base-200 min-h-screen">
|
<div class="hero bg-base-200 min-h-screen">
|
||||||
<div class="overflow-hidden">
|
<div class="overflow-hidden">
|
||||||
<GraphNode img="/face.jpg" name="Hi, I'm Alex" :text-top="true" :move-factor="6"/>
|
<ul v-for="(line, index) in linesLocations" :key="index">
|
||||||
|
<li>
|
||||||
|
<svg height="100%" width="100%" class="absolute top-0 left-0">
|
||||||
|
<line
|
||||||
|
:x1="line.x1"
|
||||||
|
:y1="line.y1"
|
||||||
|
:x2="line.x2"
|
||||||
|
:y2="line.y2" stroke="white"/>
|
||||||
|
</svg>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul v-for="(node, index) in nodes" :key="index">
|
<ul v-for="(node, index) in nodes" :key="index">
|
||||||
<li>
|
<li>
|
||||||
<GraphNode
|
<GraphNode
|
||||||
:name="node.name"
|
:name="node.name"
|
||||||
|
:img="node.img"
|
||||||
:color="node.color"
|
:color="node.color"
|
||||||
:class="`${node.hOffset} ${node.vOffset}`"
|
:class="`${node.hOffset ? node.hOffset : ''} ${node.vOffset ? node.vOffset : ''}`"
|
||||||
:text-top="node.textTop"/>
|
:text-top="node.textTop"
|
||||||
|
:move-factor="node.moveFactor"
|
||||||
|
:scale-factor="node.scaleFactor"
|
||||||
|
:initial-width="node.initialWidth"
|
||||||
|
:initial-height="node.initialHeight"
|
||||||
|
@position-changed="(x, y) => { nodeMoved(index, x, y) }"/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user