✨(component): layer弹层扩展photos方法,用于图片预览
This commit is contained in:
parent
1438147ec3
commit
892722860d
@ -350,6 +350,42 @@ const openRight = function() {
|
||||
</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">
|
||||
<legend>通讯</legend>
|
||||
</fieldset>
|
||||
@ -496,7 +532,7 @@ const changeVisible = () => {
|
||||
| shadeClose | 遮盖层关闭 | boolean | `true` | `true` `false` |
|
||||
| shadeOpacity | 遮盖层透明度 | string | `0.1` | `0.1` - `1` |
|
||||
| isHtmlFragment | 解析 html 字符 | boolean | `false` | `true` `false` |
|
||||
|
||||
| imgList | 图片数据数组 | array[{src:图片链接,alt:图片名字可选'}] | - | - |
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>动画</legend>
|
||||
</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 CloseBtn from "./CloseBtn.vue";
|
||||
import Resize from "./Resize.vue";
|
||||
import Photos from "./Photos.vue";
|
||||
import {
|
||||
Ref,
|
||||
ref,
|
||||
@ -34,6 +35,7 @@ import {
|
||||
updateMinArrays,
|
||||
getDrawerAnimationClass,
|
||||
calculateDrawerArea,
|
||||
calculatePhotosArea,
|
||||
} from "../utils";
|
||||
import useMove from "../composable/useMove";
|
||||
import useResize from "../composable/useResize";
|
||||
@ -53,7 +55,7 @@ export interface LayModalProps {
|
||||
btn?: Record<string, Function>[] | false;
|
||||
move?: 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;
|
||||
isHtmlFragment?: boolean;
|
||||
shade?: boolean | string;
|
||||
@ -73,6 +75,8 @@ export interface LayModalProps {
|
||||
isFunction?: boolean;
|
||||
isMessage?: boolean;
|
||||
appContext?: any;
|
||||
startIndex?: number;
|
||||
imgList?: { src: string; alt: string }[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<LayModalProps>(), {
|
||||
@ -101,6 +105,8 @@ const props = withDefaults(defineProps<LayModalProps>(), {
|
||||
yesText: "确定",
|
||||
isFunction: false,
|
||||
isMessage: false,
|
||||
startIndex: 0,
|
||||
imgList: () => [],
|
||||
});
|
||||
|
||||
const emit = defineEmits(["close", "update:modelValue"]);
|
||||
@ -141,11 +147,18 @@ const _l: Ref<string> = ref(offset.value[1]);
|
||||
* <p>
|
||||
*/
|
||||
const firstOpenDelayCalculation = function () {
|
||||
nextTick(() => {
|
||||
nextTick(async () => {
|
||||
area.value = getArea(layero.value);
|
||||
if (props.type === "drawer") {
|
||||
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);
|
||||
w.value = area.value[0];
|
||||
h.value = area.value[1];
|
||||
@ -334,6 +347,7 @@ const boxClasses = computed(() => {
|
||||
type === 1 ? "layui-layer-page" : "",
|
||||
type === 2 ? "layui-layer-iframe" : "",
|
||||
type === 3 ? "layui-layer-loading" : "",
|
||||
type === 4 ? "layui-layer-photos" : "",
|
||||
props.isMessage ? "layui-layer-msg" : "",
|
||||
props.isMessage && !props.icon ? "layui-layer-hui" : "",
|
||||
props.skin,
|
||||
@ -511,9 +525,28 @@ const showResize = computed(() => {
|
||||
* @param type 类型
|
||||
*/
|
||||
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 });
|
||||
</script>
|
||||
|
||||
@ -558,9 +591,15 @@ defineExpose({ reset, open, close });
|
||||
</template>
|
||||
</template>
|
||||
<Iframe v-if="type === 2" :src="props.content"></Iframe>
|
||||
<Photos
|
||||
v-if="type === 4"
|
||||
:imgList="props.imgList"
|
||||
:startIndex="props.startIndex"
|
||||
@resetCalculationPohtosArea="resetCalculationPohtosArea"
|
||||
></Photos>
|
||||
</div>
|
||||
<!-- 工具栏 -->
|
||||
<span class="layui-layer-setwin" v-if="type != 3">
|
||||
<span class="layui-layer-setwin" v-if="type != 3 && type != 4">
|
||||
<a
|
||||
v-if="maxmin && !max"
|
||||
class="layui-layer-min"
|
||||
|
@ -121,6 +121,23 @@ const layer = {
|
||||
};
|
||||
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) => {
|
||||
// 销毁定时
|
||||
|
@ -1092,7 +1092,8 @@ html #layuicss-layer {
|
||||
|
||||
.layui-layer-photos {
|
||||
background: 0 0;
|
||||
box-shadow: none
|
||||
box-shadow: none;
|
||||
border:none
|
||||
}
|
||||
|
||||
.layui-layer-photos .layui-layer-content {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { layer } from "../index"
|
||||
|
||||
// 随机数
|
||||
export function nextId() {
|
||||
var s: any = [];
|
||||
@ -121,6 +123,8 @@ export function calculateType(modalType: number | string) {
|
||||
return 2;
|
||||
} else if (modalType === "loading" || modalType === 3 || modalType === "3") {
|
||||
return 3;
|
||||
} else if (modalType === "photos") {
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -233,3 +237,61 @@ export function getDrawerAnimationClass(offset: any, isClose: boolean = false) {
|
||||
}
|
||||
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 { name } from "./package.json";
|
||||
import babel from "@rollup/plugin-babel";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import path from "path";
|
||||
|
||||
@ -30,14 +29,6 @@ export default defineConfig({
|
||||
},
|
||||
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"],
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user