!29 fullscreen 全屏组件

Merge pull request !29 from Sight/develop
This commit is contained in:
就眠儀式 2022-02-17 16:10:56 +00:00 committed by Gitee
commit 64fb5e9e59
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
7 changed files with 491 additions and 2 deletions

View File

@ -0,0 +1,228 @@
::: anchor
:::
::: title 基本介绍
:::
::: describe 将某一区域的内容全屏展示。
:::
::: title 浏览器全屏
:::
::: demo 使用 `lay-fullscreen` 标签包裹触发全屏事件的按钮
<template>
<lay-fullscreen v-slot="{ enter, exit, toggle, isFullscreen }" @fullscreenchange=fullscreen>
<lay-button type="normal" @click="enter()">进入全屏</lay-button>
<lay-button type="normal" @click="exit()">退出</lay-button>
<lay-button type="warm" @click="toggle()">切换: {{isFullscreen ? "退出" : "进入全屏"}}</lay-button>
</lay-fullscreen>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const fullscreen = function(isFullscreen){
console.log(isFullscreen)
}
return {
fullscreen
}
}
}
</script>
:::
::: title 局部区域全屏
:::
::: demo 使用 `target` 属性指定需要局部全屏的元素, 组件必须用 `div` 包裹, 因为不是所有元素都能向浏览器请求全屏
<template>
<lay-fullscreen :target="elRef" v-slot="{ enter, exit, toggle, isFullscreen }" @fullscreenchange=fullscreen2>
<div ref="elRef" class="wrapper">
<lay-button type="normal" @click="enter()">进入全屏</lay-button>
<lay-button type="normal" @click="exit()">退出</lay-button>
<lay-button type="warm" @click="toggle()"> 切换: {{isFullscreen ? "退出" : "进入全屏"}} </lay-button>
</div>
</lay-fullscreen>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const elRef = ref(null);
const fullscreen2 = function(isFullscreen){
console.log(isFullscreen)
}
return {
elRef,
fullscreen2
}
}
}
</script>
<style>
.wrapper{
padding:10px;
border:1px solid #dddddd;
background-color:#F6F6F6;
display:inline-block
}
</style>
:::
::: title 浏览器窗口内全屏
:::
::: demo 使用 `target` 属性指定需要局部全屏的元素, 并设置`immersive`属性为 `false`,可使 `target` 在浏览器窗口内全屏
<template>
<lay-fullscreen
:target="fullscreenTargetRef"
:immersive="false"
zIndex="10000"
v-slot="{ enter, exit, toggle, isFullscreen }"
@fullscreenchange=fullscreen3>
<div ref="fullscreenTargetRef" class="wrapper">
<lay-button type="normal" @click="enter()">进入全屏</lay-button>
<lay-button type="normal" @click="exit()">退出</lay-button>
<lay-button type="warm" @click="toggle()"> 切换: {{isFullscreen ? "退出" : "进入全屏"}} </lay-button>
</div>
</lay-fullscreen>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const fullscreenTargetRef = ref(null)
const fullscreen3 = function(isFullscreen){
console.log(isFullscreen)
}
return {
fullscreenTargetRef,
fullscreen3
}
}
}
</script>
<style>
.wrapper{
padding:10px;
border:1px solid #dddddd;
background-color:#F6F6F6;
display:inline-block
}
</style>
:::
::: title 铺满特定容器
:::
::: demo 使用 `target` 属性指定需要局部全屏的元素, 并设置`immersive = false`, `position="absolute"`,可使 `target` 在特定容器内全屏
<template>
<div class="container" style="position:relative;height:300px;width:500px;background-color: #cccccc;">
<lay-fullscreen
:target="fullscreenTargetRef2"
:immersive="false"
zIndex="12000"
position="absolute"
v-slot="{ enter, exit, toggle, isFullscreen }"
@fullscreenchange=fullscreen4>
<div ref="fullscreenTargetRef2" class="wrapper">
<lay-button type="normal" @click="enter()">进入全屏</lay-button>
<lay-button type="normal" @click="exit()">退出</lay-button>
<lay-button type="warm" @click="toggle()"> 切换: {{isFullscreen ? "退出" : "进入全屏"}} </lay-button>
</div>
</lay-fullscreen>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const fullscreenTargetRef2 = ref(null);
const fullscreen4 = function(isFullscreen){
console.log(isFullscreen)
}
return {
fullscreenTargetRef2,
fullscreen4
}
}
}
</script>
<style>
.container{
position:relative;
height:300px;
width:500px;
background-color: red;
border:1px solid #dddddd;
};
.wrapper{
padding:10px;
border:1px solid #dddddd;
background-color:#F6F6F6;
display:inline-block;
}
</style>
:::
:::title fullscreen 属性
:::
:::table
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| --------- | ----------------------------------------- | ------------- | ------ | ------------------- |
| target | 可选,要全屏显示的元素 | `HTMLElement` | `html` | - |
| immersive | 可选,全屏模式,`false`时浏览器窗口内全屏 | `boolean` | true | `true` `false` |
| position | 可选,浏览器窗口内全屏定位模式 | `string` | - | `absolute` `fixed` |
| zIndex | 可选,全屏层级 | `string` | - | - |
:::
:::title fullscreen 事件
:::
:::table
| 事件 | 描述 | 参数 |
|------ |----------|-----------|
| fullscreenchange | 全屏更改事件回调 | isFullscreen 是否全屏 |
:::
:::title fullscreen 插槽
:::
:::table
| 插槽 | 描述 | 参数 |
|------ |----------|-----------|
| default | 默认插槽 | - |
:::
:::title fullscreen 插槽属性
:::
:::table
| 属性 | 描述 | 类型 | 参数 |
| --------- | --------------| ------------- | ------ |
| enter | 进入全屏 | Function | 可选,HTMLElement |
| exit | 退出全屏 | Function | 可选,HTMLElement /| Document |
| toggle | 进入/退出全屏 | Function | - |
| isFullscreen | 是否全屏 | boolean | - |
:::

