✨(component): layer弹层扩展photos方法,用于图片预览
This commit is contained in:
parent
1438147ec3
commit
892722860d
@ -350,6 +350,42 @@ const openRight = function() {
|
|||||||
</script>
|
</script>
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>图片层</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
::: demo 通过 layer.photos(options) 创建图片预览弹层, 参数`options`主要传入预览的图片链接。
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button @click="signleImg">快速预览一张图片</button>
|
||||||
|
<button @click="signleImg2">单张图片带文字描述</button>
|
||||||
|
<button @click="groupImg">图片组</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { layer } from "../../../layer/src/index"
|
||||||
|
|
||||||
|
const signleImg = function() {
|
||||||
|
layer.photos("/src/assets/logo.jpg")
|
||||||
|
}
|
||||||
|
const signleImg2 = function() {
|
||||||
|
layer.photos({
|
||||||
|
imgList:[ {src:'/src/assets/logo.jpg',alt:'layer for vue'}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const groupImg = function() {
|
||||||
|
layer.photos({
|
||||||
|
imgList:[
|
||||||
|
{ src:'http://www.pearadmin.com/assets/images/un8.svg', alt:'图片1'},
|
||||||
|
{ src:'http://www.pearadmin.com/assets/images/un32.svg', alt:'图片2'}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
<fieldset class="layui-elem-field layui-field-title">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>通讯</legend>
|
<legend>通讯</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@ -496,7 +532,7 @@ const changeVisible = () => {
|
|||||||
| shadeClose | 遮盖层关闭 | boolean | `true` | `true` `false` |
|
| shadeClose | 遮盖层关闭 | boolean | `true` | `true` `false` |
|
||||||
| shadeOpacity | 遮盖层透明度 | string | `0.1` | `0.1` - `1` |
|
| shadeOpacity | 遮盖层透明度 | string | `0.1` | `0.1` - `1` |
|
||||||
| isHtmlFragment | 解析 html 字符 | boolean | `false` | `true` `false` |
|
| isHtmlFragment | 解析 html 字符 | boolean | `false` | `true` `false` |
|
||||||
|
| imgList | 图片数据数组 | array[{src:图片链接,alt:图片名字可选'}] | - | - |
|
||||||
<fieldset class="layui-elem-field layui-field-title">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>动画</legend>
|
<legend>动画</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
51
package/layer/src/component/Photos.vue
Normal file
51
package/layer/src/component/Photos.vue
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<div class="layui-layer-phimg">
|
||||||
|
<img :src="imgList[index].src" />
|
||||||
|
<div class="layui-layer-imgsee" v-if="imgList.length > 0">
|
||||||
|
<span class="layui-layer-imguide" v-if="imgList.length > 1">
|
||||||
|
<a href="javascript:;" class="layui-layer-iconext layui-layer-imgprev" @click="changeIndex(-1)"></a>
|
||||||
|
<a href="javascript:;" class="layui-layer-iconext layui-layer-imgnext" @click="changeIndex(1)"></a>
|
||||||
|
</span>
|
||||||
|
<div class="layui-layer-imgbar" style="display: block" v-if="imgList.length > 1 || imgList[index].alt">
|
||||||
|
<span class="layui-layer-imgtit">
|
||||||
|
<span v-if="imgList[index].alt">{{ imgList[index].alt }}</span>
|
||||||
|
<em v-if="imgList.length > 1">{{ index + 1 }} / {{ imgList.length }}</em>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "Photos",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { watch, ref } from "vue";
|
||||||
|
|
||||||
|
export interface LayPhotoProps {
|
||||||
|
imgList: { src: string, alt: string }[];
|
||||||
|
startIndex: number;
|
||||||
|
}
|
||||||
|
const emit = defineEmits(["resetCalculationPohtosArea"]);
|
||||||
|
const props = withDefaults(defineProps<LayPhotoProps>(), {
|
||||||
|
startIndex: 0,
|
||||||
|
});
|
||||||
|
const index = ref(props.startIndex);
|
||||||
|
watch(index, () => {
|
||||||
|
//当图片索引改变的时候 重新计算弹层的大小
|
||||||
|
emit('resetCalculationPohtosArea', index.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeIndex = (step: number) => {
|
||||||
|
let nowIndex = index.value
|
||||||
|
let next = nowIndex + step
|
||||||
|
if (next < 0) {
|
||||||
|
next = props.imgList.length - 1
|
||||||
|
}
|
||||||
|
if (next >= props.imgList.length) {
|
||||||
|
next = 0
|
||||||
|
}
|
||||||
|
index.value = next
|
||||||
|
}
|
||||||
|
</script>
|
@ -10,6 +10,7 @@ import Iframe from "./Iframe.vue";
|
|||||||
import Title from "./Title.vue";
|
import Title from "./Title.vue";
|
||||||
import CloseBtn from "./CloseBtn.vue";
|
import CloseBtn from "./CloseBtn.vue";
|
||||||
import Resize from "./Resize.vue";
|
import Resize from "./Resize.vue";
|
||||||
|
import Photos from "./Photos.vue";
|
||||||
import {
|
import {
|
||||||
Ref,
|
Ref,
|
||||||
ref,
|
ref,
|
||||||
@ -34,6 +35,7 @@ import {
|
|||||||
updateMinArrays,
|
updateMinArrays,
|
||||||
getDrawerAnimationClass,
|
getDrawerAnimationClass,
|
||||||
calculateDrawerArea,
|
calculateDrawerArea,
|
||||||
|
calculatePhotosArea,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import useMove from "../composable/useMove";
|
import useMove from "../composable/useMove";
|
||||||
import useResize from "../composable/useResize";
|
import useResize from "../composable/useResize";
|
||||||
@ -53,7 +55,7 @@ export interface LayModalProps {
|
|||||||
btn?: Record<string, Function>[] | false;
|
btn?: Record<string, Function>[] | false;
|
||||||
move?: boolean | string;
|
move?: boolean | string;
|
||||||
resize?: boolean | string;
|
resize?: boolean | string;
|
||||||
type?: 0 | 1 | 2 | 3 | "dialog" | "page" | "iframe" | "loading" | "drawer";
|
type?: 0 | 1 | 2 | 3 | "dialog" | "page" | "iframe" | "loading" | "drawer" | "photos";
|
||||||
content?: string | Function | object | VNodeTypes;
|
content?: string | Function | object | VNodeTypes;
|
||||||
isHtmlFragment?: boolean;
|
isHtmlFragment?: boolean;
|
||||||
shade?: boolean | string;
|
shade?: boolean | string;
|
||||||
@ -73,6 +75,8 @@ export interface LayModalProps {
|
|||||||
isFunction?: boolean;
|
isFunction?: boolean;
|
||||||
isMessage?: boolean;
|
isMessage?: boolean;
|
||||||
appContext?: any;
|
appContext?: any;
|
||||||
|
startIndex?: number;
|
||||||
|
imgList?: { src: string; alt: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<LayModalProps>(), {
|
const props = withDefaults(defineProps<LayModalProps>(), {
|
||||||
@ -101,6 +105,8 @@ const props = withDefaults(defineProps<LayModalProps>(), {
|
|||||||
yesText: "确定",
|
yesText: "确定",
|
||||||
isFunction: false,
|
isFunction: false,
|
||||||
isMessage: false,
|
isMessage: false,
|
||||||
|
startIndex: 0,
|
||||||
|
imgList: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["close", "update:modelValue"]);
|
const emit = defineEmits(["close", "update:modelValue"]);
|
||||||
@ -141,11 +147,18 @@ const _l: Ref<string> = ref(offset.value[1]);
|
|||||||
* <p>
|
* <p>
|
||||||
*/
|
*/
|
||||||
const firstOpenDelayCalculation = function () {
|
const firstOpenDelayCalculation = function () {
|
||||||
nextTick(() => {
|
nextTick(async () => {
|
||||||
area.value = getArea(layero.value);
|
area.value = getArea(layero.value);
|
||||||
if (props.type === "drawer") {
|
if (props.type === "drawer") {
|
||||||
area.value = calculateDrawerArea(props.offset, props.area);
|
area.value = calculateDrawerArea(props.offset, props.area);
|
||||||
}
|
}
|
||||||
|
if (props.type === "photos") {
|
||||||
|
// @ts-ignore
|
||||||
|
area.value = await calculatePhotosArea(
|
||||||
|
props.imgList[props.startIndex].src,
|
||||||
|
props
|
||||||
|
);
|
||||||
|
}
|
||||||
offset.value = calculateOffset(props.offset, area.value, props.type);
|
offset.value = calculateOffset(props.offset, area.value, props.type);
|
||||||
w.value = area.value[0];
|
w.value = area.value[0];
|
||||||
h.value = area.value[1];
|
h.value = area.value[1];
|
||||||
@ -334,6 +347,7 @@ const boxClasses = computed(() => {
|
|||||||
type === 1 ? "layui-layer-page" : "",
|
type === 1 ? "layui-layer-page" : "",
|
||||||
type === 2 ? "layui-layer-iframe" : "",
|
type === 2 ? "layui-layer-iframe" : "",
|
||||||
type === 3 ? "layui-layer-loading" : "",
|
type === 3 ? "layui-layer-loading" : "",
|
||||||
|
type === 4 ? "layui-layer-photos" : "",
|
||||||
props.isMessage ? "layui-layer-msg" : "",
|
props.isMessage ? "layui-layer-msg" : "",
|
||||||
props.isMessage && !props.icon ? "layui-layer-hui" : "",
|
props.isMessage && !props.icon ? "layui-layer-hui" : "",
|
||||||
props.skin,
|
props.skin,
|
||||||
@ -511,9 +525,28 @@ const showResize = computed(() => {
|
|||||||
* @param type 类型
|
* @param type 类型
|
||||||
*/
|
*/
|
||||||
const showTitle = computed(() => {
|
const showTitle = computed(() => {
|
||||||
return props.title && props.type != 3;
|
return props.title && props.type != 3 && props.type != "photos";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 图片弹层重新计算
|
||||||
|
*/
|
||||||
|
const resetCalculationPohtosArea = function (index: number) {
|
||||||
|
nextTick(async () => {
|
||||||
|
// @ts-ignore
|
||||||
|
area.value = await calculatePhotosArea(props.imgList[index].src, props);
|
||||||
|
offset.value = calculateOffset(props.offset, area.value, props.type);
|
||||||
|
w.value = area.value[0];
|
||||||
|
h.value = area.value[1];
|
||||||
|
t.value = offset.value[0];
|
||||||
|
l.value = offset.value[1];
|
||||||
|
_w.value = area.value[0];
|
||||||
|
_l.value = area.value[1];
|
||||||
|
_t.value = offset.value[0];
|
||||||
|
_l.value = offset.value[1];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({ reset, open, close });
|
defineExpose({ reset, open, close });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -558,9 +591,15 @@ defineExpose({ reset, open, close });
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<Iframe v-if="type === 2" :src="props.content"></Iframe>
|
<Iframe v-if="type === 2" :src="props.content"></Iframe>
|
||||||
|
<Photos
|
||||||
|
v-if="type === 4"
|
||||||
|
:imgList="props.imgList"
|
||||||
|
:startIndex="props.startIndex"
|
||||||
|
@resetCalculationPohtosArea="resetCalculationPohtosArea"
|
||||||
|
></Photos>
|
||||||
</div>
|
</div>
|
||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
<span class="layui-layer-setwin" v-if="type != 3">
|
<span class="layui-layer-setwin" v-if="type != 3 && type != 4">
|
||||||
<a
|
<a
|
||||||
v-if="maxmin && !max"
|
v-if="maxmin && !max"
|
||||||
class="layui-layer-min"
|
class="layui-layer-min"
|
||||||
|
@ -121,6 +121,23 @@ const layer = {
|
|||||||
};
|
};
|
||||||
return layer.create(option, defaultOption, callback);
|
return layer.create(option, defaultOption, callback);
|
||||||
},
|
},
|
||||||
|
//图片预览
|
||||||
|
photos: (option: any, callback?: Function) => {
|
||||||
|
if (typeof option === 'string') {
|
||||||
|
option = {
|
||||||
|
imgList: [{ src: option }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let defaultOption = {
|
||||||
|
type: 'photos',
|
||||||
|
anim: 2,
|
||||||
|
startIndex: 0,
|
||||||
|
isOutAnim: true,
|
||||||
|
shadeClose: true,
|
||||||
|
shadeOpacity: '0.7'
|
||||||
|
};
|
||||||
|
return layer.create(option, defaultOption, callback);
|
||||||
|
},
|
||||||
// 创建弹出层
|
// 创建弹出层
|
||||||
create: (option: any, defaultOption: any, callback?: Function) => {
|
create: (option: any, defaultOption: any, callback?: Function) => {
|
||||||
// 销毁定时
|
// 销毁定时
|
||||||
|
@ -1092,7 +1092,8 @@ html #layuicss-layer {
|
|||||||
|
|
||||||
.layui-layer-photos {
|
.layui-layer-photos {
|
||||||
background: 0 0;
|
background: 0 0;
|
||||||
box-shadow: none
|
box-shadow: none;
|
||||||
|
border:none
|
||||||
}
|
}
|
||||||
|
|
||||||
.layui-layer-photos .layui-layer-content {
|
.layui-layer-photos .layui-layer-content {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { layer } from "../index"
|
||||||
|
|
||||||
// 随机数
|
// 随机数
|
||||||
export function nextId() {
|
export function nextId() {
|
||||||
var s: any = [];
|
var s: any = [];
|
||||||
@ -121,6 +123,8 @@ export function calculateType(modalType: number | string) {
|
|||||||
return 2;
|
return 2;
|
||||||
} else if (modalType === "loading" || modalType === 3 || modalType === "3") {
|
} else if (modalType === "loading" || modalType === 3 || modalType === "3") {
|
||||||
return 3;
|
return 3;
|
||||||
|
} else if (modalType === "photos") {
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -233,3 +237,61 @@ export function getDrawerAnimationClass(offset: any, isClose: boolean = false) {
|
|||||||
}
|
}
|
||||||
return isClose ? `${prefix}-${suffix}-close` : `${prefix}-${suffix}`;
|
return isClose ? `${prefix}-${suffix}-close` : `${prefix}-${suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//图片预加载
|
||||||
|
export function loadImage(url: string, callback: Function, error: any) {
|
||||||
|
let img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
if (img.complete) {
|
||||||
|
return callback(img);
|
||||||
|
}
|
||||||
|
img.onload = function () {
|
||||||
|
img.onload = null;
|
||||||
|
callback(img);
|
||||||
|
};
|
||||||
|
img.onerror = function (e) {
|
||||||
|
img.onerror = null;
|
||||||
|
error(e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function calculatePhotosArea(url: string,options:object) {
|
||||||
|
let img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (img.complete) {
|
||||||
|
resolve(area(img))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const layerId=layer.load(2)
|
||||||
|
img.onload = () => {
|
||||||
|
layer.close(layerId)
|
||||||
|
resolve(area(img))
|
||||||
|
};
|
||||||
|
img.onerror = () => {
|
||||||
|
layer.close(layerId)
|
||||||
|
layer.msg('图片加载失败')
|
||||||
|
reject(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function area(img:{width:number,height:number}){
|
||||||
|
var imgarea = [img.width, img.height];
|
||||||
|
var winarea = [window.innerWidth - 100, window.innerHeight - 100];
|
||||||
|
//如果 实际图片的宽或者高比 屏幕大(那么进行缩放)
|
||||||
|
if ( imgarea[0] > winarea[0] || imgarea[1] > winarea[1]) {
|
||||||
|
let wh = [imgarea[0] / winarea[0], imgarea[1] / winarea[1]]; //取宽度缩放比例、高度缩放比例
|
||||||
|
if (wh[0] > wh[1]) {
|
||||||
|
//取缩放比例最大的进行缩放
|
||||||
|
imgarea[0] = imgarea[0] / wh[0];
|
||||||
|
imgarea[1] = imgarea[1] / wh[0];
|
||||||
|
} else if (wh[0] < wh[1]) {
|
||||||
|
imgarea[0] = imgarea[0] / wh[1];
|
||||||
|
imgarea[1] = imgarea[1] / wh[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [imgarea[0] + "px", imgarea[1] + "px"];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { name } from "./package.json";
|
import { name } from "./package.json";
|
||||||
import babel from "@rollup/plugin-babel";
|
|
||||||
import vue from "@vitejs/plugin-vue";
|
import vue from "@vitejs/plugin-vue";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
@ -30,14 +29,6 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
assetFileNames: "index.css",
|
assetFileNames: "index.css",
|
||||||
},
|
},
|
||||||
plugins: [
|
|
||||||
// @ts-ignore
|
|
||||||
babel({
|
|
||||||
exclude: "node_modules/**",
|
|
||||||
extensions: [".js", ".jsx", ".ts", ".tsx", ".vue"],
|
|
||||||
presets: ["@babel/preset-env", "@babel/preset-typescript"],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
external: ["vue"],
|
external: ["vue"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user