92 lines
2.6 KiB
Vue
92 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import { useDraggable, useEventListener, useLocalStorage } from '@vueuse/core'
|
|
import { computed, onMounted, ref, watchEffect } from 'vue'
|
|
import { recorder } from '../logic/recording'
|
|
import { currentCamera } from '../state'
|
|
|
|
const size = useLocalStorage('slidev-webcam-size', Math.round(Math.min(window.innerHeight, (window.innerWidth) / 8)))
|
|
const position = useLocalStorage('slidev-webcam-pos', {
|
|
x: window.innerWidth - size.value - 30,
|
|
y: window.innerHeight - size.value - 30,
|
|
}, { deep: true })
|
|
|
|
const frame = ref<HTMLDivElement | undefined>()
|
|
const handler = ref<HTMLDivElement | undefined>()
|
|
const video = ref<HTMLVideoElement | undefined>()
|
|
|
|
const { streamCamera, showAvatar } = recorder
|
|
|
|
const { style: containerStyle } = useDraggable(frame, {
|
|
initialValue: position,
|
|
onMove({ x, y }) {
|
|
position.value.x = x
|
|
position.value.y = y
|
|
},
|
|
})
|
|
const { isDragging: handlerDown } = useDraggable(handler, {
|
|
onMove({ x, y }) {
|
|
size.value = Math.max(10, Math.min(x - position.value.x, y - position.value.y) / 0.8536)
|
|
},
|
|
})
|
|
|
|
watchEffect(() => {
|
|
if (video.value)
|
|
video.value.srcObject = streamCamera.value!
|
|
}, { flush: 'post' })
|
|
|
|
const frameStyle = computed(() => ({
|
|
width: `${size.value}px`,
|
|
height: `${size.value}px`,
|
|
}))
|
|
|
|
const handleStyle = computed(() => ({
|
|
width: '14px',
|
|
height: '14px',
|
|
// 0.5 + 0.5 / sqrt(2)
|
|
top: `${size.value * 0.8536 - 7}px`,
|
|
left: `${size.value * 0.8536 - 7}px`,
|
|
cursor: 'nwse-resize',
|
|
}))
|
|
|
|
function fixPosition() {
|
|
// move back if the camera is outside of the canvas
|
|
if (position.value.x >= window.innerWidth)
|
|
position.value.x = window.innerWidth - size.value - 30
|
|
if (position.value.y >= window.innerHeight)
|
|
position.value.y = window.innerHeight - size.value - 30
|
|
}
|
|
|
|
useEventListener('resize', fixPosition)
|
|
onMounted(fixPosition)
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
v-if="streamCamera && showAvatar && currentCamera !== 'none'"
|
|
class="fixed z-camera"
|
|
:style="containerStyle"
|
|
>
|
|
<div
|
|
ref="frame"
|
|
class="rounded-full shadow bg-gray-400 bg-opacity-10 overflow-hidden object-cover"
|
|
:style="frameStyle"
|
|
>
|
|
<video
|
|
ref="video"
|
|
autoplay
|
|
muted
|
|
volume="0"
|
|
class="object-cover min-w-full min-h-full rounded-full"
|
|
style="transform: rotateY(180deg);"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
ref="handler"
|
|
class="absolute bottom-0 right-0 rounded-full bg-main shadow opacity-0 shadow z-dragging hover:opacity-100 dark:border dark:border-true-gray-700"
|
|
:style="handleStyle"
|
|
:class="handlerDown ? '!opacity-100' : ''"
|
|
/>
|
|
</div>
|
|
</template>
|