View File

@ -335,11 +335,17 @@ const zhCN = [
path: "/zh-CN/components/exception", path: "/zh-CN/components/exception",
component: () => import("../../docs/zh-CN/components/exception.md"), component: () => import("../../docs/zh-CN/components/exception.md"),
meta: { title: "异常" }, meta: { title: "异常" },
}, { },
{
path: "/zh-CN/components/result", path: "/zh-CN/components/result",
component: () => import("../../docs/zh-CN/components/result.md"), component: () => import("../../docs/zh-CN/components/result.md"),
meta: { title: "结果" }, meta: { title: "结果" },
}, },
{
path: "/zh-CN/components/fullscreen",
component: () => import("../../docs/zh-CN/components/fullscreen.md"),
meta: { title: "结果" },
},
], ],
} }
], ],

View File

@ -27,6 +27,12 @@ const menus = [
subTitle: "animation", subTitle: "animation",
path: "/zh-CN/components/animation", path: "/zh-CN/components/animation",
}, },
{
id: 101,
title: "全屏",
subTitle: "fullscreen",
path: "/zh-CN/components/fullscreen",
},
], ],
}, },
{ {

View File

@ -0,0 +1,9 @@
.layui-fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
overflow: auto;
}

View File

@ -0,0 +1,8 @@
import type { App } from "vue";
import Component from "./index.vue";
Component.install = (app: App) => {
app.component(Component.name, Component);
};
export default Component;

View File

@ -0,0 +1,229 @@
<script lang="ts">
export default {
name: "LayFullscreen",
};
</script>
<script lang="ts" setup>
import {
ref,
withDefaults,
computed,
onMounted,
onBeforeUnmount,
Ref,
} from "vue";
import "./index.less";
export interface LayFullscreenProps {
target?: HTMLElement;
immersive?: boolean;
position?: string;
zIndex?: string;
// top?: string;
// left?: string;
// width?: string;
// height?: string;
}
const props = withDefaults(defineProps<LayFullscreenProps>(), {
immersive: true,
});
const emit = defineEmits(["fullscreenchange"]);
type MethodMap = [
"requestFullscreen",
"exitFullscreen",
"fullscreenElement",
"fullscreenEnabled",
"fullscreenchange",
"fullscreenerror"
];
const methodMap: MethodMap[] = [
[
"requestFullscreen",
"exitFullscreen",
"fullscreenElement",
"fullscreenEnabled",
"fullscreenchange",
"fullscreenerror",
],
// New WebKit
[
"webkitRequestFullscreen",
"webkitExitFullscreen",
"webkitFullscreenElement",
"webkitFullscreenEnabled",
"webkitfullscreenchange",
"webkitfullscreenerror",
],
// Old WebKit
[
"webkitRequestFullScreen",
"webkitCancelFullScreen",
"webkitCurrentFullScreenElement",
"webkitCancelFullScreen",
"webkitfullscreenchange",
"webkitfullscreenerror",
],
[
"mozRequestFullScreen",
"mozCancelFullScreen",
"mozFullScreenElement",
"mozFullScreenEnabled",
"mozfullscreenchange",
"mozfullscreenerror",
],
[
"msRequestFullscreen",
"msExitFullscreen",
"msFullscreenElement",
"msFullscreenEnabled",
"MSFullscreenChange",
"MSFullscreenError",
],
];
//
const defaultElement: HTMLElement = document.documentElement;
//
let targetEl: Ref<HTMLElement | undefined> = ref<HTMLElement | undefined>(
props.target || defaultElement
);
//
const isFullscreen = ref(false);
//
let isSupported: boolean = false;
// API,
const unprefixedMethods: MethodMap = methodMap[0];
const fullscreenAPI: Record<string, string> = {};
for (const methodList of methodMap) {
if (methodList[1] in document) {
for (const [index, method] of methodList.entries()) {
fullscreenAPI[unprefixedMethods[index]] = method;
}
isSupported = true;
break;
}
}
/**
* 进入全屏
* @param targetEl 请求全屏显示的元素,不是所有元素都支持,建议用 div
*/
async function enter(targetEl: HTMLElement | undefined) {
if (!isSupported) return;
if (!targetEl) targetEl = activeEl.value || defaultElement;
let fullscreenEnter = null;
if (props.immersive) {
// @ts-ignore
fullscreenEnter = Promise.resolve(
targetEl[fullscreenAPI.requestFullscreen]()
);
} else {
styleLayFullscreen(targetEl, false);
fullscreenEnter = Promise.resolve(
targetEl?.classList.add("layui-fullscreen")
);
}
return await fullscreenEnter?.then(() => {
isFullscreen.value = true;
emit("fullscreenchange", isFullscreen.value);
return !!document.fullscreenElement;
});
}
/**
* 退出全屏
* @param targetEl 退出全屏元素
*/
async function exit(targetEl: HTMLElement | Document | undefined) {
if (!isSupported) return;
if (!targetEl) targetEl = activeEl.value || document;
let fullscreenExit = null;
if (props.immersive) {
// @ts-ignore
fullscreenExit = Promise.resolve(document[fullscreenAPI.exitFullscreen]());
} else {
if (targetEl instanceof Document) return;
styleLayFullscreen(targetEl, true);
fullscreenExit = Promise.resolve(
targetEl?.classList.remove("layui-fullscreen")
);
}
return await fullscreenExit?.then(() => {
isFullscreen.value = false;
emit("fullscreenchange", isFullscreen.value);
return !!document.fullscreenElement;
});
}
/**
* 进入或退出全屏
*/
async function toggle() {
if (isFullscreen.value) {
await exit(activeEl.value);
} else {
await enter(activeEl.value);
}
}
/**
*
* @param el HTML 元素
* @param isRemove 是否移除样式
*/
const styleLayFullscreen = function (
el: HTMLElement,
isRemove: boolean = false
) {
el.style.position = isRemove ? "" : props.position || "";
el.style.zIndex = isRemove ? "" : props.zIndex || "";
//
// el.style.top = isRemove ? "" : (props.top || "");
// el.style.left = isRemove ? "" : (props.left || "");
// el.style.width = isRemove ? "" : (props.width || "");
// el.style.height = isRemove ? "" : (props.height || "");
};
const activeEl = computed(() => (targetEl.value = props.target));
/**
* 处理 fullscreenchange 和浏览器窗口内全屏 Escape 按键事件
* @param event Escape 键盘事件
*/
const onFullscreenchange = function (event: KeyboardEvent) {
if (isFullscreen.value && !document.fullscreenElement) {
if (props.immersive) {
isFullscreen.value = false;
emit("fullscreenchange", isFullscreen.value);
} else if (event.key === "Escape") {
exit(activeEl.value);
}
}
};
onMounted(() => {
//@ts-ignore
document.addEventListener(fullscreenAPI.fullscreenchange, onFullscreenchange);
document.addEventListener("keydown", onFullscreenchange);
});
onBeforeUnmount(() => {
//@ts-ignore
document.addEventListener(fullscreenAPI.fullscreenchange, onFullscreenchange);
document.addEventListener("keydown", onFullscreenchange);
});
</script>
<template>
<slot
name="default"
:isFullscreen="isFullscreen"
:enter="enter"
:exit="exit"
:toggle="toggle"
></slot>
</template>

View File

@ -76,6 +76,7 @@ import LaySplitPanel from "./component/splitPanel/index";
import LaySplitPanelItem from "./component/splitPanelItem/index"; import LaySplitPanelItem from "./component/splitPanelItem/index";
import LayException from "./component/exception/index"; import LayException from "./component/exception/index";
import LayResult from "./component/result/index"; import LayResult from "./component/result/index";
import LayFullscreen from "./component/fullscreen/index";
import LayConfigProvider from "./provider"; import LayConfigProvider from "./provider";
const components: Record<string, Component> = { const components: Record<string, Component> = {
@ -149,6 +150,7 @@ const components: Record<string, Component> = {
LaySubMenu, LaySubMenu,
LayException, LayException,
LayResult, LayResult,
LayFullscreen,
LayConfigProvider, LayConfigProvider,
}; };
@ -231,6 +233,7 @@ export {
LaySubMenu, LaySubMenu,
LayException, LayException,
LayResult, LayResult,
LayFullscreen,
LayConfigProvider, LayConfigProvider,
}; };