come graph
This commit is contained in:
61
app/components/GraphNode.vue
Normal file
61
app/components/GraphNode.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import {motion, type MotionValue} from "motion-v";
|
||||
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
name,
|
||||
img = undefined,
|
||||
color = 'bg-white',
|
||||
hOffset = undefined,
|
||||
vOffset = undefined,
|
||||
} = defineProps<{
|
||||
x: MotionValue<number>,
|
||||
y: MotionValue<number>,
|
||||
name: string,
|
||||
img?: string,
|
||||
color?: string,
|
||||
hOffset?: string,
|
||||
vOffset?: string,
|
||||
}>()
|
||||
|
||||
const isHoveringParent = ref(false)
|
||||
|
||||
const roundedDivClass = `rounded-full w-full h-full absolute ${color}`
|
||||
|
||||
const hOffsetValue = hOffset === undefined ? '' : hOffset
|
||||
const vOffsetValue = vOffset === undefined ? '' : vOffset
|
||||
const computedClass = `w-20 h-20 absolute avatar ${hOffsetValue} ${vOffsetValue}`
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<motion.div
|
||||
ref="elementRef"
|
||||
:class="computedClass"
|
||||
:style="{ x, y }"
|
||||
:while-hover="{ scale: 3 }"
|
||||
@hover-start="event => {isHoveringParent=true}"
|
||||
@hover-end="event => {isHoveringParent=false}">
|
||||
|
||||
<div v-if="img !== undefined" class="mask mask-squircle w-24">
|
||||
<NuxtImg :src="img"/>
|
||||
</div>
|
||||
<div v-else :class="roundedDivClass"/>
|
||||
|
||||
<motion.p
|
||||
v-show="isHoveringParent"
|
||||
class="-top-8 absolute text-xs"
|
||||
:initial="{ opacity: 0 }"
|
||||
:animate="{ opacity: 1 }"
|
||||
:exit="{ opacity: 0 }">
|
||||
{{ name }}
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
const test = ref('coucou')
|
||||
import {frame} from "motion-v";
|
||||
import GraphNode from "~/components/GraphNode.vue";
|
||||
|
||||
|
||||
const spring = {damping: 5, stiffness: 50, restDelta: 0.001}
|
||||
|
||||
const elementRef = useTemplateRef('elementRef')
|
||||
const xPoint = useMotionValue(0)
|
||||
const yPoint = useMotionValue(0)
|
||||
const x = useSpring(xPoint, spring)
|
||||
const y = useSpring(yPoint, spring)
|
||||
|
||||
const handlePointerMove = ({clientX, clientY}: { clientX: number; clientY: number }) => {
|
||||
const element = elementRef.value?.$el
|
||||
if (!element) return
|
||||
|
||||
frame.read(() => {
|
||||
xPoint.set((clientX - element.offsetLeft - element.offsetWidth / 2) / 10)
|
||||
yPoint.set((clientY - element.offsetTop - element.offsetHeight / 2) / 10)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("pointermove", handlePointerMove)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("pointermove", handlePointerMove)
|
||||
})
|
||||
|
||||
|
||||
const nodes = [
|
||||
{
|
||||
name: "DevOps engineer",
|
||||
color: "bg-blue-100",
|
||||
hOffset: 'left-20',
|
||||
vOffset: 'top-20',
|
||||
},
|
||||
{
|
||||
name: "Game developer",
|
||||
color: "bg-blue-200",
|
||||
hOffset: 'right-20',
|
||||
vOffset: 'top-20',
|
||||
},
|
||||
{
|
||||
name: "Fullstack developer",
|
||||
color: "bg-blue-300",
|
||||
hOffset: 'left-20',
|
||||
vOffset: 'bottom-20',
|
||||
},
|
||||
{
|
||||
name: "Tools programmer",
|
||||
color: "bg-blue-400",
|
||||
hOffset: 'right-20',
|
||||
vOffset: 'bottom-20',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ test }}</p>
|
||||
<div class="hero bg-base-200 min-h-screen">
|
||||
<div class="hero-content overflow-hidden bg-">
|
||||
<GraphNode ref="elementRef" :x="x" :y="y" img="/face.jpg" name="Hi, I'm Alex" />
|
||||
<ul v-for="(node, index) in nodes" :key="index">
|
||||
<li>
|
||||
<GraphNode
|
||||
:x="x"
|
||||
:y="y"
|
||||
:name="node.name"
|
||||
:color="node.color"
|
||||
:hOffset="node.hOffset"
|
||||
:vOffset="node.vOffset"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
* {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
24
app/components/OgImage/Default.vue
Normal file
24
app/components/OgImage/Default.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
withDefaults(defineProps<{
|
||||
title?: string
|
||||
borderColor?: string
|
||||
}>(), {
|
||||
title: 'title',
|
||||
borderColor: 'blue-500'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="[`border-${borderColor}`]" class="h-full w-full flex items-start justify-start border-solid border-[12px] bg-gray-50">
|
||||
<div class="flex items-start justify-start h-full">
|
||||
<div class="flex flex-col justify-between w-full h-full">
|
||||
<h1 class="text-[80px] p-20 font-black text-left">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<p class="text-2xl pb-10 px-20 font-bold mb-0">
|
||||
mycoolsite.com
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
defineOgImageComponent('Default', {
|
||||
title: 'Portfolio',
|
||||
borderColor: 'green-300',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user