✨(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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user