feat:加上 Vue 学习站与可嵌入机芯
站点按 bloub 三栏做自定义、动画时间线和设置,支持序列导出。 形体只留常见几种并配中文名;动画页右栏拉满时间线旁空位,滚动条隐藏。 机芯补 GROK.create / <grok-bot>,以及暂停冻结。
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { t } from '@/i18n'
|
||||
import { mountReplica, type ReplicaMount } from '@/replica/host'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
size?: number
|
||||
shape?: string
|
||||
color?: string
|
||||
state?: string
|
||||
follow?: boolean
|
||||
paper?: string
|
||||
}>(),
|
||||
{
|
||||
size: 440,
|
||||
shape: 'blob',
|
||||
color: 'black',
|
||||
state: 'idle',
|
||||
follow: false,
|
||||
paper: '#f9f9f9'
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{ ready: [ok: boolean] }>()
|
||||
|
||||
const svg = ref<SVGSVGElement | null>(null)
|
||||
const live = ref(false)
|
||||
const tried = ref(false)
|
||||
let replica: ReplicaMount | null = null
|
||||
let alive = true
|
||||
|
||||
function input() {
|
||||
return {
|
||||
state: props.state,
|
||||
shape: props.shape,
|
||||
color: props.color,
|
||||
follow: props.follow,
|
||||
paper: props.paper,
|
||||
size: props.size
|
||||
}
|
||||
}
|
||||
|
||||
async function attach() {
|
||||
const node = svg.value
|
||||
if (!node) return
|
||||
const mount = await mountReplica(node, input(), true)
|
||||
if (!alive) {
|
||||
mount?.destroy()
|
||||
return
|
||||
}
|
||||
replica = mount
|
||||
live.value = !!replica
|
||||
tried.value = true
|
||||
replica?.apply(input())
|
||||
emit('ready', live.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void attach()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
alive = false
|
||||
replica?.destroy()
|
||||
replica = null
|
||||
})
|
||||
|
||||
watch(
|
||||
() => [props.state, props.shape, props.color, props.follow, props.paper, props.size],
|
||||
() => replica?.apply(input())
|
||||
)
|
||||
|
||||
defineExpose({
|
||||
spin: () => replica?.spin(),
|
||||
orbitGaze: (ms?: number) => replica?.orbitGaze(ms),
|
||||
svg: () => svg.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative inline-block aspect-square max-w-full" :style="{ width: `${props.size}px` }">
|
||||
<svg
|
||||
ref="svg"
|
||||
class="absolute inset-0 h-full w-full overflow-visible"
|
||||
:width="props.size"
|
||||
:height="props.size"
|
||||
role="img"
|
||||
:aria-label="t('app.botAria')"
|
||||
/>
|
||||
<p
|
||||
v-if="tried && !live"
|
||||
class="absolute inset-6 flex items-center justify-center text-center text-xs text-[var(--muted)]"
|
||||
>
|
||||
{{ t('app.missingGeo') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user