diff --git a/packages/devui-vue/devui/fullscreen/src/composables/use-fullscreen.ts b/packages/devui-vue/devui/fullscreen/src/composables/use-fullscreen.ts new file mode 100644 index 0000000000..798ed9001d --- /dev/null +++ b/packages/devui-vue/devui/fullscreen/src/composables/use-fullscreen.ts @@ -0,0 +1,62 @@ +import { watch, toRefs, onMounted, onUnmounted } from 'vue'; +import { + launchNormalFullscreen, + exitNormalFullscreen, + launchImmersiveFullScreen, + exitImmersiveFullScreen, + addFullScreenStyle, + removeFullScreenStyle, +} from '../utils'; + +export default function useFullscreen(props, slotElement, ctx) { + const { modelValue, mode } = toRefs(props); + let exitByKeydown = false; + + watch(modelValue, (newVal) => { + if (mode.value === 'normal') { + handleNormalFullscreen(newVal); + } + + if (mode.value === 'immersive') { + handleImmersiveFullscreen(newVal); + } + }) + + const handleNormalFullscreen = (isOpen) => { + if (isOpen) { + launchNormalFullscreen(slotElement.value, props); + addFullScreenStyle(); + } else { + exitNormalFullscreen(slotElement.value); + removeFullScreenStyle(); + } + } + + const handleImmersiveFullscreen = (isOpen) => { + if (isOpen) { + launchImmersiveFullScreen(slotElement.value); + } else { + if (!exitByKeydown) { + exitImmersiveFullScreen(document); + } + } + } + + const handleFullscreenChange = () => { + if (!document.fullscreenElement) { + ctx.emit('update:modelValue'); + + exitByKeydown = true; + } else { + exitByKeydown = false; + } + } + + onMounted(() => { + document.addEventListener('fullscreenchange', handleFullscreenChange); + }); + + onUnmounted(() => { + document.removeEventListener('fullscreenchange', handleFullscreenChange); + }); +} diff --git a/packages/devui-vue/devui/fullscreen/src/composables/use-keydown.ts b/packages/devui-vue/devui/fullscreen/src/composables/use-keydown.ts new file mode 100644 index 0000000000..c6a5ffc850 --- /dev/null +++ b/packages/devui-vue/devui/fullscreen/src/composables/use-keydown.ts @@ -0,0 +1,21 @@ +import { onMounted, onUnmounted, toRefs } from 'vue'; + +const ESC_KEY_CODE = 27; + +export default function useKeydown(props, ctx) { + const { modelValue } = toRefs(props); + + const handleKeydown = (event) => { + if (event.keyCode === ESC_KEY_CODE && modelValue) { + ctx.emit('update:modelValue', false); + } + } + + onMounted(() => { + document.addEventListener('keydown', handleKeydown); + }); + + onUnmounted(() => { + document.removeEventListener('keydown', handleKeydown); + }); +} diff --git a/packages/devui-vue/devui/fullscreen/src/fullscreen-types.ts b/packages/devui-vue/devui/fullscreen/src/fullscreen-types.ts index 5428abb388..d60e50b227 100644 --- a/packages/devui-vue/devui/fullscreen/src/fullscreen-types.ts +++ b/packages/devui-vue/devui/fullscreen/src/fullscreen-types.ts @@ -1,14 +1,14 @@ import type { PropType, ExtractPropTypes } from 'vue' -type ModeType = PropType<'immersive' | 'normal'> +type ModeType = PropType<'normal' | 'immersive'> export const fullscreenProps = { - fullscreenLaunch: { - type: Function, - default: undefined + modelValue: { + type: Boolean, + default: false, }, mode: { type: String as ModeType, - default: 'immersive' + default: 'normal' }, zIndex: { type: Number, diff --git a/packages/devui-vue/devui/fullscreen/src/fullscreen.scss b/packages/devui-vue/devui/fullscreen/src/fullscreen.scss index 38689d367e..717bdf151d 100644 --- a/packages/devui-vue/devui/fullscreen/src/fullscreen.scss +++ b/packages/devui-vue/devui/fullscreen/src/fullscreen.scss @@ -1,6 +1,6 @@ @import '../../style/theme/color'; -.fullscreen { +.devui-fullscreen { position: fixed; top: 0; left: 0; @@ -11,10 +11,10 @@ background-color: $devui-base-bg; } -.devui-fullscreen { +.devui-fullscreen-html { overflow: hidden; } :not(:root):fullscreen::backdrop { - background: #ffffff; + background: $devui-base-bg; } diff --git a/packages/devui-vue/devui/fullscreen/src/fullscreen.tsx b/packages/devui-vue/devui/fullscreen/src/fullscreen.tsx index b10f75c3bc..3523b2bca5 100644 --- a/packages/devui-vue/devui/fullscreen/src/fullscreen.tsx +++ b/packages/devui-vue/devui/fullscreen/src/fullscreen.tsx @@ -1,160 +1,22 @@ -import './fullscreen.scss' - -import { - defineComponent, - useSlots, - renderSlot, - onMounted, - onBeforeUnmount, - ref -} from 'vue' -import { fullscreenProps, FullscreenProps } from './fullscreen-types' +import { defineComponent, useSlots, renderSlot, ref } from 'vue'; +import { fullscreenProps, FullscreenProps } from './fullscreen-types'; +import useKeydown from './composables/use-keydown'; +import useFullscreen from './composables/use-fullscreen'; +import './fullscreen.scss'; export default defineComponent({ name: 'DFullscreen', props: fullscreenProps, - emits: ['fullscreenLaunch'], + emits: ['update:modelValue'], setup(props: FullscreenProps, ctx) { + const slotElement = ref(null); - let currentTarget = null - const isFullscreen = ref(false) - const slotElement = ref(null) - - const onFullScreenChange = () => { - if (currentTarget) { - const targetElement: HTMLElement = currentTarget - if (document.fullscreenElement) { // 进入全屏 - addFullScreenStyle() - launchNormalFullscreen(targetElement) - } else { // 退出全屏 - removeFullScreenStyle() - currentTarget = null - exitNormalFullscreen(targetElement) - } - // F11退出全屏时,需要将全屏状态传出去 - isFullscreen.value = !!(document.fullscreenElement) - ctx.emit('fullscreenLaunch', isFullscreen.value) - } - } - - // 页面全屏 - const launchNormalFullscreen = (targetElement: HTMLElement) => { - targetElement.classList.add('fullscreen') - if (props.zIndex) { - targetElement.setAttribute('style', `z-index: ${props.zIndex}`) - } - } - - // 退出正常全屏 - const exitNormalFullscreen = (targetElement: HTMLElement) => { - targetElement.classList.remove('fullscreen') - targetElement.style.zIndex = null - } - - // 事件监听 - const handleFullscreen = async () => { - // const targetElement = document.querySelector('[fullscreen-target]') - const targetElement = slotElement.value.querySelector('[fullscreen-target]') - let isFull = false - // 判断模式 - if (props.mode === 'normal') { // 浏览器全屏 - const fullscreen = targetElement.classList.contains('fullscreen') - if (!fullscreen) { // 进入全屏 - addFullScreenStyle() - launchNormalFullscreen(targetElement) - isFull = true - } else { // 退出全屏 - removeFullScreenStyle() - exitNormalFullscreen(targetElement) - isFull = false - } - } else { // 沉浸式全屏 - currentTarget = targetElement - if (document.fullscreenElement || document.msFullscreenElement || document.webkitFullscreenElement) { - isFull = await exitImmersiveFullScreen(document) - } else { - isFull = await launchImmersiveFullScreen(currentTarget) - } - } - isFullscreen.value = isFull - ctx.emit('fullscreenLaunch', isFullscreen.value) - } - - const addFullScreenStyle = (): void => { - document.getElementsByTagName('html')[0].classList.add('devui-fullscreen') - } - - const removeFullScreenStyle = (): void => { - document.getElementsByTagName('html')[0].classList.remove('devui-fullscreen') - } - - const exitImmersiveFullScreen = async (doc: any) => { - let fullscreenExit = null - if (doc.exitFullscreen) { - fullscreenExit = doc.exitFullscreen() - } else if (doc.mozCancelFullScreen) { - fullscreenExit = doc.mozCancelFullScreen() - } else if (doc.webkitCancelFullScreen) { - fullscreenExit = Promise.resolve(doc.webkitCancelFullScreen()); - } else if (doc.msExitFullscreen) { - fullscreenExit = Promise.resolve(doc.msExitFullscreen()); - } - return await fullscreenExit.then(() => !!document.fullscreenElement) - } - - const launchImmersiveFullScreen = async (docElement: any) => { - let fullscreenLaunch = null - if (docElement.requestFullscreen) { - fullscreenLaunch = docElement.requestFullscreen() - } else if (docElement.mozRequestFullScreen) { - fullscreenLaunch = docElement.mozRequestFullScreen() - } else if (docElement.webkitRequestFullScreen) { - fullscreenLaunch = Promise.resolve(docElement.webkitRequestFullScreen()) - } else if (docElement.msRequestFullscreen) { - fullscreenLaunch = Promise.resolve(docElement.msRequestFullscreen()) - } - return await fullscreenLaunch.then(() => !!document.fullscreenElement) - } - - const handleKeyDown = (event) => { - if (event.keyCode === 27) { // 按ESC键退出全屏 - if (isFullscreen.value) { - const targetElement = slotElement.value.querySelector('[fullscreen-target]') - if (props.mode === 'normal') { - removeFullScreenStyle() - exitNormalFullscreen(targetElement) - } else { - if (document.fullscreenElement) { exitImmersiveFullScreen(document) } - } - isFullscreen.value = false - ctx.emit('fullscreenLaunch', isFullscreen.value) - } - } - } + useFullscreen(props, slotElement, ctx); + useKeydown(props, ctx); - onMounted(() => { - const btnLaunch = slotElement.value.querySelector('[fullscreen-launch]') - if (btnLaunch) { btnLaunch.addEventListener('click', handleFullscreen) } - document.addEventListener('fullscreenchange', onFullScreenChange) - document.addEventListener('MSFullscreenChange', onFullScreenChange) - document.addEventListener('webkitfullscreenchange', onFullScreenChange) - document.addEventListener('keydown', handleKeyDown) - }) - onBeforeUnmount(()=>{ - const btnLaunch = slotElement.value.querySelector('[fullscreen-launch]') - if (btnLaunch) { btnLaunch.removeEventListener('click', handleFullscreen) } - document.removeEventListener('fullscreenchange', onFullScreenChange) - document.removeEventListener('MSFullscreenChange', onFullScreenChange) - document.removeEventListener('webkitfullscreenchange', onFullScreenChange) - document.removeEventListener('keydown', handleKeyDown) - // removeFullScreenStyle(); - }) return () => { - const defaultSlot = renderSlot(useSlots(), 'default') - // if (defaultSlot.children.length === 0) throw new Error('未发现全屏元素') - return ( -