Написание движка для интерактивного обучения
This commit is contained in:
159
resources/js/Components/Tour/Tour.vue
Normal file
159
resources/js/Components/Tour/Tour.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<script setup>
|
||||
import {
|
||||
watch,
|
||||
nextTick,
|
||||
ref
|
||||
} from "vue"
|
||||
import { useOnboardingStore } from "@/Stores/onboarding"
|
||||
import TourOverlay from "./TourOverlay.vue"
|
||||
import TourTooltip from "./TourTooltip.vue"
|
||||
import { useWaitTarget } from "./composables/useWaitTarget"
|
||||
import { useHighlight } from "./composables/useHighlight"
|
||||
import { useFloating } from "./composables/useFloating"
|
||||
|
||||
const onboarding = useOnboardingStore()
|
||||
const target = ref(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const {
|
||||
waitTarget
|
||||
} = useWaitTarget()
|
||||
|
||||
const {
|
||||
rect,
|
||||
update: updateRect
|
||||
} = useHighlight(target)
|
||||
|
||||
/**
|
||||
* Текущий шаг
|
||||
*/
|
||||
watch(
|
||||
() => onboarding.currentStep,
|
||||
async (step, oldStep) => {
|
||||
|
||||
if (!step) {
|
||||
return
|
||||
}
|
||||
|
||||
await leaveStep(oldStep)
|
||||
await enterStep(step)
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
async function enterStep(step) {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
if (step.beforeEnter) {
|
||||
await step.beforeEnter()
|
||||
}
|
||||
|
||||
/*
|
||||
* ждём элемент
|
||||
*/
|
||||
if (step.target) {
|
||||
target.value = await waitTarget(
|
||||
step.target,
|
||||
{
|
||||
timeout: 10000,
|
||||
visible: true
|
||||
}
|
||||
)
|
||||
|
||||
if (!target.value) {
|
||||
return
|
||||
}
|
||||
|
||||
await nextTick()
|
||||
|
||||
/*
|
||||
* прокрутка
|
||||
*/
|
||||
if (step.scroll) {
|
||||
target.value.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center"
|
||||
})
|
||||
|
||||
await delay(300)
|
||||
}
|
||||
|
||||
console.log("TARGET:", target.value)
|
||||
|
||||
/*
|
||||
* обновляем размеры
|
||||
*/
|
||||
updateRect()
|
||||
|
||||
console.log("RECT:", rect.value)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function leaveStep(step) {
|
||||
|
||||
if (!step) {
|
||||
return
|
||||
}
|
||||
|
||||
if (step.afterLeave) {
|
||||
await step.afterLeave()
|
||||
}
|
||||
|
||||
target.value = null
|
||||
}
|
||||
|
||||
function next() {
|
||||
onboarding.next()
|
||||
}
|
||||
|
||||
function previous() {
|
||||
onboarding.previous()
|
||||
}
|
||||
|
||||
function close() {
|
||||
onboarding.finish()
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise(resolve =>
|
||||
setTimeout(resolve, ms)
|
||||
)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<TourOverlay
|
||||
v-if="onboarding.active && target"
|
||||
:rect="rect"
|
||||
:padding="onboarding.currentStep.padding"
|
||||
/>
|
||||
|
||||
<TourTooltip
|
||||
:step="onboarding.currentStep"
|
||||
|
||||
:target="target"
|
||||
|
||||
:visible="onboarding.active"
|
||||
|
||||
:is-first="onboarding.stepIndex === 0"
|
||||
:is-last="onboarding.stepIndex === onboarding.tour.steps.length - 1"
|
||||
|
||||
@next="onboarding.next"
|
||||
@previous="onboarding.previous"
|
||||
@finish="onboarding.finish"
|
||||
@close="onboarding.finish"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
Reference in New Issue
Block a user